diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md index a3f9a38394f3..3e71b8399611 100644 --- a/.github/ISSUE_TEMPLATE/Question.md +++ b/.github/ISSUE_TEMPLATE/Question.md @@ -11,5 +11,5 @@ so we recommend using our other community resources instead of asking here 👍. If you have a support request or question please submit them to one of these resources: -* [Terraform community resources](https://www.terraform.io/docs/extend/community/index.html) +* [HashiCorp Community Forums](https://discuss.hashicorp.com/c/terraform-providers) * [HashiCorp support](https://support.hashicorp.com) (Terraform Enterprise customers) diff --git a/.gitignore b/.gitignore index 6c6ffa9ab9d0..247ea4cb6fdc 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ examples/**/test.tf examples/**/test.tfvars examples/**/terraform examples/**/terraform.zip + +#never upload the build to git +terraform-provider-azurerm diff --git a/.golangci-travisrest.yml b/.golangci-travisrest.yml new file mode 100644 index 000000000000..891ed2590ec5 --- /dev/null +++ b/.golangci-travisrest.yml @@ -0,0 +1,36 @@ +run: + deadline: 30m10s + modules-download-mode: vendor + +issues: + max-per-linter: 0 + max-same-issues: 0 + +linters: + disable-all: true + enable: + - deadcode + - errcheck + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - interfacer + - nakedret + - misspell + - structcheck + - typecheck + - unused + - unconvert + - varcheck + - vet + - vetshadow + - whitespace + +linters-settings: + errcheck: + ignore: github.com/hashicorp/terraform/helper/schema:ForceNew|Set,fmt:.*,io:Close + misspell: + ignore-words: + - hdinsight diff --git a/.golangci.yml b/.golangci.yml index c0107e1f206b..ef4d6808c63d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - deadline: 7m7s + deadline: 10m10s modules-download-mode: vendor issues: @@ -27,10 +27,11 @@ linters: - varcheck - vet - vetshadow + - whitespace linters-settings: errcheck: ignore: github.com/hashicorp/terraform/helper/schema:ForceNew|Set,fmt:.*,io:Close misspell: ignore-words: - - hdinsight \ No newline at end of file + - hdinsight diff --git a/.hashibot.hcl b/.hashibot.hcl new file mode 100644 index 000000000000..ff3069ff6b3a --- /dev/null +++ b/.hashibot.hcl @@ -0,0 +1,26 @@ +queued_behavior "release_commenter" "releases" { + repo_prefix = "terraform-provider-" + + message = <<-EOF + This has been released in [version ${var.release_version} of the provider](${var.changelog_link}). Please see the [Terraform documentation on provider versioning](https://www.terraform.io/docs/configuration/providers.html#provider-versions) or reach out if you need any assistance upgrading. As an example: + ```hcl + provider "${var.project_name}" { + version = "~> ${var.release_version}" + } + # ... other configuration ... + ``` + EOF +} + +poll "closed_issue_locker" "locker" { + schedule = "0 50 14 * * *" + closed_for = "720h" # 30 days + max_issues = 500 + sleep_between_issues = "5s" + + message = <<-EOF + I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + + If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks! + EOF +} diff --git a/.travis.yml b/.travis.yml index d687a587eb80..ae1401dfbe0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,34 +1,39 @@ -dist: trusty +dist: xenial sudo: required services: - docker language: go go: -- "1.12.x" +- "1.13.x" + +branches: + only: + - master env: - matrix: - - MODE=unit-tests - - MODE=linters - - MODE=website + global: + GOFLAGS=-mod=vendor install: -# This script is used by the Travis build to install a cookie for -# go.googlesource.com so rate limits are higher when using `go get` to fetch -# packages that live there. -# See: https://github.com/golang/go/issues/12933 -- bash scripts/gogetcookie.sh -- make tools + # This script is used by the Travis build to install a cookie for + # go.googlesource.com so rate limits are higher when using `go get` to fetch + # packages that live there. + # See: https://github.com/golang/go/issues/12933 + - bash scripts/gogetcookie.sh + - make tools -script: - - if [[ $MODE == 'unit-tests' ]]; then make test; fi - - if [[ $MODE == 'linters' ]]; then GOGC=30 make lint; fi - - if [[ $MODE == 'website' ]]; then make website-test; fi - -branches: - only: - - master matrix: fast_finish: true allow_failures: - - go: tip + - go: tip + include: + - name: "make lintrest" + script: GOGC=20 make lintrest + - name: "make tflint" + script: make tflint + - name: "make test" + script: make test + - name: "make website-lint" + script: make website-lint + - name: "make website-test" + script: make website-test diff --git a/CHANGELOG.md b/CHANGELOG.md index 379f3d7e583d..56272bb086b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,326 @@ -## 1.31.0 (Unreleased) +## 1.35.0 (Unreleased) FEATURES: -* **New Resource:** `azurerm_iot_dps` [GH-3618] +* **New Data Source:** `azurerm_app_service_certificate` [GH-4468] +* **New Data Source:** `azurerm_public_ip_prefix` [GH-4340] +* **New Data Source:** `azurerm_storage_management_policy` [GH-3819] +* **New Resource:** `azurerm_bot_channel_slack` [GH-4367] +* **New Resource:** `azurerm_bot_channel_email` [GH-4389] +* **New Resource:** `azurerm_bot_web_app` [GH-4411] +* **New Resource:** `azurerm_dashboard` [GH-4357] +* **New Resource:** `azurerm_eventhub_namespace_disaster_recovery_config` [GH-4425] +* **New Resource:** `azurerm_storage_data_lake_gen2_filesystem` [GH-4457] +* **New Resource:** `azurerm_storage_management_policy` [GH-3819] + +IMPROVEMENTS: + +* dependencies: upgrading `github.com/Azure/azure-sdk-for-go` to `v33.2.0` [GH-4334] +* kusto: updating to API version `2019-05-15` [GH-4376] +* `azurerm_analysis_services_server` - support for `backup_blob_container_uri` and `server_full_name` [GH-4397] +* `azurerm_api_management_api` - deprecate `sku` in favour of the `sku_name` property [GH-3154] +* `azurerm_app_service_custom_hostname_binding` - support for `ssl_state` and `thumbprint` [GH-4204] +* `azurerm_app_service_slot` - support for `logs` [GH-4473] +* `azurerm_application_insights_analytics_item` - Add support for App Insights Analytics Items [GH-4374] +* `azurerm_kubernetes_cluster` - support for updating the Service Principal [GH-4469] +* `azurerm_function_app` - changes to `app_service_plan_id` no longer force a new resource [GH-4439] +* `azurerm_eventhub_namespace` - support for the `network_rulesets` property [GH-4409] +* `azurerm_servicebus_namespace` - support for `zone_redundant` [GH-4432] + +BUG FIXES: + +* provider: Ensuring the user agent is configured [GH-4463] +* provider: Use real Terraform version [GH-4464] +* `azurerm_container_registry` - checking the `name` is globally unique during creation [GH-4424] +* `azurerm_hdinsight_hadoop_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_hbase_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_interactive_query_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_kafka_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_ml_services_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_rserver_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_spark_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_hdinsight_storm_cluster ` - handling the API now masking passwords [GH-4489] +* `azurerm_key_vault_certificate` - storing the certificate data as hex [GH-4335] +* `azurerm_kubernetes_cluster` - fixing a bug where upgrading to 1.34.0 would require resource recreation [GH-4469] +* `azurerm_public_ip` - ensuring that `public_ip_prefix_id` is read [GH-4344] +* `azurerm_role_assignment` - changing the `skip_service_principal_aad_check` property no longer forces a new resource [GH-4412] +* `azurerm_storage_blob` - reading the properties after an update [GH-4452] + +## 1.34.0 (September 18, 2019) + +FEATURES: + +* **New Data Source:** `azurerm_network_ddos_protection_plan` ([#4228](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4228)) +* **New Data Source:** `azurerm_proximity_placement_group` ([#4020](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4020)) +* **New Data Source:** `azurerm_servicebus_namespace_authorization_rule` ([#4294](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4294)) +* **New Data Source:** `azurerm_sql_database` ([#4210](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4210)) +* **New Data Source:** `azurerm_storage_account_blob_container_sas` ([#4195](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4195)) +* **New Resource:** `azurerm_app_service_certificate` ([#4192](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4192)) +* **New Resource:** `azurerm_app_service_source_control_token` ([#4214](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4214)) +* **New Resource:** `azurerm_bot_channels_registration` ([#4245](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4245)) +* **New Resource:** `azurerm_bot_connection` ([#4311](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4311)) +* **New Resource:** `azurerm_frontdoor` ([#3933](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3933)) +* **New Resource:** `azurerm_frontdoor_firewall_policy` ([#4125](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4125)) +* **New Resource:** `azurerm_kusto_cluster` ([#4129](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4129)) +* **New Resource:** `azurerm_kusto_database` ([#4149](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4149)) +* **New Resource:** `azurerm_marketplace_agreement` ([#4305](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4305)) +* **New Resource:** `azurerm_private_dns_zone_virtual_network_link` ([#3789](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3789)) +* **New Resource:** `azurerm_proximity_placement_group` ([#4020](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4020)) +* **New Resource:** `azurerm_stream_analytics_output_servicebus_topic` ([#4164](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4164)) +* **New Resource:** `azurerm_web_application_firewall_policy` ([#4119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4119)) + +IMPROVEMENTS: + +* dependencies: updating `github.com/Azure/azure-sdk-for-go` to `v32.5.0` ([#4166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4166)) +* dependencies: updating `github.com/Azure/go-autorest` to `v0.9.0` ([#4166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4166)) +* dependencies: updating `github.com/hashicorp/go-azure-helpers` to `v0.7.0` ([#4166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4166)) +* dependencies: updating `github.com/terraform-providers/terraform-provider-azuread` to `v0.6.0` ([#4166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4166)) +* dependencies: updating `github.com/hashicorp/terraform` to `v0.12.8` ([#4341](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4341)) +* compute: updating the API Version to `2019-07-01` ([#4331](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4331)) +* network: updating to API version `2019-06-01` ([#4291](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4291)) +* network: reverting the locking changes from #3673 ([#3673](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3673)) +* storage: caching the Resource Group Name / Account Key ([#4205](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4205)) +* storage: switching to use SharedKey for authentication with Blobs/Containers rather than SharedKeyLite ([#4235](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4235)) +* Data Source: `azurerm_storage_account` - gracefully degrading when there's a ReadOnly lock/the user doesn't have permissions to list the Keys for the storage account ([#4248](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4248)) +* Data Source: `azurerm_storage_account_sas` - adding an `ISO8601` validator to the `start` and `end` dates ([#4064](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4064)) +* Data Source: `azurerm_virtual_network` - support for the `location` property ([#4281](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4281)) +* `azurerm_api_management` - support for multiple `additional_location` blocks ([#4175](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4175)) +* `azurerm_application_gateway` - allowing `capacity` to be set to `32` ([#4189](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4189)) +* `azurerm_application_gateway` - support OWASP version `3.1` for the `rule_set_version` property ([#4263](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4263)) +* `azurerm_application_gateway` - support for the `trusted_root _certificate` property ([#4206](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4206)) +* `azurerm_app_service` - fixing a bug where the Application `logs` block would get reset when `app_settings` were configured ([#4243](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4243)) +* `azurerm_app_service` - support for sending HTTP Logs to Blob Storage ([#4249](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4249)) +* `azurerm_app_service` - the `ip_restriction.ip_address` property is now optional ([#4184](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4184)) +* `azurerm_app_service_slot` - the `ip_restriction.ip_address` property is now optional ([#4184](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4184)) +* `azurerm_availability_set` - support for the `proximity_placement_group_id` property ([#4020](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4020)) +* `azurerm_cognitive_account` - supporting `CognitiveServices` as a `kind` ([#4209](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4209)) +* `azurerm_container_registry` - support for configuring Virtual Network Rules to Subnets ([#4293](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4293)) +* `azurerm_cosmosdb_account` - correctly validate `max_interval_in_seconds` & `max_staleness_prefix` for geo replicated accounts ([#4273](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4273)) +* `azurerm_cosmosdb_account` - increase creation & deletion wait timeout to `3` hours ([#4271](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4271)) +* `azurerm_cosmosdb_sql_container` - changing the `unique_key.paths` property now forces a new resource ([#4163](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4163)) +* `azurerm_eventhub_namespace` - changing the `kafka_enabled` property now forces a new resource ([#4264](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4264)) +* `azurerm_kubernetes_cluster` - support for configuring the `kube_dashboard` within the `addon_profile` block ([#4139](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4139)) +* `azurerm_kubernetes_cluster` - prevent `pod_cidr` and azure `network_plugin` from being set at the same time causing a new resource to be created ([#4286](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4286)) +* `azurerm_mariadb_server` - support for version `10.3` ([#4170](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4170)) +* `azurerm_mariadb_server` - support for configuring `auto_grow` ([#4302](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4302)) +* `azurerm_managed_disk` - add support for the Ultra SSD `disk_iops_read_write` & `disk_mbps_read_write` properties ([#4102](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4102)) +* `azurerm_mysql_server` - support for configuring `auto_grow` ([#4303](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4303)) +* `azurerm_private_dns_zone` - polling until the dns zone is marked as fully provisioned ([#4307](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4307)) +* `azurerm_postgresql_server` - support for configuring `auto_grow` ([#4220](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4220)) +* `azurerm_resource_group` - the `name` field can now be up to 90 characters ([#4233](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4233)) +* `azurerm_role_assignment` - add `principal_type` and `skip_service_principal_aad_check` properties ([#4168](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4168)) +* `azurerm_storage_account` - gracefully degrading when there's a ReadOnly lock/the user doesn't have permissions to list the Keys for the storage account ([#4248](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4248)) +* `azurerm_storage_blob` - switching over to use the new Storage SDK ([#4179](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4179)) +* `azurerm_storage_blob` - support for Append Blobs ([#4238](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4238)) +* `azurerm_storage_blob` - support for configuring the `access_tier` ([#4238](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4238)) +* `azurerm_storage_blob` - support for specifying Block Blob content via `source_content` ([#4238](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4238)) +* `azurerm_storage_blob` - the `type` field is now Required, since it had to be set anyway ([#4238](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4238)) +* `azurerm_storage_share_directory` - support for upper-case characters in the `name` field ([#4178](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4178)) +* `azurerm_storage_table` - using the correct storage account name when checking for the presence of an existing storage table ([#4234](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4234)) +* `azurerm_stream_analytics_job` - the field `data_locale` is now optional ([#4190](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4190)) +* `azurerm_stream_analytics_job` - the field `events_late_arrival_max_delay_in_seconds` is now optional ([#4190](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4190)) +* `azurerm_stream_analytics_job` - the field `events_out_of_order_policy` is now optional ([#4190](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4190)) +* `azurerm_stream_analytics_job` - the field `output_error_policy` is now optional ([#4190](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4190)) +* `azurerm_subnet` - support for the actions `Microsoft.Network/virtualNetworks/subnets/join/action` and `Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action` ([#4137](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4137)) +* `azurerm_virtual_machine` - support for `UltraSSD_LRS` managed disks ([#3860](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3860)) +* `azurerm_virtual_machine` - support for the `proximity_placement_group_id` property ([#4020](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4020)) +* `azurerm_virtual_machine_scale_set` - support for the `proximity_placement_group_id` property ([#4020](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4020)) + +BUG FIXES: + +* `azurerm_app_service` - will no longer panic from when an access restriction rule involves a virtual network ([#4184](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4184)) +* `azurerm_app_service_slot` - will no longer panic from when an access restriction rule involves a virtual network ([#4184](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4184)) +* `azurerm_app_service_plan` and `azurerm_app_service_slot` crash fixes ([#4184](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4184)) +* `azurerm_container_group` - make `storage_account_key` field in `volume` block sensitive ([#4201](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4201)) +* `azurerm_key_vault_certificate` - prevented a panic caused by an empty element in `extended_key_usage` ([#4272](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4272)) +* `azurerm_log_analytics_linked_service` - will no longer panic if no items are passed into the property `linked_service_properties` ([#4142](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4142)) +* `azurerm_log_analytics_workspace_linked_service` - will no longer panic if no items are passed into the property `linked_service_properties` ([#4152](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4152)) +* `azurerm_network_interface` - changing the `ip_configuration` property to no longer force new resource ([#4155](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4155)) +* `azurerm_virtual_network_peering` - prevent nil object from being read ([#4180](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4180)) + +## 1.33.1 (August 27, 2019) + +* networking: reducing the number of locks to avoid deadlock when creating 3 or more subnets with Network Security Group/Route Table Associations ([#3673](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3673)) + +## 1.33.0 (August 22, 2019) + +FEATURES: + +* **New Data Source:** `azurerm_dev_test_virtual_network` ([#3746](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3746)) +* **New Resource:** `azurerm_cosmosdb_sql_container` ([#3871](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3871)) +* **New Resource:** `azurerm_container_registry_webhook` ([#4112](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4112)) +* **New Resource:** `azurerm_dev_test_lab_schedule` ([#3554](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3554)) +* **New Resource:** `azurerm_mariadb_virtual_network_rule` ([#4048](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4048)) +* **New Resource:** `azurerm_mariadb_configuration` ([#4060](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4060)) +* **New Resource:** `azurerm_private_dns_cname_record` ([#4028](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4028)) +* **New Resource:** `azurerm_recovery_services_fabric` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_recovery_services_protection_container` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_recovery_services_replication_policy` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_recovery_services_protection_container_mapping` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_recovery_network_mapping` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_recovery_replicated_vm` ([#4003](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4003)) +* **New Resource:** `azurerm_sql_failover_group` ([#3901](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3901)) +* **New Resource:** `azurerm_virtual_wan` ([#4089](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4089)) + +IMPROVEMENTS: + +* all resources: increasing the maximum number of tags from `15` to `50` ([#4071](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4071)) +* dependencies: upgrading `github.com/tombuildsstuff/giovanni` to `v0.3.2` ([#4122](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4122)) +* dependencies: upgrading the `authorization` SDK to `2018-09-01` ([#4063](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4063)) +* dependencies: upgrading `github.com/hashicorp/terraform` to `0.12.6` ([#4041](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4041)) +* internal: removing a duplicate Date/Time from the debug logs ([#4024](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4024)) +* Data Source `azurerm_dns_zone`: deprecating the `zone_type` field ([#4033](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4033)) +* `azurerm_app_service` - `filesystem` logging can now be set. ([#4025](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4025)) +* `azurerm_batch_pool` - Support for Container Registry configurations ([#4072](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4072)) +* `azurerm_container_group` - support for attaching to a (Private) Virtual Network ([#3716](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3716)) +* `azurerm_container_group` - `log_type` can now be an empty string ([#4013](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4013)) +* `azurerm_cognitive_account` - Adding 'QnAMaker' as Kind ([#4126](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4126)) +* `azurerm_dns_zone` - deprecating the `zone_type` field ([#4033](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4033)) +* `azurerm_function_app` - support for cors ([#3949](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3949)) +* `azurerm_function_app` - support for the `virtual_network_name` property ([#4078](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4078)) +* `azurerm_iot_dps` - add support for the `linked_hub` property ([#3922](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3922)) +* `azurerm_kubernetes_cluster` - support for the `enable_pod_security_policy` property ([#4098](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4098)) +* `azurerm_monitor_diagnostic_setting` - support for `log_analytics_destination_type` ([#3987](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3987)) +* `azurerm_role_assignment` - now supports management groups ([#4063](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4063)) +* `azurerm_storage_account` - requesting an access token using the ARM Authorizer ([#4099](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4099)) +* `azurerm_storage_account` - support for `BlockBlobStorage` ([#4131](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4131)) +* `azurerm_subnet` - support for the Service Endpoints `Microsoft.BareMetal/AzureVMware`, `Microsoft.BareMetal/CrayServers`, `Microsoft.Databricks/workspaces` and `Microsoft.Web/hostingEnvironments` ([#4115](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4115)) +* `azurerm_traffic_manager_profile` - support for the `interval_in_seconds`, `timeout_in_seconds`, and `tolerated_number_of_failures` properties ([#3473](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3473)) +* `azurerm_user_assigned_identity` - the `name` field can now be up to 128 characters ([#4094](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4094)) + +BUG FIXES: + +* `azurerm_app_service_plan` - workaround for missing error on 404 ([#3990](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3990)) +* `azurerm_batch_certificate` - the `thumbprint_algorithm` property is now case insensitive ([#3977](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3977)) +* `azurerm_notification_hub_authorization_rule - fixing an issue when creating multiple authorization rules at the same time ([#4087](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4087)) +* `azurerm_postgresql_server` - removal of unsupported version `10.2` ([#3915](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3915)) +* `azurerm_role_definition` - enture `role_definition_id` is correctly set if left empty during creation ([#3913](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3913)) +* `azurerm_storage_account` - making `default_action` within the `network_rules` block required ([#4037](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4037)) +* `azurerm_storage_account` - making the `network_rules` block computed ([#4037](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4037)) +* `azurerm_storage_queue` - switching to using SharedKey for authentication ([#4122](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4122)) +* `azurerm_storage_share` - allow up to 100TB for the `quota` property ([#4054](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4054)) +* `azurerm_storage_share_directory` - handling the share being eventually consistent ([#4122](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4122)) +* `azurerm_storage_share_directory` - allowing nested directories ([#4122](https://github.com/terraform-providers/terraform-provider-azurerm/issues/4122)) + +## 1.32.1 (July 31, 2019) + +BUG FIXES: + +* `azurerm_application_gateway` fix an index out of range crash ([#3966](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3966)) +* `azurerm_api_management_backend` - ensuring a nil `certificates` object is sent to the API instead of an empty one ([#3931](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3931)) +* `azurerm_api_managment_product` - additional validation for `approval_required` ([#3945](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3945)) +* `azurerm_network_ddos_protection_plan` - correctly decodes the resource ID on read/delete ([#3975](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3975)) +* `azurerm_dev_test_virtual_network` - generate subnet IDs in the correct format ([#3717](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3717)) +* `azurerm_iot_dps` fixed deletion issue when using a service principal ([#3973](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3973)) +* `azurerm_kubernetes_cluster` - the `load_balancer_sku` property is now case insensitive ([#3958](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3958)) +* `azurerm_postgresql_server` - add missing support for version `11.0` ([#3970](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3970)) +* `azurerm_storage_*` - prevent multiple panics when a storage account/resource group cannot be found ([#3986](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3986)) +* `azurerm_storage_account` - fix `enable_advanced_threat_protection` create/read for unsupported regions ([#3947](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3947)) +* `azurerm_storage_table` - now migrates older versions of the resource id to the new format ([#3932](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3932)) +* `azurerm_virtual_machine_scale_set` - the `ssh_keys` property of the `os_profile_linux_config` block now recognizes updates ([#3837](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3837)) +* `azurerm_virtual_machine_scale_set` - changes made to the `network_profile` property should now be correctly reflected during updates ([#3821](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3821)) + + +## 1.32.0 (July 24, 2019) + +FEATURES: + +* **New Data Source:** `azurerm_maps_account` ([#3698](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3698)) +* **New Data Source:** `azurerm_mssql_elasticpool` ([#3824](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3824)) +* **New Resource:** `azurerm_analysis_services_server` ([#3721](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3721)) +* **New Resource:** `azurerm_api_management_backend` ([#3676](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3676)) +* **New Resource:** `azurerm_batch_application` ([#3825](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3825)) +* **New Resource:** `azurerm_maps_account` ([#3698](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3698)) +* **New Resource:** `azurerm_private_dns_zone_a_record` ([#3849](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3849)) +* **New Resource:** `azurerm_storage_table_entity` ([#3831](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3831)) +* **New Resource:** `azurerm_storage_share_directory` ([#3802](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3802)) + +IMPROVEMENTS: + +* dependencies: upgrading to `v31.0.0` of `github.com/Azure/azure-sdk-for-go` ([#3786](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3786)) +* dependencies: upgrading to `v0.5.0` of `github.com/hashicorp/go-azure-helpers` ([#3850](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3850)) +* dependencies: upgrading the `containerservice` SDK to `2019-02-01` ([#3787](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3787)) +* dependencies: upgrading the `subscription` SDK to `2018-06-01` ([#3811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3811)) +* authentication: showing a more helpful error when attempting to use the Azure CLI authentication when logged in as a Service Principal ([#3850](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3850)) +* Data Source `azurerm_function_app` - support for `auth_settings` ([#3893](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3893)) +* Data Source `azurerm_subscription` - support the `tenant_id` property ([#3811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3811)) +* `azurerm_app_service` - support for backups ([#3804](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3804)) +* `azurerm_app_service` - support for storage mounts ([#3792](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3792)) +* `azurerm_app_service` - support for user assigned identities ([#3637](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3637)) +* `azurerm_app_service_slot` - support for `auth_settings` ([#3897](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3897)) +* `azurerm_app_service_slot` - support for user assigned identities ([#3637](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3637)) +* `azurerm_application_gateway` - Support for Managed Identities ([#3648](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3648)) +* `azurerm_batch_pool` - support for custom images with the `storage_image_reference` property ([#3530](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3530)) +* `azurerm_batch_account` - expose required properties for when `pool_allocation_mode` is `UserSubscription` ([#3535](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3535)) +* `azurerm_cognitive_account` - add support for `CustomVision.Training` and `CustomVision.Prediction` to the `kind` property ([#3817](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3817)) +* `azurerm_container_registry` - support for `network_rule_set` property ([#3194](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3194)) +* `azurerm_cosmosdb_account` - validate `max_interval_in_seconds` and `max_staleness_prefix` correctly when using more then 1 geo_location ([#3906](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3906)) +* `azurerm_function_app` - support for `auth_settings` ([#3893](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3893)) +* `azurerm_iothub` - support for the `file_upload` property ([#3735](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3735)) +* `azurerm_kubernetes_cluster` - support for auto scaling ([#3361](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3361)) +* `azurerm_kubernetes_cluster` - support for `custom_resource_group_name` ([#3785](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3785)) +* `azurerm_kubernetes_cluster` - support for the `node_taints` property ([#3787](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3787)) +* `azurerm_kubernetes_cluster` - support for the `windows_profile` property ([#3519](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3519)) +* `kubernetes_cluster` - support for specifying the `load_balancer_sku` property ([#3890](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3890)) +* `azurerm_recovery_services_protected_vm` - changing `backup_policy_id` no longer forces a new resource ([#3822](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3822)) +* `azurerm_security_center_contact` - the `phone` property is now optional ([#3761](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3761)) +* `azurerm_storage_account` - the `account_kind` property now supports `FileStorage` ([#3750](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3750)) +* `azurerm_storage_account` - support for the `enable_advanced_threat_protection` property ([#3782](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3782)) +* `azurerm_storage_account` - support for `queue_properties` ([#3859](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3859)) +* `azurerm_storage_blob` - making `metadata` a computed field ([#3842](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3842)) +* `azurerm_storage_container` - switching to use github.com/tombuildsstuff/giovanni ([#3857](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3857)) +* `azurerm_storage_container` - adding support for `metadata` ([#3857](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3857)) +* `azurerm_storage_container` - can now create containers with the name `$web` ([#3896](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3896)) +* `azurerm_storage_queue` - switching to use github.com/tombuildsstuff/giovanni ([#3832](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3832)) +* `azurerm_storage_share` - switching to use github.com/tombuildsstuff/giovanni ([#3828](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3828)) +* `azurerm_storage_share` - support for configuring ACL's ([#3830](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3830)) +* `azurerm_storage_share` - support for configuring MetaData ([#3830](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3830)) +* `azurerm_storage_table` - switching to use github.com/tombuildsstuff/giovanni ([#3834](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3834)) +* `azurerm_storage_table` - support for configuring ACL's ([#3847](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3847)) +* `azurerm_traffic_manager_endpoint` - supper for `custom_header` and `subnet` properties ([#3655](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3655)) +* `azurerm_virtual_machine` - switching over to use the github.com/tombuildsstuff/giovanni Storage SDK ([#3838](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3838)) +* `azurerm_virtual_machine` - looking up the data disks attached to the Virtual Machine when optionally deleting them upon deletion rather than parsing them from the config ([#3838](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3838)) +* `azurerm_virtual_machine_scale_set` - prevent `public_ip_address_configuration` from being lost during update ([#3767](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3767)) + +BUG FIXES: + +* `azurerm_image` - prevent crash when using `data_disk` ([#3797](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3797)) +* `azurerm_role_assignment` - now correctly uses `scope` when looking up the role definition by name ([#3768](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3768)) + +## 1.31.0 (June 28, 2019) + +FEATURES: + +* increase the default timeout to `3 hours` ([#3737](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3737)) +* **New Resource:** `azurerm_iot_dps` ([#3618](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3618)) +* **New Resource:** `azurerm_iot_dps_certificate` ([#3567](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3645)) +* **New Resource:** `azurerm_mariadb_firewall_rule` ([#3720](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3720)) +* **New Resource:** `azurerm_private_dns_zone` ([#3718](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3718)) +* **New Resource:** `azurerm_stream_analytics_output_mssql` ([#3567](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3567)) + +IMPROVEMENTS: + +* Data Source `azurerm_key_vault` - deprecated `sku` in favour of `sku_name` ([#3119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3119)) +* `azurerm_app_service` - support for shipping the application logs to blob storage ([#3520](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3520)) +* `azurerm_app_service_plan` - prevent a panic during import ([#3657](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3657)) +* `azurerm_app_service_slot` - updating `identity` no longer forces a new resource ([#3702](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3702)) +* `azurerm_automation_account` - deprecated `sku` in favour of `sku_name` ([#3119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3119)) +* `azurerm_key_vault` - deprecated `sku` in favour of `sku_name` ([#3119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3119)) +* `azurerm_key_vault_key` - add support for Elliptic Curve based keys ([#1814](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1814)) +* `azurerm_traffic_manager_profile` - `ttl` can now be 1 second ([#3632](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3632)) +* `azurerm_eventgrid_event_subscription` - now retrieves the full URL for event webhooks ([#3630](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3630)) +* `azurerm_lb` - support for the `public_ip_prefix_id` property ([#3675](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3675)) +* `azurerm_mysql_server` - add validation to the `name` property ([#3695](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3695)) +* `azurerm_notification_hub_namespace` - deprecated `sku` in favour of `sku_name` ([#3119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3119)) +* `azurerm_redis_firewall_rule` - no longer fails with multiple rules ([#3731](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3731)) +* `azurerm_relay_namespace` - deprecated `sku` in favour of `sku_name` ([#3119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3119)) +* `azurerm_service_fabric_cluster` - `tenant_id`, `cluster_application_id`, and `client_application_id` are now updateable ([#3654](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3654)) +* `azurerm_service_fabric_cluster` - ability to set `certificate_common_names` ([#3652](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3652)) +* `azurerm_storage_account` - ability to set `default_action` oi the `network_rules` block ([#3255](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3255)) + +BUG FIXES: + +* `azurerm_cosmosdb_account` - will ignore `500` responses from `documentdb.DatabaseAccountsClient#CheckNameExists` requests to work around a broken API ([#3747](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3747)) ## 1.30.1 (June 07, 2019) @@ -564,7 +882,7 @@ IMPROVEMENTS: * dependencies: upgrading to v21.3.0 of `github.com/Azure/azure-sdk-for-go` ([#2163](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2163)) * refactoring: decoupling Resource Provider Registration to enable splitting out the authentication library ([#2197](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2197)) * sdk: upgrading to `2018-10-01` of the `containerinstance` sdk ([#2174](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2174)) -* `azurerm_automation_account` - exposing `dsc_server_endpoint`, `dsc_primary_access_key`, `dsc_secondary_access_key` properties [[#2166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2166)] +* `azurerm_automation_account` - exposing `dsc_server_endpoint`, `dsc_primary_access_key`, `dsc_secondary_access_key` properties ([#2166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2166)) * `azurerm_automation_account` - support for the `free` SKU ([#2166](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2166)) * `azurerm_client_config` - ensuring the `service_principal_application_id` and `service_principal_object_id` are always set ([#2120](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2120)) * `azurerm_cosmosdb_account` - support for the `enable_multiple_write_locations` property ([#2109](https://github.com/terraform-providers/terraform-provider-azurerm/issues/2109)) @@ -694,7 +1012,7 @@ FEATURES: IMPROVEMENTS: * 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)) +* 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)) @@ -1076,7 +1394,7 @@ IMPROVEMENTS: * `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_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)) diff --git a/GNUmakefile b/GNUmakefile index e7e48cdcfa3e..1cdfd03baaa4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,33 +9,26 @@ GOFLAGS=-mod=vendor default: build +tools: + @echo "==> installing required tooling..." + @sh "$(CURDIR)/scripts/gogetcookie.sh" + GO111MODULE=off go get -u github.com/client9/misspell/cmd/misspell + GO111MODULE=off go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + GO111MODULE=off go get -u github.com/bflad/tfproviderlint/cmd/tfproviderlint + build: fmtcheck go install build-docker: mkdir -p bin - docker run --rm -v $$(pwd)/bin:/go/bin -v $$(pwd):/go/src/github.com/terraform-providers/terraform-provider-azurerm -w /go/src/github.com/terraform-providers/terraform-provider-azurerm -e GOOS golang:1.12 make build - -test-docker: - docker run --rm -v $$(pwd):/go/src/github.com/terraform-providers/terraform-provider-azurerm -w /go/src/github.com/terraform-providers/terraform-provider-azurerm golang:1.12 make test - -test: fmtcheck - go test -i $(TEST) || exit 1 - echo $(TEST) | \ - xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 - -testacc: fmtcheck - TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 180m -ldflags="-X=github.com/terraform-providers/terraform-provider-azurerm/version.ProviderVersion=acc" - -debugacc: fmtcheck - TF_ACC=1 dlv test $(TEST) --headless --listen=:2345 --api-version=2 -- -test.v $(TESTARGS) + docker run --rm -v $$(pwd)/bin:/go/bin -v $$(pwd):/go/src/github.com/terraform-providers/terraform-provider-azurerm -w /go/src/github.com/terraform-providers/terraform-provider-azurerm -e GOOS golang:1.13 make build fmt: @echo "==> Fixing source code with gofmt..." # This logic should match the search logic in scripts/gofmtcheck.sh find . -name '*.go' | grep -v vendor | xargs gofmt -s -w -# Currently required by tf-deploy compile +# Currently required by tf-deploy compile, duplicated by linters fmtcheck: @sh "$(CURDIR)/scripts/gofmtcheck.sh" @@ -47,11 +40,34 @@ lint: @echo "==> Checking source code against linters..." golangci-lint run ./... -tools: - @echo "==> installing required tooling..." - @sh "$(CURDIR)/scripts/gogetcookie.sh" - GO111MODULE=off go get -u github.com/client9/misspell/cmd/misspell - GO111MODULE=off go get -u github.com/golangci/golangci-lint/cmd/golangci-lint +# we have split off static check because it causes travis to fail with an OOM error +lintstatic: + @echo "==> Checking source code against static check linters..." + (while true; do sleep 300; echo "(Hey travis! I'm still alive and linting)"; done) & PID=$$!; echo $$PID; \ + golangci-lint run ./... -v --no-config --concurrency 1 --deadline=30m10s --disable-all --enable=staticcheck; ES=$$?; kill -9 $$PID; exit $$ES + +lintrest: + @echo "==> Checking source code against linters..." + golangci-lint run ./... -v --config .golangci-travisrest.yml + +tflint: + @echo "==> Checking source code against terraform provider linters..." + @tfproviderlint \ + -R001 -R002 -R003 -R004\ + -S001 -S002 -S003 -S004 -S005 -S006 -S007 -S008 -S009 -S010 -S011 -S012 -S013 -S014 -S015 -S016 -S017 -S018 -S019\ + ./$(PKG_NAME) + +whitespace: + @echo "==> Fixing source code with whitespace linter..." + golangci-lint run ./... --no-config --disable-all --enable=whitespace --fix + +test-docker: + docker run --rm -v $$(pwd):/go/src/github.com/terraform-providers/terraform-provider-azurerm -w /go/src/github.com/terraform-providers/terraform-provider-azurerm golang:1.13 make test + +test: fmtcheck + go test -i $(TEST) || exit 1 + echo $(TEST) | \ + xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 test-compile: @if [ "$(TEST)" = "./..." ]; then \ @@ -61,6 +77,16 @@ test-compile: fi go test -c $(TEST) $(TESTARGS) +testacc: fmtcheck + TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 180m -ldflags="-X=github.com/terraform-providers/terraform-provider-azurerm/version.ProviderVersion=acc" + +debugacc: fmtcheck + TF_ACC=1 dlv test $(TEST) --headless --listen=:2345 --api-version=2 -- -test.v $(TESTARGS) + +website-lint: + @echo "==> Checking website against linters..." + @misspell -error -source=text -i hdinsight website/ + website: ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO))) echo "$(WEBSITE_REPO) not found in your GOPATH (necessary for layouts and assets), get-ting..." diff --git a/README.md b/README.md index 78f8f739831b..04202064efa6 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,21 @@ -AzureRM Terraform Provider -================== +# Terraform Provider for Azure (Resource Manager) -- Website: https://www.terraform.io -- [![Gitter chat](https://badges.gitter.im/hashicorp-terraform/Lobby.png)](https://gitter.im/hashicorp-terraform/Lobby) -- Mailing list: [Google Groups](http://groups.google.com/group/terraform-tool) -- Slack workspace: [Terraform on Azure](https://terraform-azure.slack.com) ([Request Invite](https://join.slack.com/t/terraform-azure/shared_invite/enQtNDMzNjQ5NzcxMDc3LTJkZTJhNTg3NTE5ZTdjZjFhMThmMTVmOTg5YWJkMDU1YTMzN2YyOWJmZGM3MGI4OTQ0ODQxNTEyNjdjMDAxMjM)) +The AzureRM Provider supports Terraform 0.10.x and later - but Terraform 0.12.x is recommended. -General Requirements ------------- +* [Terraform Website](https://www.terraform.io) +* [AzureRM Provider Documentation](https://www.terraform.io/docs/providers/azurerm/index.html) +* [AzureRM Provider Usage Examples](https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples) +* [Slack Workspace for Contributors](https://terraform-azure.slack.com) ([Request Invite](https://join.slack.com/t/terraform-azure/shared_invite/enQtNDMzNjQ5NzcxMDc3LTJkZTJhNTg3NTE5ZTdjZjFhMThmMTVmOTg5YWJkMDU1YTMzN2YyOWJmZGM3MGI4OTQ0ODQxNTEyNjdjMDAxMjM)) -- [Terraform](https://www.terraform.io/downloads.html) 0.10.x -- [Go](https://golang.org/doc/install) 1.12.x (to build the provider plugin) - -Windows Specific Requirements ------------------------------ -- [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm) -- [Git Bash for Windows](https://git-scm.com/download/win) - -For *GNU32 Make*, make sure its bin path is added to PATH environment variable.* - -For *Git Bash for Windows*, at the step of "Adjusting your PATH environment", please choose "Use Git and optional Unix tools from Windows Command Prompt".* - -Building The Provider ---------------------- - -Clone repository to: `$GOPATH/src/github.com/terraform-providers/terraform-provider-azurerm` - -```sh -$ mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers -$ git clone git@github.com:terraform-providers/terraform-provider-azurerm -``` - -Enter the provider directory and build the provider - -```sh -$ cd $GOPATH/src/github.com/terraform-providers/terraform-provider-azurerm -$ make build -``` - -Using the provider ----------------------- +## Usage Example ``` # Configure the Microsoft Azure Provider provider "azurerm" { + # We recommend pinning to the specific version of the Azure Provider you're using + # since new versions are released frequently + version = "=1.34.0" + # More information on the authentication methods supported by # the AzureRM Provider can be found here: # http://terraform.io/docs/providers/azurerm/index.html @@ -71,12 +43,34 @@ resource "azurerm_virtual_network" "test" { Further [usage documentation is available on the Terraform website](https://www.terraform.io/docs/providers/azurerm/index.html). -Developing the Provider ---------------------------- +## Developer Requirements -If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.12+ is **required**). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`. +* [Terraform](https://www.terraform.io/downloads.html) version 0.10.x + +* [Go](https://golang.org/doc/install) version 1.13.x (to build the provider plugin) -To compile the provider, run `make build`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. +If you're on Windows you'll also need: +* [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm) +* [Git Bash for Windows](https://git-scm.com/download/win) + +For *GNU32 Make*, make sure its bin path is added to PATH environment variable.* + +For *Git Bash for Windows*, at the step of "Adjusting your PATH environment", please choose "Use Git and optional Unix tools from Windows Command Prompt".* + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.13+ is **required**). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`. + +First clone the repository to: `$GOPATH/src/github.com/terraform-providers/terraform-provider-azurerm` + +```sh +$ mkdir -p $GOPATH/src/github.com/terraform-providers; cd $GOPATH/src/github.com/terraform-providers +$ git clone git@github.com:terraform-providers/terraform-provider-azurerm +$ cd $GOPATH/src/github.com/terraform-providers/terraform-provider-azurerm +``` + +Once inside the provider directory, you can run `make tools` to install the dependent tooling required to compile the provider. + +At this point you can compile the provider by running `make build`, which will build the provider and put the provider binary in the `$GOPATH/bin` directory. ```sh $ make build @@ -85,7 +79,13 @@ $ $GOPATH/bin/terraform-provider-azurerm ... ``` -In order to run the unit tests for the provider, you can run: +You can also cross-compile if necessary: + +```sh +GOOS=windows GOARCH=amd64 make build +``` + +In order to run the Unit Tests for the provider, you can run: ```sh $ make test @@ -106,11 +106,7 @@ The following Environment Variables must be set in your shell prior to running a - `ARM_ENVIRONMENT` - `ARM_TEST_LOCATION` - `ARM_TEST_LOCATION_ALT` +- `ARM_TEST_LOCATION_ALT2` **Note:** Acceptance tests create real resources in Azure which often cost money to run. -Crosscompiling --------------- -```sh -GOOS=windows GOARCH=amd64 make build -``` diff --git a/azurerm/automation_variable.go b/azurerm/automation_variable.go index 1ff271962424..b53a77559de2 100644 --- a/azurerm/automation_variable.go +++ b/azurerm/automation_variable.go @@ -13,6 +13,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -129,7 +130,7 @@ func resourceAutomationVariableCreateUpdate(d *schema.ResourceData, meta interfa accountName := d.Get("automation_account_name").(string) varTypeLower := strings.ToLower(varType) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { resp, err := client.Get(ctx, resourceGroup, accountName, name) if err != nil { if !utils.ResponseWasNotFound(resp.Response) { @@ -192,7 +193,7 @@ func resourceAutomationVariableRead(d *schema.ResourceData, meta interface{}, va client := meta.(*ArmClient).automation.VariableClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -283,7 +284,7 @@ func resourceAutomationVariableDelete(d *schema.ResourceData, meta interface{}, client := meta.(*ArmClient).automation.VariableClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/common_hdinsight.go b/azurerm/common_hdinsight.go index 36cba27930db..149d1991b34a 100644 --- a/azurerm/common_hdinsight.go +++ b/azurerm/common_hdinsight.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/preview/hdinsight/mgmt/2018-06-01-preview/hdinsight" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -15,7 +16,7 @@ func hdinsightClusterUpdate(clusterKind string, readFunc schema.ReadFunc) schema client := meta.(*ArmClient).hdinsight.ClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -24,9 +25,9 @@ func hdinsightClusterUpdate(clusterKind string, readFunc schema.ReadFunc) schema name := id.Path["clusters"] if d.HasChange("tags") { - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) params := hdinsight.ClusterPatchParameters{ - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.Update(ctx, resourceGroup, name, params); err != nil { return fmt.Errorf("Error updating Tags for HDInsight %q Cluster %q (Resource Group %q): %+v", clusterKind, name, resourceGroup, err) @@ -63,7 +64,7 @@ func hdinsightClusterDelete(clusterKind string) schema.DeleteFunc { client := meta.(*ArmClient).hdinsight.ClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/config.go b/azurerm/config.go index 4a46b8c84952..e89053e8c404 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -3,1542 +3,283 @@ package azurerm import ( "context" "fmt" - "log" - "os" - "strings" - "sync" "time" - resourcesprofile "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" - "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement" - appinsights "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" - automationSvc "github.com/Azure/azure-sdk-for-go/services/automation/mgmt/2015-10-31/automation" - "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" - cdnSvc "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2017-10-12/cdn" - cognitiveSvc "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2017-04-18/cognitiveservices" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" - "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance" - "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2017-10-01/containerregistry" - "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-02-01/containerservice" - "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb" - databricksSvc "github.com/Azure/azure-sdk-for-go/services/databricks/mgmt/2018-04-01/databricks" - datafactorySvc "github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory" - analyticsAccount "github.com/Azure/azure-sdk-for-go/services/datalake/analytics/mgmt/2016-11-01/account" - "github.com/Azure/azure-sdk-for-go/services/datalake/store/2016-11-01/filesystem" - storeAccount "github.com/Azure/azure-sdk-for-go/services/datalake/store/mgmt/2016-11-01/account" - "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2016-05-15/dtl" - eventHubSvc "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - keyVault "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault" - "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" - "github.com/Azure/azure-sdk-for-go/services/logic/mgmt/2016-06-01/logic" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" - mediaSvc "github.com/Azure/azure-sdk-for-go/services/mediaservices/mgmt/2018-07-01/media" - "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" - notificationHubsSvc "github.com/Azure/azure-sdk-for-go/services/notificationhubs/mgmt/2017-04-01/notificationhubs" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" - "github.com/Azure/azure-sdk-for-go/services/preview/devspaces/mgmt/2018-06-01-preview/devspaces" - dnsSvc "github.com/Azure/azure-sdk-for-go/services/preview/dns/mgmt/2018-03-01-preview/dns" - eventGridSvc "github.com/Azure/azure-sdk-for-go/services/preview/eventgrid/mgmt/2018-09-15-preview/eventgrid" - hdinsightSvc "github.com/Azure/azure-sdk-for-go/services/preview/hdinsight/mgmt/2018-06-01-preview/hdinsight" - iotHubSvc "github.com/Azure/azure-sdk-for-go/services/preview/iothub/mgmt/2018-12-01-preview/devices" - "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights" - msiSvc "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" - "github.com/Azure/azure-sdk-for-go/services/preview/operationalinsights/mgmt/2015-11-01-preview/operationalinsights" - "github.com/Azure/azure-sdk-for-go/services/preview/operationsmanagement/mgmt/2015-11-01-preview/operationsmanagement" - "github.com/Azure/azure-sdk-for-go/services/preview/resources/mgmt/2018-03-01-preview/managementgroups" - "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v1.0/security" - signalrSvc "github.com/Azure/azure-sdk-for-go/services/preview/signalr/mgmt/2018-03-01-preview/signalr" - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - MsSql "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2017-10-01-preview/sql" - iotdps "github.com/Azure/azure-sdk-for-go/services/provisioningservices/mgmt/2018-01-22/iothub" - "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2016-06-01/recoveryservices" - "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2017-07-01/backup" - redisSvc "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" - relaySvc "github.com/Azure/azure-sdk-for-go/services/relay/mgmt/2017-04-01/relay" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-06-01/subscriptions" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-09-01/locks" - policySvc "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" - schedulerSvc "github.com/Azure/azure-sdk-for-go/services/scheduler/mgmt/2016-03-01/scheduler" - searchSvc "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2015-08-19/search" - servicebusSvc "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" - "github.com/Azure/azure-sdk-for-go/services/servicefabric/mgmt/2018-02-01/servicefabric" - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" - "github.com/Azure/azure-sdk-for-go/services/streamanalytics/mgmt/2016-03-01/streamanalytics" - "github.com/Azure/azure-sdk-for-go/services/trafficmanager/mgmt/2018-04-01/trafficmanager" - "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/apimgmt" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-helpers/authentication" + "github.com/hashicorp/go-azure-helpers/sender" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/analysisservices" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/apimanagement" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/applicationinsights" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/authorization" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/automation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/batch" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/bot" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cdn" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cognitive" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/containers" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/cosmos" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/databricks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/datafactory" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/datalake" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/devspace" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/devtestlabs" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/dns" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventgrid" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventhub" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/graph" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/hdinsight" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/iothub" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/logic" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maps" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mariadb" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/media" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/monitor" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/msi" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/notificationhub" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/policy" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/portal" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/postgres" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/privatedns" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/recoveryservices" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/redis" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/relay" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/scheduler" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/search" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/securitycenter" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/servicebus" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/servicefabric" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/signalr" - - mainStorage "github.com/Azure/azure-sdk-for-go/storage" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" - az "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/go-azure-helpers/authentication" - "github.com/hashicorp/terraform/httpclient" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" - "github.com/terraform-providers/terraform-provider-azurerm/version" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/sql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/streamanalytics" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/subscription" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/trafficmanager" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/web" ) // ArmClient contains the handles to all the specific Azure Resource Manager // resource classes' respective clients. type ArmClient struct { - clientId string - tenantId string - subscriptionId string - partnerId string + // inherit the fields from the parent, so that we should be able to set/access these at either level + clients.Client + + clientId string + tenantId string + subscriptionId string + partnerId string + + getAuthenticatedObjectID func(context.Context) (string, error) usingServicePrincipal bool - environment az.Environment - skipProviderRegistration bool - StopContext context.Context + environment azure.Environment + skipProviderRegistration bool // Services - apimgmt *apimgmt.Client + // NOTE: all new services should be Public as they're going to be relocated in the near-future + analysisservices *analysisservices.Client + apiManagement *apimanagement.Client + appInsights *applicationinsights.Client automation *automation.Client + authorization *authorization.Client + batch *batch.Client + bot *bot.Client cdn *cdn.Client cognitive *cognitive.Client + compute *clients.ComputeClient containers *containers.Client + cosmos *cosmos.Client databricks *databricks.Client dataFactory *datafactory.Client + datalake *datalake.Client devSpace *devspace.Client + devTestLabs *devtestlabs.Client dns *dns.Client + privateDns *privatedns.Client eventGrid *eventgrid.Client eventhub *eventhub.Client + frontdoor *frontdoor.Client + graph *graph.Client hdinsight *hdinsight.Client iothub *iothub.Client + keyvault *keyvault.Client + kusto *kusto.Client logAnalytics *loganalytics.Client + logic *logic.Client + managementGroups *managementgroup.Client + maps *maps.Client + mariadb *mariadb.Client media *media.Client + monitor *monitor.Client + mysql *mysql.Client msi *msi.Client + mssql *mssql.Client + network *network.Client notificationHubs *notificationhub.Client policy *policy.Client + portal *portal.Client + postgres *postgres.Client + recoveryServices *recoveryservices.Client redis *redis.Client relay *relay.Client - scheduler *scheduler.Client - search *search.Client - servicebus *servicebus.Client - signalr *signalr.Client - - // TODO: refactor - cosmosAccountsClient documentdb.DatabaseAccountsClient - - // Application Insights - appInsightsClient appinsights.ComponentsClient - appInsightsAPIKeyClient appinsights.APIKeysClient - appInsightsWebTestsClient appinsights.WebTestsClient - - // Authentication - roleAssignmentsClient authorization.RoleAssignmentsClient - roleDefinitionsClient authorization.RoleDefinitionsClient - applicationsClient graphrbac.ApplicationsClient - servicePrincipalsClient graphrbac.ServicePrincipalsClient - - // Autoscale Settings - autoscaleSettingsClient insights.AutoscaleSettingsClient - - // Batch - batchAccountClient batch.AccountClient - batchCertificateClient batch.CertificateClient - batchPoolClient batch.PoolClient - - // Compute - availSetClient compute.AvailabilitySetsClient - diskClient compute.DisksClient - imageClient compute.ImagesClient - galleriesClient compute.GalleriesClient - galleryImagesClient compute.GalleryImagesClient - galleryImageVersionsClient compute.GalleryImageVersionsClient - snapshotsClient compute.SnapshotsClient - usageOpsClient compute.UsageClient - vmExtensionImageClient compute.VirtualMachineExtensionImagesClient - vmExtensionClient compute.VirtualMachineExtensionsClient - vmScaleSetClient compute.VirtualMachineScaleSetsClient - vmImageClient compute.VirtualMachineImagesClient - vmClient compute.VirtualMachinesClient - - // DevTestLabs - devTestLabsClient dtl.LabsClient - devTestPoliciesClient dtl.PoliciesClient - devTestVirtualMachinesClient dtl.VirtualMachinesClient - devTestVirtualNetworksClient dtl.VirtualNetworksClient - - // Databases - mariadbDatabasesClient mariadb.DatabasesClient - mariadbServersClient mariadb.ServersClient - mysqlConfigurationsClient mysql.ConfigurationsClient - mysqlDatabasesClient mysql.DatabasesClient - mysqlFirewallRulesClient mysql.FirewallRulesClient - mysqlServersClient mysql.ServersClient - mysqlVirtualNetworkRulesClient mysql.VirtualNetworkRulesClient - postgresqlConfigurationsClient postgresql.ConfigurationsClient - postgresqlDatabasesClient postgresql.DatabasesClient - postgresqlFirewallRulesClient postgresql.FirewallRulesClient - postgresqlServersClient postgresql.ServersClient - postgresqlVirtualNetworkRulesClient postgresql.VirtualNetworkRulesClient - sqlDatabasesClient sql.DatabasesClient - sqlDatabaseThreatDetectionPoliciesClient sql.DatabaseThreatDetectionPoliciesClient - sqlElasticPoolsClient sql.ElasticPoolsClient - // Client for the new 2017-10-01-preview SQL API which implements vCore, DTU, and Azure data standards - msSqlElasticPoolsClient MsSql.ElasticPoolsClient - sqlFirewallRulesClient sql.FirewallRulesClient - sqlServersClient sql.ServersClient - sqlServerAzureADAdministratorsClient sql.ServerAzureADAdministratorsClient - sqlVirtualNetworkRulesClient sql.VirtualNetworkRulesClient - - // Data Lake Store - dataLakeStoreAccountClient storeAccount.AccountsClient - dataLakeStoreFirewallRulesClient storeAccount.FirewallRulesClient - dataLakeStoreFilesClient filesystem.Client - - // Data Lake Analytics - dataLakeAnalyticsAccountClient analyticsAccount.AccountsClient - dataLakeAnalyticsFirewallRulesClient analyticsAccount.FirewallRulesClient - - // KeyVault - keyVaultClient keyvault.VaultsClient - keyVaultManagementClient keyVault.BaseClient - - // Logic - logicWorkflowsClient logic.WorkflowsClient - - // Management Groups - managementGroupsClient managementgroups.Client - managementGroupsSubscriptionClient managementgroups.SubscriptionsClient - - // Monitor - monitorActionGroupsClient insights.ActionGroupsClient - monitorActivityLogAlertsClient insights.ActivityLogAlertsClient - monitorAlertRulesClient insights.AlertRulesClient - monitorDiagnosticSettingsClient insights.DiagnosticSettingsClient - monitorDiagnosticSettingsCategoryClient insights.DiagnosticSettingsCategoryClient - monitorLogProfilesClient insights.LogProfilesClient - monitorMetricAlertsClient insights.MetricAlertsClient - - // Networking - applicationGatewayClient network.ApplicationGatewaysClient - applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient - azureFirewallsClient network.AzureFirewallsClient - connectionMonitorsClient network.ConnectionMonitorsClient - ddosProtectionPlanClient network.DdosProtectionPlansClient - expressRouteAuthsClient network.ExpressRouteCircuitAuthorizationsClient - expressRouteCircuitClient network.ExpressRouteCircuitsClient - expressRoutePeeringsClient network.ExpressRouteCircuitPeeringsClient - ifaceClient network.InterfacesClient - loadBalancerClient network.LoadBalancersClient - localNetConnClient network.LocalNetworkGatewaysClient - netProfileClient network.ProfilesClient - packetCapturesClient network.PacketCapturesClient - publicIPClient network.PublicIPAddressesClient - publicIPPrefixClient network.PublicIPPrefixesClient - routesClient network.RoutesClient - routeTablesClient network.RouteTablesClient - secGroupClient network.SecurityGroupsClient - secRuleClient network.SecurityRulesClient - subnetClient network.SubnetsClient - vnetGatewayConnectionsClient network.VirtualNetworkGatewayConnectionsClient - vnetGatewayClient network.VirtualNetworkGatewaysClient - vnetClient network.VirtualNetworksClient - vnetPeeringsClient network.VirtualNetworkPeeringsClient - watcherClient network.WatchersClient - - // Recovery Services - recoveryServicesVaultsClient recoveryservices.VaultsClient - recoveryServicesProtectedItemsClient backup.ProtectedItemsGroupClient - recoveryServicesProtectionPoliciesClient backup.ProtectionPoliciesClient - - // Resources - managementLocksClient locks.ManagementLocksClient - deploymentsClient resources.DeploymentsGroupClient - providersClient resourcesprofile.ProvidersClient - resourcesClient resources.GroupClient - resourceGroupsClient resources.GroupsGroupClient - subscriptionsClient subscriptions.GroupClient - - // Security Centre - securityCenterPricingClient security.PricingsClient - securityCenterContactsClient security.ContactsClient - securityCenterWorkspaceClient security.WorkspaceSettingsClient - - // Service Fabric - serviceFabricClustersClient servicefabric.ClustersClient - - // Storage - storageServiceClient storage.AccountsClient - storageUsageClient storage.UsagesClient - - // Stream Analytics - streamAnalyticsFunctionsClient streamanalytics.FunctionsClient - streamAnalyticsJobsClient streamanalytics.StreamingJobsClient - streamAnalyticsInputsClient streamanalytics.InputsClient - streamAnalyticsOutputsClient streamanalytics.OutputsClient - streamAnalyticsTransformationsClient streamanalytics.TransformationsClient - - // Traffic Manager - trafficManagerGeographialHierarchiesClient trafficmanager.GeographicHierarchiesClient - trafficManagerProfilesClient trafficmanager.ProfilesClient - trafficManagerEndpointsClient trafficmanager.EndpointsClient - - // Web - appServicePlansClient web.AppServicePlansClient - appServicesClient web.AppsClient -} - -func (c *ArmClient) configureClient(client *autorest.Client, auth autorest.Authorizer) { - setUserAgent(client, c.partnerId) - client.Authorizer = auth - client.RequestInspector = azure.WithCorrelationRequestID(azure.CorrelationRequestID()) - client.Sender = azure.BuildSender() - client.SkipResourceProviderRegistration = c.skipProviderRegistration - client.PollingDuration = 60 * time.Minute -} - -func setUserAgent(client *autorest.Client, partnerID string) { - // TODO: This is the SDK version not the CLI version, once we are on 0.12, should revisit - tfUserAgent := httpclient.UserAgentString() - - pv := version.ProviderVersion - providerUserAgent := fmt.Sprintf("%s terraform-provider-azurerm/%s", tfUserAgent, pv) - client.UserAgent = strings.TrimSpace(fmt.Sprintf("%s %s", client.UserAgent, providerUserAgent)) - - // append the CloudShell version to the user agent if it exists - if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" { - client.UserAgent = fmt.Sprintf("%s %s", client.UserAgent, azureAgent) - } - - if partnerID != "" { - client.UserAgent = fmt.Sprintf("%s pid-%s", client.UserAgent, partnerID) - } - - log.Printf("[DEBUG] AzureRM Client User Agent: %s\n", client.UserAgent) + resource *resource.Client + Scheduler *scheduler.Client + Search *search.Client + SecurityCenter *securitycenter.Client + ServiceBus *servicebus.Client + ServiceFabric *servicefabric.Client + SignalR *signalr.Client + Storage *storage.Client + StreamAnalytics *streamanalytics.Client + Subscription *subscription.Client + Sql *sql.Client + TrafficManager *trafficmanager.Client + web *web.Client } // getArmClient is a helper method which returns a fully instantiated // *ArmClient based on the Config's current settings. -func getArmClient(c *authentication.Config, skipProviderRegistration bool, partnerId string) (*ArmClient, error) { - env, err := authentication.DetermineEnvironment(c.Environment) +func getArmClient(authConfig *authentication.Config, skipProviderRegistration bool, tfVersion, partnerId string, disableCorrelationRequestID bool) (*ArmClient, error) { + env, err := authentication.DetermineEnvironment(authConfig.Environment) if err != nil { return nil, err } // client declarations: client := ArmClient{ - clientId: c.ClientID, - tenantId: c.TenantID, - subscriptionId: c.SubscriptionID, + Client: clients.Client{}, + + clientId: authConfig.ClientID, + tenantId: authConfig.TenantID, + subscriptionId: authConfig.SubscriptionID, partnerId: partnerId, environment: *env, - usingServicePrincipal: c.AuthenticatedAsAServicePrincipal, + usingServicePrincipal: authConfig.AuthenticatedAsAServicePrincipal, + getAuthenticatedObjectID: authConfig.GetAuthenticatedObjectID, skipProviderRegistration: skipProviderRegistration, } - oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, c.TenantID) + oauthConfig, err := authConfig.BuildOAuthConfig(env.ActiveDirectoryEndpoint) if err != nil { return nil, err } // OAuthConfigForTenant returns a pointer, which can be nil. if oauthConfig == nil { - return nil, fmt.Errorf("Unable to configure OAuthConfig for tenant %s", c.TenantID) + return nil, fmt.Errorf("Unable to configure OAuthConfig for tenant %s", authConfig.TenantID) } - sender := azure.BuildSender() + sender := sender.BuildSender("AzureRM") // Resource Manager endpoints endpoint := env.ResourceManagerEndpoint - auth, err := c.GetAuthorizationToken(sender, oauthConfig, env.TokenAudience) + auth, err := authConfig.GetAuthorizationToken(sender, oauthConfig, env.TokenAudience) if err != nil { return nil, err } // Graph Endpoints graphEndpoint := env.GraphEndpoint - graphAuth, err := c.GetAuthorizationToken(sender, oauthConfig, graphEndpoint) + graphAuth, err := authConfig.GetAuthorizationToken(sender, oauthConfig, graphEndpoint) if err != nil { return nil, err } - // Key Vault Endpoints - keyVaultAuth := autorest.NewBearerAuthorizerCallback(sender, func(tenantID, resource string) (*autorest.BearerAuthorizer, error) { - keyVaultSpt, err := c.GetAuthorizationToken(sender, oauthConfig, resource) - if err != nil { - return nil, err - } - - return keyVaultSpt, nil - }) - - client.registerAPIManagementClients(endpoint, c.SubscriptionID, auth) - client.registerAppInsightsClients(endpoint, c.SubscriptionID, auth) - client.registerAutomationClients(endpoint, c.SubscriptionID, auth) - client.registerAuthentication(endpoint, graphEndpoint, c.SubscriptionID, c.TenantID, auth, graphAuth) - client.registerBatchClients(endpoint, c.SubscriptionID, auth) - client.registerCDNClients(endpoint, c.SubscriptionID, auth) - client.registerCognitiveServiceClients(endpoint, c.SubscriptionID, auth) - client.registerComputeClients(endpoint, c.SubscriptionID, auth) - client.registerContainerClients(endpoint, c.SubscriptionID, auth) - client.registerCosmosAccountsClients(endpoint, c.SubscriptionID, auth) - client.registerDatabricksClients(endpoint, c.SubscriptionID, auth) - client.registerDatabases(endpoint, c.SubscriptionID, auth, sender) - client.registerDataFactoryClients(endpoint, c.SubscriptionID, auth) - client.registerDataLakeStoreClients(endpoint, c.SubscriptionID, auth) - client.registerDevSpaceClients(endpoint, c.SubscriptionID, auth) - client.registerDevTestClients(endpoint, c.SubscriptionID, auth) - client.registerDNSClients(endpoint, c.SubscriptionID, auth) - client.registerEventGridClients(endpoint, c.SubscriptionID, auth) - client.registerEventHubClients(endpoint, c.SubscriptionID, auth) - client.registerHDInsightsClients(endpoint, c.SubscriptionID, auth) - client.registerIoTHubClients(endpoint, c.SubscriptionID, auth) - client.registerKeyVaultClients(endpoint, c.SubscriptionID, auth, keyVaultAuth) - client.registerLogicClients(endpoint, c.SubscriptionID, auth) - client.registerMediaServiceClients(endpoint, c.SubscriptionID, auth) - client.registerMonitorClients(endpoint, c.SubscriptionID, auth) - client.registerMSIClient(endpoint, c.SubscriptionID, auth) - client.registerNetworkingClients(endpoint, c.SubscriptionID, auth) - client.registerNotificationHubsClient(endpoint, c.SubscriptionID, auth) - client.registerOperationalInsightsClients(endpoint, c.SubscriptionID, auth) - client.registerRecoveryServiceClients(endpoint, c.SubscriptionID, auth) - client.registerPolicyClients(endpoint, c.SubscriptionID, auth) - client.registerManagementGroupClients(endpoint, auth) - client.registerRedisClients(endpoint, c.SubscriptionID, auth) - client.registerRelayClients(endpoint, c.SubscriptionID, auth) - client.registerResourcesClients(endpoint, c.SubscriptionID, auth) - client.registerSearchClients(endpoint, c.SubscriptionID, auth) - client.registerSecurityCenterClients(endpoint, c.SubscriptionID, auth) - client.registerServiceBusClients(endpoint, c.SubscriptionID, auth) - client.registerServiceFabricClients(endpoint, c.SubscriptionID, auth) - client.registerSchedulerClients(endpoint, c.SubscriptionID, auth) - client.registerSignalRClients(endpoint, c.SubscriptionID, auth) - client.registerStorageClients(endpoint, c.SubscriptionID, auth) - client.registerStreamAnalyticsClients(endpoint, c.SubscriptionID, auth) - client.registerTrafficManagerClients(endpoint, c.SubscriptionID, auth) - client.registerWebClients(endpoint, c.SubscriptionID, auth) - - return &client, nil -} - -func (c *ArmClient) registerAPIManagementClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - apisClient := apimanagement.NewAPIClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apisClient.Client, auth) - - apiPoliciesClient := apimanagement.NewAPIPolicyClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apiPoliciesClient.Client, auth) - - apiOperationsClient := apimanagement.NewAPIOperationClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apiOperationsClient.Client, auth) - - apiOperationPoliciesClient := apimanagement.NewAPIOperationPolicyClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apiOperationPoliciesClient.Client, auth) - - apiSchemasClient := apimanagement.NewAPISchemaClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apiSchemasClient.Client, auth) - - apiVersionSetClient := apimanagement.NewAPIVersionSetClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&apiVersionSetClient.Client, auth) - - authorizationServersClient := apimanagement.NewAuthorizationServerClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&authorizationServersClient.Client, auth) - - certificatesClient := apimanagement.NewCertificateClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&certificatesClient.Client, auth) - - groupsClient := apimanagement.NewGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&groupsClient.Client, auth) - - groupUsersClient := apimanagement.NewGroupUserClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&groupUsersClient.Client, auth) - - loggerClient := apimanagement.NewLoggerClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&loggerClient.Client, auth) - - policyClient := apimanagement.NewPolicyClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&policyClient.Client, auth) - - serviceClient := apimanagement.NewServiceClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&serviceClient.Client, auth) - - signInClient := apimanagement.NewSignInSettingsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&signInClient.Client, auth) - - signUpClient := apimanagement.NewSignUpSettingsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&signUpClient.Client, auth) - - openIdConnectClient := apimanagement.NewOpenIDConnectProviderClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&openIdConnectClient.Client, auth) - - productsClient := apimanagement.NewProductClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&productsClient.Client, auth) - - productApisClient := apimanagement.NewProductAPIClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&productApisClient.Client, auth) - - productGroupsClient := apimanagement.NewProductGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&productGroupsClient.Client, auth) - - productPoliciesClient := apimanagement.NewProductPolicyClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&productPoliciesClient.Client, auth) - - propertiesClient := apimanagement.NewPropertyClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&propertiesClient.Client, auth) - - subscriptionsClient := apimanagement.NewSubscriptionClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&subscriptionsClient.Client, auth) - - usersClient := apimanagement.NewUserClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&usersClient.Client, auth) - - c.apimgmt = &apimgmt.Client{ - ApiClient: apisClient, - ApiPoliciesClient: apiPoliciesClient, - ApiOperationsClient: apiOperationsClient, - ApiOperationPoliciesClient: apiOperationPoliciesClient, - ApiSchemasClient: apiSchemasClient, - ApiVersionSetClient: apiVersionSetClient, - AuthorizationServersClient: authorizationServersClient, - CertificatesClient: certificatesClient, - GroupClient: groupsClient, - GroupUsersClient: groupUsersClient, - LoggerClient: loggerClient, - PolicyClient: policyClient, - ServiceClient: serviceClient, - SignInClient: signInClient, - SignUpClient: signUpClient, - OpenIdConnectClient: openIdConnectClient, - ProductsClient: productsClient, - ProductApisClient: productApisClient, - ProductGroupsClient: productGroupsClient, - ProductPoliciesClient: productPoliciesClient, - PropertyClient: propertiesClient, - SubscriptionsClient: subscriptionsClient, - UsersClient: usersClient, - } -} - -func (c *ArmClient) registerAppInsightsClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - ai := appinsights.NewComponentsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&ai.Client, auth) - c.appInsightsClient = ai - - aiak := appinsights.NewAPIKeysClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&aiak.Client, auth) - c.appInsightsAPIKeyClient = aiak - - aiwt := appinsights.NewWebTestsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&aiwt.Client, auth) - c.appInsightsWebTestsClient = aiwt -} - -func (c *ArmClient) registerAutomationClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - accountClient := automationSvc.NewAccountClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&accountClient.Client, auth) - - agentRegistrationInfoClient := automationSvc.NewAgentRegistrationInformationClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&agentRegistrationInfoClient.Client, auth) - - credentialClient := automationSvc.NewCredentialClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&credentialClient.Client, auth) - - dscConfigurationClient := automationSvc.NewDscConfigurationClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&dscConfigurationClient.Client, auth) - - dscNodeConfigurationClient := automationSvc.NewDscNodeConfigurationClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&dscNodeConfigurationClient.Client, auth) - - moduleClient := automationSvc.NewModuleClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&moduleClient.Client, auth) - - runbookClient := automationSvc.NewRunbookClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&runbookClient.Client, auth) - - scheduleClient := automationSvc.NewScheduleClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&scheduleClient.Client, auth) - - runbookDraftClient := automationSvc.NewRunbookDraftClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&runbookDraftClient.Client, auth) - - variableClient := automationSvc.NewVariableClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&variableClient.Client, auth) - - c.automation = &automation.Client{ - AccountClient: accountClient, - AgentRegistrationInfoClient: agentRegistrationInfoClient, - CredentialClient: credentialClient, - DscConfigurationClient: dscConfigurationClient, - DscNodeConfigurationClient: dscNodeConfigurationClient, - ModuleClient: moduleClient, - RunbookClient: runbookClient, - RunbookDraftClient: runbookDraftClient, - ScheduleClient: scheduleClient, - VariableClient: variableClient, - } -} - -func (c *ArmClient) registerAuthentication(endpoint, graphEndpoint, subscriptionId, tenantId string, auth, graphAuth autorest.Authorizer) { - assignmentsClient := authorization.NewRoleAssignmentsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&assignmentsClient.Client, auth) - c.roleAssignmentsClient = assignmentsClient - - definitionsClient := authorization.NewRoleDefinitionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&definitionsClient.Client, auth) - c.roleDefinitionsClient = definitionsClient - - applicationsClient := graphrbac.NewApplicationsClientWithBaseURI(graphEndpoint, tenantId) - c.configureClient(&applicationsClient.Client, graphAuth) - c.applicationsClient = applicationsClient - - servicePrincipalsClient := graphrbac.NewServicePrincipalsClientWithBaseURI(graphEndpoint, tenantId) - c.configureClient(&servicePrincipalsClient.Client, graphAuth) - c.servicePrincipalsClient = servicePrincipalsClient -} - -func (c *ArmClient) registerBatchClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - batchAccount := batch.NewAccountClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&batchAccount.Client, auth) - c.batchAccountClient = batchAccount - - batchCertificateClient := batch.NewCertificateClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&batchCertificateClient.Client, auth) - c.batchCertificateClient = batchCertificateClient - - batchPool := batch.NewPoolClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&batchPool.Client, auth) - c.batchPoolClient = batchPool -} - -func (c *ArmClient) registerCDNClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - customDomainsClient := cdnSvc.NewCustomDomainsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&customDomainsClient.Client, auth) - - endpointsClient := cdnSvc.NewEndpointsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&endpointsClient.Client, auth) - - profilesClient := cdnSvc.NewProfilesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&profilesClient.Client, auth) - - c.cdn = &cdn.Client{ - CustomDomainsClient: customDomainsClient, - EndpointsClient: endpointsClient, - ProfilesClient: profilesClient, - } -} - -func (c *ArmClient) registerCognitiveServiceClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - accountsClient := cognitiveSvc.NewAccountsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&accountsClient.Client, auth) - - c.cognitive = &cognitive.Client{ - AccountsClient: accountsClient, - } -} - -func (c *ArmClient) registerCosmosAccountsClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - ca := documentdb.NewDatabaseAccountsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&ca.Client, auth) - c.cosmosAccountsClient = ca -} - -func (c *ArmClient) registerMediaServiceClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - mediaServicesClient := mediaSvc.NewMediaservicesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mediaServicesClient.Client, auth) - - c.media = &media.Client{ - ServicesClient: mediaServicesClient, - } -} - -func (c *ArmClient) registerComputeClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - availabilitySetsClient := compute.NewAvailabilitySetsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&availabilitySetsClient.Client, auth) - c.availSetClient = availabilitySetsClient - - diskClient := compute.NewDisksClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&diskClient.Client, auth) - c.diskClient = diskClient - - imagesClient := compute.NewImagesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&imagesClient.Client, auth) - c.imageClient = imagesClient - - snapshotsClient := compute.NewSnapshotsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&snapshotsClient.Client, auth) - c.snapshotsClient = snapshotsClient - - usageClient := compute.NewUsageClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&usageClient.Client, auth) - c.usageOpsClient = usageClient - - extensionImagesClient := compute.NewVirtualMachineExtensionImagesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&extensionImagesClient.Client, auth) - c.vmExtensionImageClient = extensionImagesClient - - extensionsClient := compute.NewVirtualMachineExtensionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&extensionsClient.Client, auth) - c.vmExtensionClient = extensionsClient - - virtualMachineImagesClient := compute.NewVirtualMachineImagesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&virtualMachineImagesClient.Client, auth) - c.vmImageClient = virtualMachineImagesClient - - scaleSetsClient := compute.NewVirtualMachineScaleSetsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&scaleSetsClient.Client, auth) - c.vmScaleSetClient = scaleSetsClient - - virtualMachinesClient := compute.NewVirtualMachinesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&virtualMachinesClient.Client, auth) - c.vmClient = virtualMachinesClient - - galleriesClient := compute.NewGalleriesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&galleriesClient.Client, auth) - c.galleriesClient = galleriesClient - - galleryImagesClient := compute.NewGalleryImagesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&galleryImagesClient.Client, auth) - c.galleryImagesClient = galleryImagesClient - - galleryImageVersionsClient := compute.NewGalleryImageVersionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&galleryImageVersionsClient.Client, auth) - c.galleryImageVersionsClient = galleryImageVersionsClient -} - -func (c *ArmClient) registerContainerClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - registriesClient := containerregistry.NewRegistriesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(®istriesClient.Client, auth) - - replicationsClient := containerregistry.NewReplicationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&replicationsClient.Client, auth) - - groupsClient := containerinstance.NewContainerGroupsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&groupsClient.Client, auth) - - // ACS - containerServicesClient := containerservice.NewContainerServicesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&containerServicesClient.Client, auth) - - // AKS - kubernetesClustersClient := containerservice.NewManagedClustersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&kubernetesClustersClient.Client, auth) - - c.containers = &containers.Client{ - KubernetesClustersClient: kubernetesClustersClient, - GroupsClient: groupsClient, - RegistryClient: registriesClient, - RegistryReplicationsClient: replicationsClient, - ServicesClient: containerServicesClient, - } -} - -func (c *ArmClient) registerDatabricksClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - workspacesClient := databricksSvc.NewWorkspacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&workspacesClient.Client, auth) - - c.databricks = &databricks.Client{ - WorkspacesClient: workspacesClient, - } -} - -func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) { - mariadbDBClient := mariadb.NewDatabasesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mariadbDBClient.Client, auth) - c.mariadbDatabasesClient = mariadbDBClient - - mariadbServersClient := mariadb.NewServersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mariadbServersClient.Client, auth) - c.mariadbServersClient = mariadbServersClient - - // MySQL - mysqlConfigClient := mysql.NewConfigurationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mysqlConfigClient.Client, auth) - c.mysqlConfigurationsClient = mysqlConfigClient - - mysqlDBClient := mysql.NewDatabasesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mysqlDBClient.Client, auth) - c.mysqlDatabasesClient = mysqlDBClient - - mysqlFWClient := mysql.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mysqlFWClient.Client, auth) - c.mysqlFirewallRulesClient = mysqlFWClient - - mysqlServersClient := mysql.NewServersClientWithBaseURI(endpoint, subscriptionId) - 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) - c.postgresqlConfigurationsClient = postgresqlConfigClient - - postgresqlDBClient := postgresql.NewDatabasesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&postgresqlDBClient.Client, auth) - c.postgresqlDatabasesClient = postgresqlDBClient - - postgresqlFWClient := postgresql.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&postgresqlFWClient.Client, auth) - c.postgresqlFirewallRulesClient = postgresqlFWClient - - postgresqlSrvClient := postgresql.NewServersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&postgresqlSrvClient.Client, auth) - c.postgresqlServersClient = postgresqlSrvClient - - postgresqlVNRClient := postgresql.NewVirtualNetworkRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&postgresqlVNRClient.Client, auth) - c.postgresqlVirtualNetworkRulesClient = postgresqlVNRClient - - // SQL Azure - sqlDBClient := sql.NewDatabasesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlDBClient.Client, auth) - c.sqlDatabasesClient = sqlDBClient - - sqlDTDPClient := sql.NewDatabaseThreatDetectionPoliciesClientWithBaseURI(endpoint, subscriptionId) - setUserAgent(&sqlDTDPClient.Client, "") - sqlDTDPClient.Authorizer = auth - sqlDTDPClient.Sender = sender - sqlDTDPClient.SkipResourceProviderRegistration = c.skipProviderRegistration - c.sqlDatabaseThreatDetectionPoliciesClient = sqlDTDPClient - - sqlFWClient := sql.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlFWClient.Client, auth) - c.sqlFirewallRulesClient = sqlFWClient - - sqlEPClient := sql.NewElasticPoolsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlEPClient.Client, auth) - c.sqlElasticPoolsClient = sqlEPClient - - MsSqlEPClient := MsSql.NewElasticPoolsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&MsSqlEPClient.Client, auth) - c.msSqlElasticPoolsClient = MsSqlEPClient - - sqlSrvClient := sql.NewServersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlSrvClient.Client, auth) - c.sqlServersClient = sqlSrvClient - - sqlADClient := sql.NewServerAzureADAdministratorsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlADClient.Client, auth) - c.sqlServerAzureADAdministratorsClient = sqlADClient - - sqlVNRClient := sql.NewVirtualNetworkRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&sqlVNRClient.Client, auth) - c.sqlVirtualNetworkRulesClient = sqlVNRClient -} - -func (c *ArmClient) registerDataFactoryClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - factoriesClient := datafactorySvc.NewFactoriesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&factoriesClient.Client, auth) - - datasetsClient := datafactorySvc.NewDatasetsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&datasetsClient.Client, auth) - - linkedServicesClient := datafactorySvc.NewLinkedServicesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&linkedServicesClient.Client, auth) - - pipelinesClient := datafactorySvc.NewPipelinesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&pipelinesClient.Client, auth) - - c.dataFactory = &datafactory.Client{ - FactoriesClient: factoriesClient, - DatasetClient: datasetsClient, - LinkedServiceClient: linkedServicesClient, - PipelinesClient: pipelinesClient, - } -} - -func (c *ArmClient) registerDataLakeStoreClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - storeAccountClient := storeAccount.NewAccountsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&storeAccountClient.Client, auth) - c.dataLakeStoreAccountClient = storeAccountClient - - storeFirewallRulesClient := storeAccount.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&storeFirewallRulesClient.Client, auth) - c.dataLakeStoreFirewallRulesClient = storeFirewallRulesClient - - analyticsAccountClient := analyticsAccount.NewAccountsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&analyticsAccountClient.Client, auth) - c.dataLakeAnalyticsAccountClient = analyticsAccountClient - - filesClient := filesystem.NewClient() - c.configureClient(&filesClient.Client, auth) - c.dataLakeStoreFilesClient = filesClient - - analyticsFirewallRulesClient := analyticsAccount.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&analyticsFirewallRulesClient.Client, auth) - c.dataLakeAnalyticsFirewallRulesClient = analyticsFirewallRulesClient -} - -func (c *ArmClient) registerDevTestClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - labsClient := dtl.NewLabsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&labsClient.Client, auth) - c.devTestLabsClient = labsClient - - devTestPoliciesClient := dtl.NewPoliciesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&devTestPoliciesClient.Client, auth) - c.devTestPoliciesClient = devTestPoliciesClient - - devTestVirtualMachinesClient := dtl.NewVirtualMachinesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&devTestVirtualMachinesClient.Client, auth) - c.devTestVirtualMachinesClient = devTestVirtualMachinesClient - - devTestVirtualNetworksClient := dtl.NewVirtualNetworksClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&devTestVirtualNetworksClient.Client, auth) - c.devTestVirtualNetworksClient = devTestVirtualNetworksClient -} - -func (c *ArmClient) registerDevSpaceClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - controllersClient := devspaces.NewControllersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&controllersClient.Client, auth) - c.devSpace = &devspace.Client{ - ControllersClient: controllersClient, - } -} - -func (c *ArmClient) registerDNSClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - recordSetsClient := dnsSvc.NewRecordSetsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&recordSetsClient.Client, auth) - - zonesClient := dnsSvc.NewZonesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&zonesClient.Client, auth) - - c.dns = &dns.Client{ - RecordSetsClient: recordSetsClient, - ZonesClient: zonesClient, - } -} - -func (c *ArmClient) registerEventGridClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - domainsClient := eventGridSvc.NewDomainsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&domainsClient.Client, auth) - - eventSubscriptionsClient := eventGridSvc.NewEventSubscriptionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&eventSubscriptionsClient.Client, auth) - - topicsClient := eventGridSvc.NewTopicsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&topicsClient.Client, auth) - - c.eventGrid = &eventgrid.Client{ - DomainsClient: domainsClient, - EventSubscriptionsClient: eventSubscriptionsClient, - TopicsClient: topicsClient, - } -} - -func (c *ArmClient) registerEventHubClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - eventHubsClient := eventHubSvc.NewEventHubsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&eventHubsClient.Client, auth) - - groupsClient := eventHubSvc.NewConsumerGroupsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&groupsClient.Client, auth) - - namespacesClient := eventHubSvc.NewNamespacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&namespacesClient.Client, auth) - - c.eventhub = &eventhub.Client{ - ConsumerGroupClient: groupsClient, - EventHubsClient: eventHubsClient, - NamespacesClient: namespacesClient, - } -} - -func (c *ArmClient) registerHDInsightsClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - applicationsClient := hdinsightSvc.NewApplicationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&applicationsClient.Client, auth) - - clustersClient := hdinsightSvc.NewClustersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&clustersClient.Client, auth) - - configurationsClient := hdinsightSvc.NewConfigurationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&configurationsClient.Client, auth) - - c.hdinsight = &hdinsight.Client{ - ApplicationsClient: applicationsClient, - ClustersClient: clustersClient, - ConfigurationsClient: configurationsClient, - } -} -func (c *ArmClient) registerIoTHubClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - iotClient := iotHubSvc.NewIotHubResourceClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&iotClient.Client, auth) - - iotDpsClient := iotdps.NewIotDpsResourceClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&iotDpsClient.Client, auth) - - c.iothub = &iothub.Client{ - ResourceClient: iotClient, - DPSResourceClient: iotDpsClient, - } -} - -func (c *ArmClient) registerKeyVaultClients(endpoint, subscriptionId string, auth autorest.Authorizer, keyVaultAuth autorest.Authorizer) { - keyVaultClient := keyvault.NewVaultsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&keyVaultClient.Client, auth) - c.keyVaultClient = keyVaultClient - - keyVaultManagementClient := keyVault.New() - c.configureClient(&keyVaultManagementClient.Client, keyVaultAuth) - c.keyVaultManagementClient = keyVaultManagementClient -} - -func (c *ArmClient) registerLogicClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - workflowsClient := logic.NewWorkflowsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&workflowsClient.Client, auth) - c.logicWorkflowsClient = workflowsClient -} - -func (c *ArmClient) registerMonitorClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - agc := insights.NewActionGroupsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&agc.Client, auth) - c.monitorActionGroupsClient = agc - - alac := insights.NewActivityLogAlertsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&alac.Client, auth) - c.monitorActivityLogAlertsClient = alac - - arc := insights.NewAlertRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&arc.Client, auth) - c.monitorAlertRulesClient = arc - - monitorLogProfilesClient := insights.NewLogProfilesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&monitorLogProfilesClient.Client, auth) - c.monitorLogProfilesClient = monitorLogProfilesClient - - mac := insights.NewMetricAlertsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&mac.Client, auth) - c.monitorMetricAlertsClient = mac - - autoscaleSettingsClient := insights.NewAutoscaleSettingsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&autoscaleSettingsClient.Client, auth) - c.autoscaleSettingsClient = autoscaleSettingsClient - - monitoringInsightsClient := insights.NewDiagnosticSettingsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&monitoringInsightsClient.Client, auth) - c.monitorDiagnosticSettingsClient = monitoringInsightsClient - - monitoringCategorySettingsClient := insights.NewDiagnosticSettingsCategoryClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&monitoringCategorySettingsClient.Client, auth) - c.monitorDiagnosticSettingsCategoryClient = monitoringCategorySettingsClient -} - -func (c *ArmClient) registerMSIClient(endpoint, subscriptionId string, auth autorest.Authorizer) { - userAssignedIdentitiesClient := msiSvc.NewUserAssignedIdentitiesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&userAssignedIdentitiesClient.Client, auth) - - c.msi = &msi.Client{ - UserAssignedIdentitiesClient: userAssignedIdentitiesClient, - } -} - -func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - applicationGatewaysClient := network.NewApplicationGatewaysClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&applicationGatewaysClient.Client, auth) - c.applicationGatewayClient = applicationGatewaysClient - - appSecurityGroupsClient := network.NewApplicationSecurityGroupsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&appSecurityGroupsClient.Client, auth) - c.applicationSecurityGroupsClient = appSecurityGroupsClient - - azureFirewallsClient := network.NewAzureFirewallsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&azureFirewallsClient.Client, auth) - c.azureFirewallsClient = azureFirewallsClient - - connectionMonitorsClient := network.NewConnectionMonitorsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&connectionMonitorsClient.Client, auth) - c.connectionMonitorsClient = connectionMonitorsClient - - ddosProtectionPlanClient := network.NewDdosProtectionPlansClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&ddosProtectionPlanClient.Client, auth) - c.ddosProtectionPlanClient = ddosProtectionPlanClient - - expressRouteAuthsClient := network.NewExpressRouteCircuitAuthorizationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&expressRouteAuthsClient.Client, auth) - c.expressRouteAuthsClient = expressRouteAuthsClient - - expressRouteCircuitsClient := network.NewExpressRouteCircuitsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&expressRouteCircuitsClient.Client, auth) - c.expressRouteCircuitClient = expressRouteCircuitsClient - - expressRoutePeeringsClient := network.NewExpressRouteCircuitPeeringsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&expressRoutePeeringsClient.Client, auth) - c.expressRoutePeeringsClient = expressRoutePeeringsClient - - interfacesClient := network.NewInterfacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&interfacesClient.Client, auth) - c.ifaceClient = interfacesClient - - loadBalancersClient := network.NewLoadBalancersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&loadBalancersClient.Client, auth) - c.loadBalancerClient = loadBalancersClient - - localNetworkGatewaysClient := network.NewLocalNetworkGatewaysClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&localNetworkGatewaysClient.Client, auth) - c.localNetConnClient = localNetworkGatewaysClient - - gatewaysClient := network.NewVirtualNetworkGatewaysClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&gatewaysClient.Client, auth) - c.vnetGatewayClient = gatewaysClient - - gatewayConnectionsClient := network.NewVirtualNetworkGatewayConnectionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&gatewayConnectionsClient.Client, auth) - c.vnetGatewayConnectionsClient = gatewayConnectionsClient - - netProfileClient := network.NewProfilesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&netProfileClient.Client, auth) - c.netProfileClient = netProfileClient - - networksClient := network.NewVirtualNetworksClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&networksClient.Client, auth) - c.vnetClient = networksClient - - packetCapturesClient := network.NewPacketCapturesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&packetCapturesClient.Client, auth) - c.packetCapturesClient = packetCapturesClient - - peeringsClient := network.NewVirtualNetworkPeeringsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&peeringsClient.Client, auth) - c.vnetPeeringsClient = peeringsClient - - publicIPAddressesClient := network.NewPublicIPAddressesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&publicIPAddressesClient.Client, auth) - c.publicIPClient = publicIPAddressesClient - - publicIPPrefixesClient := network.NewPublicIPPrefixesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&publicIPPrefixesClient.Client, auth) - c.publicIPPrefixClient = publicIPPrefixesClient - - routesClient := network.NewRoutesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&routesClient.Client, auth) - c.routesClient = routesClient - - routeTablesClient := network.NewRouteTablesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&routeTablesClient.Client, auth) - c.routeTablesClient = routeTablesClient - - securityGroupsClient := network.NewSecurityGroupsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&securityGroupsClient.Client, auth) - c.secGroupClient = securityGroupsClient - - securityRulesClient := network.NewSecurityRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&securityRulesClient.Client, auth) - c.secRuleClient = securityRulesClient - - subnetsClient := network.NewSubnetsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&subnetsClient.Client, auth) - c.subnetClient = subnetsClient - - watchersClient := network.NewWatchersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&watchersClient.Client, auth) - c.watcherClient = watchersClient -} - -func (c *ArmClient) registerNotificationHubsClient(endpoint, subscriptionId string, auth autorest.Authorizer) { - namespacesClient := notificationHubsSvc.NewNamespacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&namespacesClient.Client, auth) - - notificationHubsClient := notificationHubsSvc.NewClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(¬ificationHubsClient.Client, auth) - - c.notificationHubs = ¬ificationhub.Client{ - HubsClient: notificationHubsClient, - NamespacesClient: namespacesClient, - } -} - -func (c *ArmClient) registerOperationalInsightsClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - workspacesClient := operationalinsights.NewWorkspacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&workspacesClient.Client, auth) - - solutionsClient := operationsmanagement.NewSolutionsClientWithBaseURI(endpoint, subscriptionId, "Microsoft.OperationsManagement", "solutions", "testing") - c.configureClient(&solutionsClient.Client, auth) - - linkedServicesClient := operationalinsights.NewLinkedServicesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&linkedServicesClient.Client, auth) - - c.logAnalytics = &loganalytics.Client{ - LinkedServicesClient: linkedServicesClient, - SolutionsClient: solutionsClient, - WorkspacesClient: workspacesClient, - } -} - -func (c *ArmClient) registerRecoveryServiceClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - vaultsClient := recoveryservices.NewVaultsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&vaultsClient.Client, auth) - c.recoveryServicesVaultsClient = vaultsClient - - protectedItemsClient := backup.NewProtectedItemsGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&protectedItemsClient.Client, auth) - c.recoveryServicesProtectedItemsClient = protectedItemsClient - - protectionPoliciesClient := backup.NewProtectionPoliciesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&protectionPoliciesClient.Client, auth) - c.recoveryServicesProtectionPoliciesClient = protectionPoliciesClient -} - -func (c *ArmClient) registerRedisClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - cacheClient := redisSvc.NewClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&cacheClient.Client, auth) - - firewallRuleClient := redisSvc.NewFirewallRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&firewallRuleClient.Client, auth) - - patchSchedulesClient := redisSvc.NewPatchSchedulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&patchSchedulesClient.Client, auth) - - c.redis = &redis.Client{ - Client: cacheClient, - FirewallRulesClient: firewallRuleClient, - PatchSchedulesClient: patchSchedulesClient, - } -} - -func (c *ArmClient) registerRelayClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - namespacesClient := relaySvc.NewNamespacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&namespacesClient.Client, auth) - - c.relay = &relay.Client{ - NamespacesClient: namespacesClient, - } -} - -func (c *ArmClient) registerResourcesClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - locksClient := locks.NewManagementLocksClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&locksClient.Client, auth) - c.managementLocksClient = locksClient - - deploymentsClient := resources.NewDeploymentsGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&deploymentsClient.Client, auth) - c.deploymentsClient = deploymentsClient - - resourcesClient := resources.NewGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&resourcesClient.Client, auth) - c.resourcesClient = resourcesClient - - resourceGroupsClient := resources.NewGroupsGroupClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&resourceGroupsClient.Client, auth) - c.resourceGroupsClient = resourceGroupsClient - - subscriptionsClient := subscriptions.NewGroupClientWithBaseURI(endpoint) - c.configureClient(&subscriptionsClient.Client, auth) - c.subscriptionsClient = subscriptionsClient - - // this has to come from the Profile since this is shared with Stack - providersClient := resourcesprofile.NewProvidersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&providersClient.Client, auth) - c.providersClient = providersClient -} - -func (c *ArmClient) registerSchedulerClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - jobCollectionsClient := schedulerSvc.NewJobCollectionsClientWithBaseURI(endpoint, subscriptionId) //nolint: megacheck - c.configureClient(&jobCollectionsClient.Client, auth) - - jobsClient := schedulerSvc.NewJobsClientWithBaseURI(endpoint, subscriptionId) //nolint: megacheck - c.configureClient(&jobsClient.Client, auth) - - c.scheduler = &scheduler.Client{ - JobCollectionsClient: jobCollectionsClient, - JobsClient: jobsClient, - } -} - -func (c *ArmClient) registerSearchClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - searchAdminKeysClient := searchSvc.NewAdminKeysClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&searchAdminKeysClient.Client, auth) - - servicesClient := searchSvc.NewServicesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&servicesClient.Client, auth) - - c.search = &search.Client{ - AdminKeysClient: searchAdminKeysClient, - ServicesClient: servicesClient, - } -} - -func (c *ArmClient) registerSecurityCenterClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - ascLocation := "Global" - - securityCenterPricingClient := security.NewPricingsClientWithBaseURI(endpoint, subscriptionId, ascLocation) - c.configureClient(&securityCenterPricingClient.Client, auth) - c.securityCenterPricingClient = securityCenterPricingClient - - securityCenterContactsClient := security.NewContactsClientWithBaseURI(endpoint, subscriptionId, ascLocation) - c.configureClient(&securityCenterContactsClient.Client, auth) - c.securityCenterContactsClient = securityCenterContactsClient - - securityCenterWorkspaceClient := security.NewWorkspaceSettingsClientWithBaseURI(endpoint, subscriptionId, ascLocation) - c.configureClient(&securityCenterWorkspaceClient.Client, auth) - c.securityCenterWorkspaceClient = securityCenterWorkspaceClient -} - -func (c *ArmClient) registerServiceBusClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - queuesClient := servicebusSvc.NewQueuesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&queuesClient.Client, auth) - - namespacesClient := servicebusSvc.NewNamespacesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&namespacesClient.Client, auth) - - topicsClient := servicebusSvc.NewTopicsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&topicsClient.Client, auth) - - subscriptionsClient := servicebusSvc.NewSubscriptionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&subscriptionsClient.Client, auth) - - subscriptionRulesClient := servicebusSvc.NewRulesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&subscriptionRulesClient.Client, auth) - - c.servicebus = &servicebus.Client{ - QueuesClient: queuesClient, - NamespacesClient: namespacesClient, - TopicsClient: topicsClient, - SubscriptionsClient: subscriptionsClient, - SubscriptionRulesClient: subscriptionRulesClient, - } -} - -func (c *ArmClient) registerServiceFabricClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - clustersClient := servicefabric.NewClustersClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&clustersClient.Client, auth) - c.serviceFabricClustersClient = clustersClient -} - -func (c *ArmClient) registerSignalRClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - client := signalrSvc.NewClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&client.Client, auth) - - c.signalr = &signalr.Client{ - Client: client, - } -} - -func (c *ArmClient) registerStorageClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - accountsClient := storage.NewAccountsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&accountsClient.Client, auth) - c.storageServiceClient = accountsClient - - usageClient := storage.NewUsagesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&usageClient.Client, auth) - c.storageUsageClient = usageClient -} - -func (c *ArmClient) registerStreamAnalyticsClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - functionsClient := streamanalytics.NewFunctionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&functionsClient.Client, auth) - c.streamAnalyticsFunctionsClient = functionsClient - - jobsClient := streamanalytics.NewStreamingJobsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&jobsClient.Client, auth) - c.streamAnalyticsJobsClient = jobsClient - - inputsClient := streamanalytics.NewInputsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&inputsClient.Client, auth) - c.streamAnalyticsInputsClient = inputsClient - - outputsClient := streamanalytics.NewOutputsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&outputsClient.Client, auth) - c.streamAnalyticsOutputsClient = outputsClient - - transformationsClient := streamanalytics.NewTransformationsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&transformationsClient.Client, auth) - c.streamAnalyticsTransformationsClient = transformationsClient -} - -func (c *ArmClient) registerTrafficManagerClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - endpointsClient := trafficmanager.NewEndpointsClientWithBaseURI(endpoint, c.subscriptionId) - c.configureClient(&endpointsClient.Client, auth) - c.trafficManagerEndpointsClient = endpointsClient - - geographicalHierarchiesClient := trafficmanager.NewGeographicHierarchiesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&geographicalHierarchiesClient.Client, auth) - c.trafficManagerGeographialHierarchiesClient = geographicalHierarchiesClient - - profilesClient := trafficmanager.NewProfilesClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&profilesClient.Client, auth) - c.trafficManagerProfilesClient = profilesClient -} - -func (c *ArmClient) registerWebClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - appServicePlansClient := web.NewAppServicePlansClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&appServicePlansClient.Client, auth) - c.appServicePlansClient = appServicePlansClient - - appsClient := web.NewAppsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&appsClient.Client, auth) - c.appServicesClient = appsClient -} - -func (c *ArmClient) registerPolicyClients(endpoint, subscriptionId string, auth autorest.Authorizer) { - assignmentsClient := policySvc.NewAssignmentsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&assignmentsClient.Client, auth) - - definitionsClient := policySvc.NewDefinitionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&definitionsClient.Client, auth) - - setDefinitionsClient := policySvc.NewSetDefinitionsClientWithBaseURI(endpoint, subscriptionId) - c.configureClient(&setDefinitionsClient.Client, auth) - - c.policy = &policy.Client{ - AssignmentsClient: assignmentsClient, - DefinitionsClient: definitionsClient, - SetDefinitionsClient: setDefinitionsClient, - } -} - -func (c *ArmClient) registerManagementGroupClients(endpoint string, auth autorest.Authorizer) { - managementGroupsClient := managementgroups.NewClientWithBaseURI(endpoint) - c.configureClient(&managementGroupsClient.Client, auth) - c.managementGroupsClient = managementGroupsClient - - managementGroupsSubscriptionClient := managementgroups.NewSubscriptionsClientWithBaseURI(endpoint) - c.configureClient(&managementGroupsSubscriptionClient.Client, auth) - c.managementGroupsSubscriptionClient = managementGroupsSubscriptionClient -} - -var ( - storageKeyCacheMu sync.RWMutex - storageKeyCache = make(map[string]string) -) - -func (c *ArmClient) getKeyForStorageAccount(ctx context.Context, resourceGroupName, storageAccountName string) (string, bool, error) { - cacheIndex := resourceGroupName + "/" + storageAccountName - storageKeyCacheMu.RLock() - key, ok := storageKeyCache[cacheIndex] - storageKeyCacheMu.RUnlock() - - if ok { - return key, true, nil - } - - storageKeyCacheMu.Lock() - defer storageKeyCacheMu.Unlock() - key, ok = storageKeyCache[cacheIndex] - if !ok { - accountKeys, err := c.storageServiceClient.ListKeys(ctx, resourceGroupName, storageAccountName) - if utils.ResponseWasNotFound(accountKeys.Response) { - return "", false, nil - } - if err != nil { - // We assume this is a transient error rather than a 404 (which is caught above), so assume the - // storeAccount still exists. - return "", true, fmt.Errorf("Error retrieving keys for storage storeAccount %q: %s", storageAccountName, err) - } - - if accountKeys.Keys == nil { - return "", false, fmt.Errorf("Nil key returned for storage storeAccount %q", storageAccountName) - } - - keys := *accountKeys.Keys - if len(keys) <= 0 { - return "", false, fmt.Errorf("No keys returned for storage storeAccount %q", storageAccountName) - } - - keyPtr := keys[0].Value - if keyPtr == nil { - return "", false, fmt.Errorf("The first key returned is nil for storage storeAccount %q", storageAccountName) - } - - key = *keyPtr - storageKeyCache[cacheIndex] = key - } - - return key, true, nil -} - -func (c *ArmClient) getBlobStorageClientForStorageAccount(ctx context.Context, resourceGroupName, storageAccountName string) (*mainStorage.BlobStorageClient, bool, error) { - key, accountExists, err := c.getKeyForStorageAccount(ctx, resourceGroupName, storageAccountName) - if err != nil { - return nil, accountExists, err - } - if !accountExists { - return nil, false, nil - } - - storageClient, err := mainStorage.NewClient(storageAccountName, key, c.environment.StorageEndpointSuffix, - mainStorage.DefaultAPIVersion, true) - if err != nil { - return nil, true, fmt.Errorf("Error creating storage client for storage storeAccount %q: %s", storageAccountName, err) - } - - blobClient := storageClient.GetBlobService() - return &blobClient, true, nil -} - -func (c *ArmClient) getFileServiceClientForStorageAccount(ctx context.Context, resourceGroupName, storageAccountName string) (*mainStorage.FileServiceClient, bool, error) { - key, accountExists, err := c.getKeyForStorageAccount(ctx, resourceGroupName, storageAccountName) + // Storage Endpoints + storageAuth, err := authConfig.GetAuthorizationToken(sender, oauthConfig, env.ResourceIdentifiers.Storage) if err != nil { - return nil, accountExists, err - } - if !accountExists { - return nil, false, nil - } - - storageClient, err := mainStorage.NewClient(storageAccountName, key, c.environment.StorageEndpointSuffix, - mainStorage.DefaultAPIVersion, true) - if err != nil { - return nil, true, fmt.Errorf("Error creating storage client for storage storeAccount %q: %s", storageAccountName, err) - } - - fileClient := storageClient.GetFileService() - return &fileClient, true, nil -} - -func (c *ArmClient) getTableServiceClientForStorageAccount(ctx context.Context, resourceGroupName, storageAccountName string) (*mainStorage.TableServiceClient, bool, error) { - key, accountExists, err := c.getKeyForStorageAccount(ctx, resourceGroupName, storageAccountName) - if err != nil { - return nil, accountExists, err - } - if !accountExists { - return nil, false, nil - } - - storageClient, err := mainStorage.NewClient(storageAccountName, key, c.environment.StorageEndpointSuffix, - mainStorage.DefaultAPIVersion, true) - if err != nil { - return nil, true, fmt.Errorf("Error creating storage client for storage storeAccount %q: %s", storageAccountName, err) - } - - tableClient := storageClient.GetTableService() - return &tableClient, true, nil -} - -func (c *ArmClient) getQueueServiceClientForStorageAccount(ctx context.Context, resourceGroupName, storageAccountName string) (*mainStorage.QueueServiceClient, bool, error) { - key, accountExists, err := c.getKeyForStorageAccount(ctx, resourceGroupName, storageAccountName) - if err != nil { - return nil, accountExists, err - } - if !accountExists { - return nil, false, nil + return nil, err } - storageClient, err := mainStorage.NewClient(storageAccountName, key, c.environment.StorageEndpointSuffix, - mainStorage.DefaultAPIVersion, true) - if err != nil { - return nil, true, fmt.Errorf("Error creating storage client for storage storeAccount %q: %s", storageAccountName, err) - } + // Key Vault Endpoints + keyVaultAuth := authConfig.BearerAuthorizerCallback(sender, oauthConfig) + + o := &common.ClientOptions{ + SubscriptionId: authConfig.SubscriptionID, + TenantID: authConfig.TenantID, + PartnerId: partnerId, + TerraformVersion: tfVersion, + GraphAuthorizer: graphAuth, + GraphEndpoint: graphEndpoint, + KeyVaultAuthorizer: keyVaultAuth, + ResourceManagerAuthorizer: auth, + ResourceManagerEndpoint: endpoint, + StorageAuthorizer: storageAuth, + PollingDuration: 180 * time.Minute, + SkipProviderReg: skipProviderRegistration, + DisableCorrelationRequestID: disableCorrelationRequestID, + Environment: *env, + } + + client.analysisservices = analysisservices.BuildClient(o) + client.apiManagement = apimanagement.BuildClient(o) + client.appInsights = applicationinsights.BuildClient(o) + client.automation = automation.BuildClient(o) + client.authorization = authorization.BuildClient(o) + client.batch = batch.BuildClient(o) + client.bot = bot.BuildClient(o) + client.cdn = cdn.BuildClient(o) + client.cognitive = cognitive.BuildClient(o) + client.compute = clients.NewComputeClient(o) + client.containers = containers.BuildClient(o) + client.cosmos = cosmos.BuildClient(o) + client.databricks = databricks.BuildClient(o) + client.dataFactory = datafactory.BuildClient(o) + client.datalake = datalake.BuildClient(o) + client.devSpace = devspace.BuildClient(o) + client.devTestLabs = devtestlabs.BuildClient(o) + client.dns = dns.BuildClient(o) + client.eventGrid = eventgrid.BuildClient(o) + client.eventhub = eventhub.BuildClient(o) + client.frontdoor = frontdoor.BuildClient(o) + client.graph = graph.BuildClient(o) + client.hdinsight = hdinsight.BuildClient(o) + client.iothub = iothub.BuildClient(o) + client.keyvault = keyvault.BuildClient(o) + client.kusto = kusto.BuildClient(o) + client.logic = logic.BuildClient(o) + client.logAnalytics = loganalytics.BuildClient(o) + client.maps = maps.BuildClient(o) + client.mariadb = mariadb.BuildClient(o) + client.media = media.BuildClient(o) + client.monitor = monitor.BuildClient(o) + client.mssql = mssql.BuildClient(o) + client.msi = msi.BuildClient(o) + client.mysql = mysql.BuildClient(o) + client.managementGroups = managementgroup.BuildClient(o) + client.network = network.BuildClient(o) + client.notificationHubs = notificationhub.BuildClient(o) + client.policy = policy.BuildClient(o) + client.portal = portal.BuildClient(o) + client.postgres = postgres.BuildClient(o) + client.privateDns = privatedns.BuildClient(o) + client.recoveryServices = recoveryservices.BuildClient(o) + client.redis = redis.BuildClient(o) + client.relay = relay.BuildClient(o) + client.resource = resource.BuildClient(o) + client.Search = search.BuildClient(o) + client.SecurityCenter = securitycenter.BuildClient(o) + client.ServiceBus = servicebus.BuildClient(o) + client.ServiceFabric = servicefabric.BuildClient(o) + client.Scheduler = scheduler.BuildClient(o) + client.SignalR = signalr.BuildClient(o) + client.StreamAnalytics = streamanalytics.BuildClient(o) + client.Storage = storage.BuildClient(o) + client.Subscription = subscription.BuildClient(o) + client.Sql = sql.BuildClient(o) + client.TrafficManager = trafficmanager.BuildClient(o) + client.web = web.BuildClient(o) - queueClient := storageClient.GetQueueService() - return &queueClient, true, nil + return &client, nil } diff --git a/azurerm/data_factory.go b/azurerm/data_factory.go index ac2d9c7be8b4..c6bd3b394f04 100644 --- a/azurerm/data_factory.go +++ b/azurerm/data_factory.go @@ -31,7 +31,7 @@ func expandDataFactoryLinkedServiceIntegrationRuntime(integrationRuntimeName str // Because the password isn't returned from the api in the connection string, we'll check all // but the password string and return true if they match. -func azureRmDataFactoryLinkedServiceConnectionStringDiff(k, old string, new string, d *schema.ResourceData) bool { +func azureRmDataFactoryLinkedServiceConnectionStringDiff(_, old string, new string, _ *schema.ResourceData) bool { oldSplit := strings.Split(strings.ToLower(old), ";") newSplit := strings.Split(strings.ToLower(new), ";") diff --git a/azurerm/data_source_api_management.go b/azurerm/data_source_api_management.go index 8b6581201b5d..bfdf152af347 100644 --- a/azurerm/data_source_api_management.go +++ b/azurerm/data_source_api_management.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -39,6 +40,7 @@ func dataSourceApiManagementService() *schema.Resource { Computed: true, }, + // TODO: Remove in 2.0 "sku": { Type: schema.TypeList, Computed: true, @@ -56,6 +58,11 @@ func dataSourceApiManagementService() *schema.Resource { }, }, + "sku_name": { + Type: schema.TypeString, + Computed: true, + }, + "notification_sender_email": { Type: schema.TypeString, Computed: true, @@ -146,13 +153,13 @@ func dataSourceApiManagementService() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceApiManagementRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ServiceClient + client := meta.(*ArmClient).apiManagement.ServiceClient name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) @@ -180,7 +187,6 @@ func dataSourceApiManagementRead(d *schema.ResourceData, meta interface{}) error if props := resp.ServiceProperties; props != nil { d.Set("publisher_email", props.PublisherEmail) d.Set("publisher_name", props.PublisherName) - d.Set("notification_sender_email", props.NotificationSenderEmail) d.Set("gateway_url", props.GatewayURL) d.Set("gateway_regional_url", props.GatewayRegionalURL) @@ -198,13 +204,17 @@ func dataSourceApiManagementRead(d *schema.ResourceData, meta interface{}) error } } - if err := d.Set("sku", flattenDataSourceApiManagementServiceSku(resp.Sku)); err != nil { - return fmt.Errorf("Error setting `sku`: %+v", err) + if sku := resp.Sku; sku != nil { + // TODO: Remove in 2.0 + if err := d.Set("sku", flattenApiManagementServiceSku(resp.Sku)); err != nil { + return fmt.Errorf("Error setting `sku`: %+v", err) + } + if err := d.Set("sku_name", flattenApiManagementServiceSkuName(resp.Sku)); err != nil { + return fmt.Errorf("Error setting `sku_name`: %+v", err) + } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenDataSourceApiManagementHostnameConfigurations(input *[]apimanagement.HostnameConfiguration) []interface{} { @@ -289,22 +299,6 @@ func flattenDataSourceApiManagementAdditionalLocations(input *[]apimanagement.Ad return results } -func flattenDataSourceApiManagementServiceSku(profile *apimanagement.ServiceSkuProperties) []interface{} { - if profile == nil { - return []interface{}{} - } - - sku := make(map[string]interface{}) - - sku["name"] = string(profile.Name) - - if profile.Capacity != nil { - sku["capacity"] = *profile.Capacity - } - - return []interface{}{sku} -} - func apiManagementDataSourceHostnameSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ "host_name": { diff --git a/azurerm/data_source_api_management_api.go b/azurerm/data_source_api_management_api.go index 39f4948daf32..eb8bc8309d05 100644 --- a/azurerm/data_source_api_management_api.go +++ b/azurerm/data_source_api_management_api.go @@ -106,7 +106,7 @@ func dataSourceApiManagementApi() *schema.Resource { func dataSourceApiManagementApiRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).apimgmt.ApiClient + client := meta.(*ArmClient).apiManagement.ApiClient resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) diff --git a/azurerm/data_source_api_management_group.go b/azurerm/data_source_api_management_group.go index cc68a5e06488..d5a7e13f9328 100644 --- a/azurerm/data_source_api_management_group.go +++ b/azurerm/data_source_api_management_group.go @@ -44,7 +44,7 @@ func dataSourceApiManagementGroup() *schema.Resource { } func dataSourceApiManagementGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupClient + client := meta.(*ArmClient).apiManagement.GroupClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) diff --git a/azurerm/data_source_api_management_product.go b/azurerm/data_source_api_management_product.go index a9492fc1d3db..c5cf74872da2 100644 --- a/azurerm/data_source_api_management_product.go +++ b/azurerm/data_source_api_management_product.go @@ -58,7 +58,7 @@ func dataSourceApiManagementProduct() *schema.Resource { } } func dataSourceApiManagementProductRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductsClient + client := meta.(*ArmClient).apiManagement.ProductsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) diff --git a/azurerm/data_source_api_management_product_test.go b/azurerm/data_source_api_management_product_test.go index 1b8c5aed460b..6a81fe584235 100644 --- a/azurerm/data_source_api_management_product_test.go +++ b/azurerm/data_source_api_management_product_test.go @@ -54,7 +54,6 @@ resource "azurerm_api_management" "test" { resource_group_name = "${azurerm_resource_group.test.name}" } - resource "azurerm_api_management_product" "test" { product_id = "test-product" api_management_name = "${azurerm_api_management.test.name}" diff --git a/azurerm/data_source_api_management_user.go b/azurerm/data_source_api_management_user.go index 374bd3245d50..be5263ec9a8e 100644 --- a/azurerm/data_source_api_management_user.go +++ b/azurerm/data_source_api_management_user.go @@ -48,7 +48,7 @@ func dataSourceArmApiManagementUser() *schema.Resource { } func dataSourceArmApiManagementUserRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.UsersClient + client := meta.(*ArmClient).apiManagement.UsersClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) diff --git a/azurerm/data_source_api_management_user_test.go b/azurerm/data_source_api_management_user_test.go index e757346ccf9c..288ce1e3f9f7 100644 --- a/azurerm/data_source_api_management_user_test.go +++ b/azurerm/data_source_api_management_user_test.go @@ -53,16 +53,15 @@ resource "azurerm_api_management" "test" { resource_group_name = "${azurerm_resource_group.test.name}" } - resource "azurerm_api_management_user" "test" { - user_id = "test-user" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - first_name = "Acceptance" - last_name = "Test" - email = "azure-acctest%d@example.com" - state = "active" - note = "Used for testing in dimension C-137." + user_id = "test-user" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + first_name = "Acceptance" + last_name = "Test" + email = "azure-acctest%d@example.com" + state = "active" + note = "Used for testing in dimension C-137." } data "azurerm_api_management_user" "test" { diff --git a/azurerm/data_source_app_service.go b/azurerm/data_source_app_service.go index f2ebd2ed222c..6b9b8cfa892e 100644 --- a/azurerm/data_source_app_service.go +++ b/azurerm/data_source_app_service.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -53,6 +54,9 @@ func dataSourceArmAppService() *schema.Resource { "app_settings": { Type: schema.TypeMap, Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "connection_string": { @@ -77,7 +81,7 @@ func dataSourceArmAppService() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), "site_credential": { Type: schema.TypeList, @@ -132,7 +136,7 @@ func dataSourceArmAppService() *schema.Resource { } } func dataSourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicesClient + client := meta.(*ArmClient).web.AppServicesClient resourceGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) @@ -174,7 +178,7 @@ func dataSourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error if err != nil { return err } - siteCredResp, err := siteCredFuture.Result(client) + siteCredResp, err := siteCredFuture.Result(*client) if err != nil { return fmt.Errorf("Error making Read request on AzureRM App Service Site Credential %q: %+v", name, err) } @@ -220,7 +224,5 @@ func dataSourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error return err } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_app_service_certificate.go b/azurerm/data_source_app_service_certificate.go new file mode 100644 index 000000000000..030c9fec250a --- /dev/null +++ b/azurerm/data_source_app_service_certificate.go @@ -0,0 +1,108 @@ +package azurerm + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceAppServiceCertificate() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAppServiceCertificateRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "location": azure.SchemaLocationForDataSource(), + + "friendly_name": { + Type: schema.TypeString, + Computed: true, + }, + + "subject_name": { + Type: schema.TypeString, + Computed: true, + }, + + "host_names": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "issuer": { + Type: schema.TypeString, + Computed: true, + }, + + "issue_date": { + Type: schema.TypeString, + Computed: true, + }, + + "expiration_date": { + Type: schema.TypeString, + Computed: true, + }, + + "thumbprint": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func dataSourceAppServiceCertificateRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + client := meta.(*ArmClient).web.CertificatesClient + + resourceGroup := d.Get("resource_group_name").(string) + name := d.Get("name").(string) + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("App Service Certificate %q (Resource Group %q) does not exist!", name, resourceGroup) + } + + return fmt.Errorf("Error retrieving App Service Certificate %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.SetId(*resp.ID) + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if props := resp.CertificateProperties; props != nil { + d.Set("friendly_name", props.FriendlyName) + d.Set("subject_name", props.SubjectName) + d.Set("host_names", props.HostNames) + d.Set("issuer", props.Issuer) + d.Set("issue_date", props.IssueDate.Format(time.RFC3339)) + d.Set("expiration_date", props.ExpirationDate.Format(time.RFC3339)) + d.Set("thumbprint", props.Thumbprint) + } + + return tags.FlattenAndSet(d, resp.Tags) +} diff --git a/azurerm/data_source_app_service_certificate_test.go b/azurerm/data_source_app_service_certificate_test.go new file mode 100644 index 000000000000..aba76a1a44d7 --- /dev/null +++ b/azurerm/data_source_app_service_certificate_test.go @@ -0,0 +1,45 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceAzureRMAppServiceCertificate_basic(t *testing.T) { + dataSourceName := "data.azurerm_app_service_certificate.test" + rInt := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAppServiceCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMAppServiceCertificate_basic(rInt, location), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "id"), + resource.TestCheckResourceAttrSet(dataSourceName, "subject_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "issue_date"), + resource.TestCheckResourceAttrSet(dataSourceName, "expiration_date"), + resource.TestCheckResourceAttrSet(dataSourceName, "thumbprint"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMAppServiceCertificate_basic(rInt int, location string) string { + template := testAccAzureRMAppServiceCertificatePfxNoPassword(rInt, location) + return fmt.Sprintf(` +%s + +data "azurerm_app_service_certificate" "test" { + name = "${azurerm_app_service_certificate.test.name}" + resource_group_name = "${azurerm_app_service_certificate.test.resource_group_name}" +} +`, template) +} diff --git a/azurerm/data_source_app_service_plan.go b/azurerm/data_source_app_service_plan.go index e4026083e954..817f5442eb73 100644 --- a/azurerm/data_source_app_service_plan.go +++ b/azurerm/data_source_app_service_plan.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -84,13 +85,13 @@ func dataSourceAppServicePlan() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceAppServicePlanRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicePlansClient + client := meta.(*ArmClient).web.AppServicePlansClient name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) @@ -110,7 +111,6 @@ func dataSourceAppServicePlanRead(d *schema.ResourceData, meta interface{}) erro d.Set("name", name) d.Set("resource_group_name", resourceGroup) d.Set("kind", resp.Kind) - d.Set("is_xenon", resp.IsXenon) if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) @@ -128,13 +128,13 @@ func dataSourceAppServicePlanRead(d *schema.ResourceData, meta interface{}) erro if props.MaximumElasticWorkerCount != nil { d.Set("maximum_elastic_worker_count", int(*props.MaximumElasticWorkerCount)) } + + d.Set("is_xenon", props.IsXenon) } if err := d.Set("sku", flattenAppServicePlanSku(resp.Sku)); err != nil { return fmt.Errorf("Error setting `sku`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_app_service_plan_test.go b/azurerm/data_source_app_service_plan_test.go index f74ee05f39a1..bd68b5878999 100644 --- a/azurerm/data_source_app_service_plan_test.go +++ b/azurerm/data_source_app_service_plan_test.go @@ -204,26 +204,26 @@ data "azurerm_app_service_plan" "test" { func testAccDataSourceAppServicePlan_basicWindowsContainer(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" + name = "acctestRG-%d" + location = "%s" } resource "azurerm_app_service_plan" "test" { - name = "acctestASP-%d" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - is_xenon = true - kind = "xenon" + name = "acctestASP-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + is_xenon = true + kind = "xenon" - sku { - tier = "PremiumContainer" - size = "PC2" - } + sku { + tier = "PremiumContainer" + size = "PC2" + } } data "azurerm_app_service_plan" "test" { - name = "${azurerm_app_service_plan.test.name}" - resource_group_name = "${azurerm_app_service_plan.test.resource_group_name}" + name = "${azurerm_app_service_plan.test.name}" + resource_group_name = "${azurerm_app_service_plan.test.resource_group_name}" } `, rInt, location, rInt) } diff --git a/azurerm/data_source_application_insights.go b/azurerm/data_source_application_insights.go index 878bf4a07cb7..e13e7b34a068 100644 --- a/azurerm/data_source_application_insights.go +++ b/azurerm/data_source_application_insights.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,13 +45,16 @@ func dataSourceArmApplicationInsights() *schema.Resource { "tags": { Type: schema.TypeMap, Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } } func dataSourceArmApplicationInsightsRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsClient + client := meta.(*ArmClient).appInsights.ComponentsClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -70,7 +74,5 @@ func dataSourceArmApplicationInsightsRead(d *schema.ResourceData, meta interface d.Set("location", resp.Location) d.Set("app_id", resp.AppID) d.Set("application_type", resp.ApplicationType) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_application_insights_test.go b/azurerm/data_source_application_insights_test.go index feac4cfebdd0..cd84ade5d72a 100644 --- a/azurerm/data_source_application_insights_test.go +++ b/azurerm/data_source_application_insights_test.go @@ -36,19 +36,19 @@ func TestAccDataSourceApplicationInsights_basic(t *testing.T) { func testAccResourceApplicationInsights_complete(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-%[1]d" - location = "%[2]s" + name = "acctestRG-%[1]d" + location = "%[2]s" } resource "azurerm_application_insights" "test" { - name = "acctestappinsights-%[1]d" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - application_type = "other" - - tags = { - "foo" = "bar" - } + name = "acctestappinsights-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + application_type = "other" + + tags = { + "foo" = "bar" + } } data "azurerm_application_insights" "test" { diff --git a/azurerm/data_source_application_security_group.go b/azurerm/data_source_application_security_group.go index 741fc1ddedd5..fd6732c1690f 100644 --- a/azurerm/data_source_application_security_group.go +++ b/azurerm/data_source_application_security_group.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -22,13 +23,13 @@ func dataSourceArmApplicationSecurityGroup() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationSecurityGroupsClient + client := meta.(*ArmClient).network.ApplicationSecurityGroupsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -51,7 +52,5 @@ func dataSourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta inte d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_availability_set.go b/azurerm/data_source_availability_set.go index fc2ee22e1115..70950abfbc46 100644 --- a/azurerm/data_source_availability_set.go +++ b/azurerm/data_source_availability_set.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,13 +44,13 @@ func dataSourceArmAvailabilitySet() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmAvailabilitySetRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).availSetClient + client := meta.(*ArmClient).compute.AvailabilitySetsClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -79,7 +80,5 @@ func dataSourceArmAvailabilitySetRead(d *schema.ResourceData, meta interface{}) d.Set("platform_fault_domain_count", strconv.Itoa(int(*v))) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_availability_set_test.go b/azurerm/data_source_availability_set_test.go index 12d4893443ad..d0b961772357 100644 --- a/azurerm/data_source_availability_set_test.go +++ b/azurerm/data_source_availability_set_test.go @@ -34,8 +34,8 @@ func TestAccDataSourceAvailabilitySet_basic(t *testing.T) { func testAccDataSourceAvailabilitySet_basic(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-%[1]d" - location = "%[2]s" + name = "acctestRG-%[1]d" + location = "%[2]s" } resource "azurerm_availability_set" "test" { @@ -43,9 +43,9 @@ resource "azurerm_availability_set" "test" { location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - tags = { - "foo" = "bar" - } + tags = { + "foo" = "bar" + } } data "azurerm_availability_set" "test" { diff --git a/azurerm/data_source_azuread_application.go b/azurerm/data_source_azuread_application.go index 49a4d50305c0..664acdc49b51 100644 --- a/azurerm/data_source_azuread_application.go +++ b/azurerm/data_source_azuread_application.go @@ -74,13 +74,12 @@ As such the Azure Active Directory resources within the AzureRM Provider are now } func dataSourceArmAzureADApplicationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationsClient + client := meta.(*ArmClient).graph.ApplicationsClient ctx := meta.(*ArmClient).StopContext var application graphrbac.Application if oId, ok := d.GetOk("object_id"); ok { - // use the object_id to find the Azure AD application objectId := oId.(string) resp, err := client.Get(ctx, objectId) @@ -94,7 +93,6 @@ func dataSourceArmAzureADApplicationRead(d *schema.ResourceData, meta interface{ application = resp } else { - // use the name to find the Azure AD application name := d.Get("name").(string) filter := fmt.Sprintf("displayName eq '%s'", name) @@ -132,16 +130,12 @@ func dataSourceArmAzureADApplicationRead(d *schema.ResourceData, meta interface{ d.Set("available_to_other_tenants", application.AvailableToOtherTenants) d.Set("oauth2_allow_implicit_flow", application.Oauth2AllowImplicitFlow) - if s := application.IdentifierUris; s != nil { - if err := d.Set("identifier_uris", *s); err != nil { - return fmt.Errorf("Error setting `identifier_uris`: %+v", err) - } + if err := d.Set("identifier_uris", application.IdentifierUris); err != nil { + return fmt.Errorf("Error setting `identifier_uris`: %+v", err) } - if s := application.ReplyUrls; s != nil { - if err := d.Set("reply_urls", *s); err != nil { - return fmt.Errorf("Error setting `reply_urls`: %+v", err) - } + if err := d.Set("reply_urls", application.ReplyUrls); err != nil { + return fmt.Errorf("Error setting `reply_urls`: %+v", err) } return nil diff --git a/azurerm/data_source_azuread_service_principal.go b/azurerm/data_source_azuread_service_principal.go index 6c6041abcffa..37fb42750516 100644 --- a/azurerm/data_source_azuread_service_principal.go +++ b/azurerm/data_source_azuread_service_principal.go @@ -45,13 +45,12 @@ As such the Azure Active Directory resources within the AzureRM Provider are now } func dataSourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext var servicePrincipal *graphrbac.ServicePrincipal if v, ok := d.GetOk("object_id"); ok { - //use the object_id to find the Azure AD service principal objectId := v.(string) app, err := client.Get(ctx, objectId) @@ -64,9 +63,7 @@ func dataSourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, me } servicePrincipal = &app - } else if _, ok := d.GetOk("display_name"); ok { - // use the display_name to find the Azure AD service principal displayName := d.Get("display_name").(string) filter := fmt.Sprintf("displayName eq '%s'", displayName) @@ -91,9 +88,7 @@ func dataSourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, me if servicePrincipal == nil { return fmt.Errorf("A Service Principal with the Display Name %q was not found", displayName) } - } else { - // use the application_id to find the Azure AD service principal applicationId := d.Get("application_id").(string) filter := fmt.Sprintf("appId eq '%s'", applicationId) @@ -118,7 +113,6 @@ func dataSourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, me if servicePrincipal == nil { return fmt.Errorf("A Service Principal for Application ID %q was not found", applicationId) } - } d.SetId(*servicePrincipal.ObjectID) diff --git a/azurerm/data_source_batch_account.go b/azurerm/data_source_batch_account.go index 8c62980bbe53..0ee5a4ab2a05 100644 --- a/azurerm/data_source_batch_account.go +++ b/azurerm/data_source_batch_account.go @@ -6,6 +6,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -30,6 +31,22 @@ func dataSourceArmBatchAccount() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "key_vault_reference": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "url": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "primary_access_key": { Type: schema.TypeString, Sensitive: true, @@ -44,13 +61,13 @@ func dataSourceArmBatchAccount() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmBatchAccountRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchAccountClient + client := meta.(*ArmClient).batch.AccountClient resourceGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) @@ -79,20 +96,26 @@ func dataSourceArmBatchAccountRead(d *schema.ResourceData, meta interface{}) err d.Set("storage_account_id", autoStorage.StorageAccountID) } d.Set("pool_allocation_mode", props.PoolAllocationMode) - } + poolAllocationMode := d.Get("pool_allocation_mode").(string) - if d.Get("pool_allocation_mode").(string) == string(batch.BatchService) { - keys, err := client.GetKeys(ctx, resourceGroup, name) + if poolAllocationMode == string(batch.BatchService) { + keys, err := client.GetKeys(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("Cannot read keys for Batch account %q (resource group %q): %v", name, resourceGroup, err) - } + if err != nil { + return fmt.Errorf("Cannot read keys for Batch account %q (resource group %q): %v", name, resourceGroup, err) + } - d.Set("primary_access_key", keys.Primary) - d.Set("secondary_access_key", keys.Secondary) - } + d.Set("primary_access_key", keys.Primary) + d.Set("secondary_access_key", keys.Secondary) - flattenAndSetTags(d, resp.Tags) + // set empty keyvault reference which is not needed in Batch Service allocation mode. + d.Set("key_vault_reference", []interface{}{}) + } else if poolAllocationMode == string(batch.UserSubscription) { + if err := d.Set("key_vault_reference", azure.FlattenBatchAccountKeyvaultReference(props.KeyVaultReference)); err != nil { + return fmt.Errorf("Error flattening `key_vault_reference`: %+v", err) + } + } + } - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_batch_account_test.go b/azurerm/data_source_batch_account_test.go index e9cf65b60bb2..0c118a2209ff 100644 --- a/azurerm/data_source_batch_account_test.go +++ b/azurerm/data_source_batch_account_test.go @@ -2,6 +2,7 @@ package azurerm import ( "fmt" + "os" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -58,6 +59,34 @@ func TestAccDataSourceAzureRMBatchAccount_complete(t *testing.T) { }) } +func TestAccDataSourceAzureRMBatchAccount_userSubscription(t *testing.T) { + dataSourceName := "data.azurerm_batch_account.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + tenantID := os.Getenv("ARM_TENANT_ID") + subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") + + config := testAccDataSourceAzureBatchAccount_userSubscription(ri, rs, location, tenantID, subscriptionID) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("testaccbatch%s", rs)), + resource.TestCheckResourceAttr(dataSourceName, "location", azure.NormalizeLocation(location)), + resource.TestCheckResourceAttr(dataSourceName, "pool_allocation_mode", "UserSubscription"), + resource.TestCheckResourceAttr(dataSourceName, "key_vault_reference.#", "1"), + ), + }, + }, + }) +} + func testAccDataSourceAzureRMBatchAccount_basic(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -112,3 +141,67 @@ data "azurerm_batch_account" "test" { } `, rInt, location, rString, rString) } + +func testAccDataSourceAzureBatchAccount_userSubscription(rInt int, rString string, location string, tenantID string, subscriptionID string) string { + return fmt.Sprintf(` +data "azurerm_azuread_service_principal" "test" { + display_name = "Microsoft Azure Batch" +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-%d-batchaccount" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "batchkv%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + enabled_for_disk_encryption = true + enabled_for_deployment = true + enabled_for_template_deployment = true + tenant_id = "%s" + + sku { + name = "standard" + } + + access_policy { + tenant_id = "%s" + object_id = "${data.azurerm_azuread_service_principal.test.object_id}" + + secret_permissions = [ + "get", + "list", + "set", + "delete" + ] + + } +} + +resource "azurerm_role_assignment" "contribrole" { + scope = "/subscriptions/%s" + role_definition_name = "Contributor" + principal_id = "${data.azurerm_azuread_service_principal.test.object_id}" +} + +resource "azurerm_batch_account" "test" { + name = "testaccbatch%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + pool_allocation_mode = "UserSubscription" + + key_vault_reference { + id = "${azurerm_key_vault.test.id}" + url = "${azurerm_key_vault.test.vault_uri}" + } +} + +data "azurerm_batch_account" "test" { + name = "${azurerm_batch_account.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rString, tenantID, tenantID, subscriptionID, rString) +} diff --git a/azurerm/data_source_batch_certificate.go b/azurerm/data_source_batch_certificate.go index ead007ea910b..f86599cc42c1 100644 --- a/azurerm/data_source_batch_certificate.go +++ b/azurerm/data_source_batch_certificate.go @@ -50,7 +50,7 @@ func dataSourceArmBatchCertificate() *schema.Resource { func dataSourceArmBatchCertificateRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).batchCertificateClient + client := meta.(*ArmClient).batch.CertificateClient resourceGroupName := d.Get("resource_group_name").(string) accountName := d.Get("account_name").(string) diff --git a/azurerm/data_source_batch_certificate_test.go b/azurerm/data_source_batch_certificate_test.go index 967413496468..904f1d3516e9 100644 --- a/azurerm/data_source_batch_certificate_test.go +++ b/azurerm/data_source_batch_certificate_test.go @@ -9,12 +9,12 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" ) -func TestAccDataSourceAzureRMBatchCertificate(t *testing.T) { +func TestAccDataSourceAzureRMBatchCertificate_basic(t *testing.T) { dataSourceName := "data.azurerm_batch_certificate.test" ri := tf.AccRandTimeInt() rs := acctest.RandString(4) location := testLocation() - config := testAccDataSourceAzureRMBatchCertificate(ri, rs, location) + config := testAccDataSourceAzureRMBatchCertificate_basic(ri, rs, location) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -23,19 +23,19 @@ func TestAccDataSourceAzureRMBatchCertificate(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", "SHA1-42C107874FD0E4A9583292A2F1098E8FE4B2EDDA"), + resource.TestCheckResourceAttr(dataSourceName, "name", "sha1-42c107874fd0e4a9583292a2f1098e8fe4b2edda"), resource.TestCheckResourceAttr(dataSourceName, "account_name", fmt.Sprintf("testaccbatch%s", rs)), resource.TestCheckResourceAttr(dataSourceName, "format", "Pfx"), resource.TestCheckResourceAttr(dataSourceName, "public_data", "MIIFqzCCA5OgAwIBAgIJAMs4jwMPq7T1MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMRgwFgYDVQQKDA9UZXJyYWZvcm0gVGVzdHMxDjAMBgNVBAsMBUF6dXJlMR4wHAYDVQQDDBVUZXJyYWZvcm0gQXBwIEdhdGV3YXkwHhcNMTYxMTAxMTcxOTEyWhcNMjYxMDMwMTcxOTEyWjBsMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEYMBYGA1UECgwPVGVycmFmb3JtIFRlc3RzMQ4wDAYDVQQLDAVBenVyZTEeMBwGA1UEAwwVVGVycmFmb3JtIEFwcCBHYXRld2F5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA49HW2pYIlW/mlaadLA1AsXiV48xVhXAvGVk3DEl1ffjp5bN8rap5WV1D83uMg1Ii7CJM8yNHkRkvN8n5WXFng4R5V1jPxGOTAj+xLybvEASi++GZelWdpOuMk8/nAoKPMbQ5NyKFy5WzlOduMldR7Awt2pwdId3akqm1i9ITG9Js+4P4nYXM8vfJCajILqi4YfhEoCNvS1EUgvlpSFE7pfNhc2W+zsfUWxWmB2SpWwX9MgQ1D4OmdKp+Eo+b6vzst3XArKMHMadPTUAk8H+ZgAnlX9yO+3vQ6z86vma/WgrG2LH6GCGXBjmKlhxVCPMLA5LeRUwEGc/Q7X/ClitGWY9umPN1XVj5e5Di1K2M082Y14mgbTTRTpv/nx7Xlph+MHnVhEWvaGMpqCHuM1W1y7wIS1IREYQ2q+K54xxZSPKYJMSnmj6A0hR/LBV0rL1uVhedEpdviduuO76qCyZrGG4HwBlW4hnIaahLzgqlvlmbDUQonAVPDgi3brVdXJgLv2zi7/ZHFW3IHgDylUVIdig0ccbzxKymlkGQ0RsLBjWOyxak2J8bN5JNVyxSwX43NZqxJ8yOv5xjB+rVMri9SX3Dl5NbFzOjynov601Pmwvb7zYnyttG2Hl5EKrkahjijGRjGy3EWEiBiArLkdTKCDHBlHxykTEvY6ZH5B9waP0CAwEAAaNQME4wHQYDVR0OBBYEFD2/Hq3IivZ5RMOKrPsM7ijIFHmMMB8GA1UdIwQYMBaAFD2/Hq3IivZ5RMOKrPsM7ijIFHmMMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKxHWO/Q4labjnCVxYi+kaMRCPJUdHj7lga8yi8EGHaL+CbwynkaiyTfPvtmcqiuaZM9BaXsuNMRcHMtXM0EHBsjViwAHk6SrqLXd/opFvMI2QbG93koFUCpczrpyO9GvnRN4iOIYbSPXAdGOB6bkpMbm/XajORoDrua+/ET/X/1FP0GZBTmEFwojuCfOI/VuJXj0OW8XzkLmsXiLpOiakjU1obBup/1lz9DtOEBsiB9Ury+f5gZ+FnZuqhgQxeDxlZ69P6YYAfkzhcfbf7HO+nMKhppAj1BFeR4SBb+F/fLchCGO5yohwkxWz3i2q9gTDhBgo31416viyCKFWSVW3Vn7jbsjZ+Q9MK1jVSOSxC7qoQkRoNy9SKpqylunXZb+K6F3HfBkDQvn3OwsxYiSOcX9JaWpQAInNIZVg+WrJ1PXm8PFIaVPJfMgP3GOdm9vRAMjOM5Bc9iqGr2spimFd5h0GmgLvh35B3jHHWF4i3NupJQ6hUvHQZtYZOxfwxnY0/LVBTyLTVlniFA7dGSI+5Uexm+Pjh7IMGI532jTONlfNm9Bz/jdf1o0FlOclzG6Eif22gml3GM3xCUVlaElylYNAjO2lfvZuRVo5GKdMwtV9acNl0OwSx+0zbMYY2Ni3jQCI4kOL5Csctryf0rHXTlCCvnzBYVDPKmFJPna61T"), - resource.TestCheckResourceAttr(dataSourceName, "thumbprint", "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA"), - resource.TestCheckResourceAttr(dataSourceName, "thumbprint_algorithm", "SHA1"), + resource.TestCheckResourceAttr(dataSourceName, "thumbprint", "42c107874fd0e4a9583292a2f1098e8fe4b2edda"), + resource.TestCheckResourceAttr(dataSourceName, "thumbprint_algorithm", "sha1"), // api now always returns this as lowercase ), }, }, }) } -func testAccDataSourceAzureRMBatchCertificate(rInt int, rString string, location string) string { +func testAccDataSourceAzureRMBatchCertificate_basic(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "testaccbatch%d" @@ -50,20 +50,19 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.pfx")}" - format = "Pfx" - password = "terraform" - thumbprint = "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.pfx")}" + format = "Pfx" + password = "terraform" + thumbprint = "42c107874fd0e4a9583292a2f1098e8fe4b2edda" + thumbprint_algorithm = "SHA1" } data "azurerm_batch_certificate" "test" { - name = "${azurerm_batch_certificate.test.name}" - account_name = "${azurerm_batch_account.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" + name = "${azurerm_batch_certificate.test.name}" + account_name = "${azurerm_batch_account.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" } - `, rInt, location, rString) } diff --git a/azurerm/data_source_batch_pool.go b/azurerm/data_source_batch_pool.go index 868703fa0c74..68c80ff919db 100644 --- a/azurerm/data_source_batch_pool.go +++ b/azurerm/data_source_batch_pool.go @@ -119,6 +119,27 @@ func dataSourceArmBatchPool() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "container_registries": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "registry_server": { + Type: schema.TypeString, + Computed: true, + }, + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + }, + }, }, }, }, @@ -181,6 +202,9 @@ func dataSourceArmBatchPool() *schema.Resource { "environment": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "user_identity": { @@ -252,7 +276,7 @@ func dataSourceArmBatchPool() *schema.Resource { } func dataSourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchPoolClient + client := meta.(*ArmClient).batch.PoolClient name := d.Get("name").(string) accountName := d.Get("account_name").(string) @@ -288,7 +312,7 @@ func dataSourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error if dcfg := props.DeploymentConfiguration; dcfg != nil { if vmcfg := dcfg.VirtualMachineConfiguration; vmcfg != nil { - if err := d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(vmcfg.ContainerConfiguration)); err != nil { + if err := d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(d, vmcfg.ContainerConfiguration)); err != nil { return fmt.Errorf("error setting `container_configuration`: %v", err) } diff --git a/azurerm/data_source_batch_pool_test.go b/azurerm/data_source_batch_pool_test.go index d21ee83d67af..d348183b63a0 100644 --- a/azurerm/data_source_batch_pool_test.go +++ b/azurerm/data_source_batch_pool_test.go @@ -54,6 +54,9 @@ func TestAccDataSourceAzureRMBatchPool_complete(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "certificate.0.visibility.3294600504", "StartTask"), resource.TestCheckResourceAttr(dataSourceName, "certificate.0.visibility.4077195354", "RemoteUser"), resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.type", "DockerCompatible"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.0.registry_server", "myContainerRegistry.azurecr.io"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.0.user_name", "myUserName"), ), }, }, @@ -88,23 +91,23 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.pfx")}" - format = "Pfx" - password = "terraform" - thumbprint = "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.pfx")}" + format = "Pfx" + password = "terraform" + thumbprint = "42c107874fd0e4a9583292a2f1098e8fe4b2edda" + thumbprint_algorithm = "SHA1" } resource "azurerm_batch_pool" "test" { - name = "testaccpool%s" - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - display_name = "Test Acc Pool" - vm_size = "Standard_A1" - node_agent_sku_id = "batch.node.ubuntu 16.04" - max_tasks_per_node = 2 + name = "testaccpool%s" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + display_name = "Test Acc Pool" + vm_size = "Standard_A1" + node_agent_sku_id = "batch.node.ubuntu 16.04" + max_tasks_per_node = 2 fixed_scale { target_dedicated_nodes = 2 @@ -121,13 +124,20 @@ resource "azurerm_batch_pool" "test" { certificate { id = "${azurerm_batch_certificate.test.id}" store_location = "CurrentUser" - visibility = [ "StartTask", "RemoteUser" ] + visibility = ["StartTask", "RemoteUser"] } container_configuration { type = "DockerCompatible" + container_registries= [ + { + registry_server = "myContainerRegistry.azurecr.io" + user_name = "myUserName" + password = "myPassword" + }, + ] } - + start_task { command_line = "echo 'Hello World from $env'" max_task_retry_count = 1 diff --git a/azurerm/data_source_builtin_role_definition.go b/azurerm/data_source_builtin_role_definition.go index 0b6af0a8a1af..982646d4f0be 100644 --- a/azurerm/data_source_builtin_role_definition.go +++ b/azurerm/data_source_builtin_role_definition.go @@ -3,7 +3,7 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization" "github.com/hashicorp/terraform/helper/schema" ) @@ -81,7 +81,7 @@ As such this Data Source will be removed in v2.0 of the AzureRM Provider. } func dataSourceArmBuiltInRoleDefinitionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleDefinitionsClient + client := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_cdn_profile.go b/azurerm/data_source_cdn_profile.go index b7f679f76f4b..816b0b5f70b3 100644 --- a/azurerm/data_source_cdn_profile.go +++ b/azurerm/data_source_cdn_profile.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -27,7 +28,7 @@ func dataSourceArmCdnProfile() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -59,7 +60,5 @@ func dataSourceArmCdnProfileRead(d *schema.ResourceData, meta interface{}) error d.Set("sku", string(sku.Name)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_client_config.go b/azurerm/data_source_client_config.go index 9b97cf5e3caa..a98b9167c032 100644 --- a/azurerm/data_source_client_config.go +++ b/azurerm/data_source_client_config.go @@ -17,21 +17,31 @@ func dataSourceArmClientConfig() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "tenant_id": { Type: schema.TypeString, Computed: true, }, + "subscription_id": { Type: schema.TypeString, Computed: true, }, - "service_principal_application_id": { + + "object_id": { Type: schema.TypeString, Computed: true, }, + + "service_principal_application_id": { + Type: schema.TypeString, + Computed: true, + Deprecated: "This has been deprecated in favour of the `client_id` property", + }, "service_principal_object_id": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Deprecated: "This has been deprecated in favour of the unified `authenticated_object_id` property", }, }, } @@ -43,7 +53,7 @@ func dataSourceArmClientConfigRead(d *schema.ResourceData, meta interface{}) err var servicePrincipal *graphrbac.ServicePrincipal if client.usingServicePrincipal { - spClient := client.servicePrincipalsClient + spClient := client.graph.ServicePrincipalsClient // Application & Service Principal is 1:1 per tenant. Since we know the appId (client_id) // here, we can query for the Service Principal whose appId matches. filter := fmt.Sprintf("appId eq '%s'", client.clientId) @@ -66,12 +76,19 @@ func dataSourceArmClientConfigRead(d *schema.ResourceData, meta interface{}) err d.Set("subscription_id", client.subscriptionId) if principal := servicePrincipal; principal != nil { - d.Set("service_principal_application_id", principal.AppID) + d.Set("service_principal_application_id", client.clientId) d.Set("service_principal_object_id", principal.ObjectID) } else { d.Set("service_principal_application_id", "") d.Set("service_principal_object_id", "") } + d.Set("object_id", "") + if v, err := client.getAuthenticatedObjectID(ctx); err != nil { + return fmt.Errorf("Error getting authenticated object ID: %v", err) + } else { + d.Set("object_id", v) + } + return nil } diff --git a/azurerm/data_source_client_config_test.go b/azurerm/data_source_client_config_test.go index 3e39d0ac62a3..ba51b18c9980 100644 --- a/azurerm/data_source_client_config_test.go +++ b/azurerm/data_source_client_config_test.go @@ -25,6 +25,7 @@ func TestAccDataSourceAzureRMClientConfig_basic(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "client_id", clientId), resource.TestCheckResourceAttr(dataSourceName, "tenant_id", tenantId), resource.TestCheckResourceAttr(dataSourceName, "subscription_id", subscriptionId), + testAzureRMClientConfigGUIDAttr(dataSourceName, "object_id"), testAzureRMClientConfigGUIDAttr(dataSourceName, "service_principal_application_id"), testAzureRMClientConfigGUIDAttr(dataSourceName, "service_principal_object_id"), ), diff --git a/azurerm/data_source_container_registry.go b/azurerm/data_source_container_registry.go index 8edfdd1a1e10..3e2ff2dc6d8a 100644 --- a/azurerm/data_source_container_registry.go +++ b/azurerm/data_source_container_registry.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -54,13 +55,13 @@ func dataSourceArmContainerRegistry() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).containers.RegistryClient + client := meta.(*ArmClient).containers.RegistriesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -108,7 +109,5 @@ func dataSourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{} d.Set("admin_password", "") } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_cosmos_db_account.go b/azurerm/data_source_cosmos_db_account.go index 0aee23ac951b..1c131e50511e 100644 --- a/azurerm/data_source_cosmos_db_account.go +++ b/azurerm/data_source_cosmos_db_account.go @@ -5,6 +5,7 @@ import ( "log" "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" @@ -25,7 +26,7 @@ func dataSourceArmCosmosDbAccount() *schema.Resource { "location": azure.SchemaLocationForDataSource(), - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), "offer_type": { Type: schema.TypeString, @@ -178,7 +179,7 @@ func dataSourceArmCosmosDbAccount() *schema.Resource { } func dataSourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -201,7 +202,6 @@ func dataSourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) d.Set("location", azure.NormalizeLocation(*location)) } d.Set("kind", string(resp.Kind)) - flattenAndSetTags(d, resp.Tags) if props := resp.DatabaseAccountProperties; props != nil { d.Set("offer_type", string(props.DatabaseAccountOfferType)) @@ -238,18 +238,30 @@ func dataSourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) readEndpoints := make([]string, 0) if locations := props.ReadLocations; locations != nil { for _, l := range *locations { + if l.DocumentEndpoint == nil { + continue + } + readEndpoints = append(readEndpoints, *l.DocumentEndpoint) } } - d.Set("read_endpoints", readEndpoints) + if err := d.Set("read_endpoints", readEndpoints); err != nil { + return fmt.Errorf("Error setting `read_endpoints`: %s", err) + } writeEndpoints := make([]string, 0) if locations := props.WriteLocations; locations != nil { for _, l := range *locations { + if l.DocumentEndpoint == nil { + continue + } + writeEndpoints = append(writeEndpoints, *l.DocumentEndpoint) } } - d.Set("write_endpoints", writeEndpoints) + if err := d.Set("write_endpoints", writeEndpoints); err != nil { + return fmt.Errorf("Error setting `write_endpoints`: %s", err) + } d.Set("enable_multiple_write_locations", resp.EnableMultipleWriteLocations) } @@ -270,7 +282,7 @@ func dataSourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) d.Set("secondary_readonly_master_key", readonlyKeys.SecondaryReadonlyMasterKey) } - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenAzureRmCosmosDBAccountCapabilitiesAsList(capabilities *[]documentdb.Capability) *[]map[string]interface{} { diff --git a/azurerm/data_source_data_lake_store.go b/azurerm/data_source_data_lake_store.go index 0ccdc4857e25..4da6e65e5630 100644 --- a/azurerm/data_source_data_lake_store.go +++ b/azurerm/data_source_data_lake_store.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -48,13 +49,13 @@ func dataSourceArmDataLakeStoreAccount() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmDateLakeStoreAccountRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).dataLakeStoreAccountClient + client := meta.(*ArmClient).datalake.StoreAccountsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -90,7 +91,5 @@ func dataSourceArmDateLakeStoreAccountRead(d *schema.ResourceData, meta interfac } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_dev_test_lab.go b/azurerm/data_source_dev_test_lab.go index dc994d515431..ab972349301d 100644 --- a/azurerm/data_source_dev_test_lab.go +++ b/azurerm/data_source_dev_test_lab.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -29,7 +30,7 @@ func dataSourceArmDevTestLab() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), "artifacts_storage_account_id": { Type: schema.TypeString, @@ -65,7 +66,7 @@ func dataSourceArmDevTestLab() *schema.Resource { } func dataSourceArmDevTestLabRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).devTestLabsClient + client := meta.(*ArmClient).devTestLabs.LabsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -100,7 +101,5 @@ func dataSourceArmDevTestLabRead(d *schema.ResourceData, meta interface{}) error d.Set("unique_identifier", props.UniqueIdentifier) } - flattenAndSetTags(d, read.Tags) - - return nil + return tags.FlattenAndSet(d, read.Tags) } diff --git a/azurerm/data_source_dev_test_virtual_network.go b/azurerm/data_source_dev_test_virtual_network.go new file mode 100644 index 000000000000..3f4a358734f5 --- /dev/null +++ b/azurerm/data_source_dev_test_virtual_network.go @@ -0,0 +1,181 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2016-05-15/dtl" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmDevTestVirtualNetwork() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmDevTestVnetRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "lab_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.DevTestLabName(), + }, + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "unique_identifier": { + Type: schema.TypeString, + Computed: true, + }, + + "allowed_subnets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_public_ip": { + Type: schema.TypeString, + Computed: true, + }, + "lab_subnet_name": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "subnet_overrides": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lab_subnet_name": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + "use_in_vm_creation_permission": { + Type: schema.TypeString, + Computed: true, + }, + "use_public_ip_address_permission": { + Type: schema.TypeString, + Computed: true, + }, + "virtual_network_pool_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceArmDevTestVnetRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).devTestLabs.VirtualNetworksClient + ctx := meta.(*ArmClient).StopContext + + resGroup := d.Get("resource_group_name").(string) + labName := d.Get("lab_name").(string) + name := d.Get("name").(string) + + resp, err := client.Get(ctx, resGroup, labName, name, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: Virtual Network %q in Dev Test Lab %q (Resource Group %q) was not found", name, labName, resGroup) + } + + return fmt.Errorf("Error making Read request on Virtual Network %q in Dev Test Lab %q (Resource Group %q): %+v", name, labName, resGroup, err) + } + + if resp.ID == nil || *resp.ID == "" { + return fmt.Errorf("API returns a nil/empty id on Virtual Network %q in Dev Test Lab %q (Resource Group %q): %+v", name, labName, resGroup, err) + } + d.SetId(*resp.ID) + + if props := resp.VirtualNetworkProperties; props != nil { + if as := props.AllowedSubnets; as != nil { + if err := d.Set("allowed_subnets", flattenDevTestVirtualNetworkAllowedSubnets(as)); err != nil { + return fmt.Errorf("error setting `allowed_subnets`: %v", err) + } + } + if so := props.SubnetOverrides; so != nil { + if err := d.Set("subnet_overrides", flattenDevTestVirtualNetworkSubnetOverrides(so)); err != nil { + return fmt.Errorf("error setting `subnet_overrides`: %v", err) + } + } + d.Set("unique_identifier", props.UniqueIdentifier) + } + return nil +} + +func flattenDevTestVirtualNetworkAllowedSubnets(input *[]dtl.Subnet) []interface{} { + result := make([]interface{}, 0) + + if input == nil { + return result + } + + for _, v := range *input { + allowedSubnet := make(map[string]interface{}) + + allowedSubnet["allow_public_ip"] = string(v.AllowPublicIP) + + if resourceID := v.ResourceID; resourceID != nil { + allowedSubnet["resource_id"] = *resourceID + } + + if labSubnetName := v.LabSubnetName; labSubnetName != nil { + allowedSubnet["lab_subnet_name"] = *labSubnetName + } + + result = append(result, allowedSubnet) + } + + return result +} + +func flattenDevTestVirtualNetworkSubnetOverrides(input *[]dtl.SubnetOverride) []interface{} { + result := make([]interface{}, 0) + + if input == nil { + return result + } + + for _, v := range *input { + subnetOverride := make(map[string]interface{}) + if v.LabSubnetName != nil { + subnetOverride["lab_subnet_name"] = *v.LabSubnetName + } + if v.ResourceID != nil { + subnetOverride["resource_id"] = *v.ResourceID + } + + subnetOverride["use_public_ip_address_permission"] = string(v.UsePublicIPAddressPermission) + subnetOverride["use_in_vm_creation_permission"] = string(v.UseInVMCreationPermission) + + if v.VirtualNetworkPoolName != nil { + subnetOverride["virtual_network_pool_name"] = *v.VirtualNetworkPoolName + } + + result = append(result, subnetOverride) + } + + return result +} diff --git a/azurerm/data_source_dev_test_virtual_network_test.go b/azurerm/data_source_dev_test_virtual_network_test.go new file mode 100644 index 000000000000..a6ebb2b0f696 --- /dev/null +++ b/azurerm/data_source_dev_test_virtual_network_test.go @@ -0,0 +1,80 @@ +package azurerm + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceArmDevTestVirtualNetwork_basic(t *testing.T) { + dataSourceName := "data.azurerm_dev_test_virtual_network.test" + ri := tf.AccRandTimeInt() + + name := fmt.Sprintf("acctestdtvn%d", ri) + labName := fmt.Sprintf("acctestdtl%d", ri) + resGroup := fmt.Sprintf("acctestRG-%d", ri) + subnetName := name + "Subnet" + subnetResourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s", os.Getenv("ARM_SUBSCRIPTION_ID"), resGroup, name, subnetName) + + config := testAccDataSourceArmDevTestVirtualNetwork_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "name", name), + resource.TestCheckResourceAttr(dataSourceName, "lab_name", labName), + resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resGroup), + resource.TestCheckResourceAttr(dataSourceName, "allowed_subnets.0.allow_public_ip", "Allow"), + resource.TestCheckResourceAttr(dataSourceName, "allowed_subnets.0.lab_subnet_name", subnetName), + resource.TestCheckResourceAttr(dataSourceName, "allowed_subnets.0.resource_id", subnetResourceID), + resource.TestCheckResourceAttr(dataSourceName, "subnet_overrides.0.lab_subnet_name", subnetName), + resource.TestCheckResourceAttr(dataSourceName, "subnet_overrides.0.resource_id", subnetResourceID), + resource.TestCheckResourceAttr(dataSourceName, "subnet_overrides.0.use_in_vm_creation_permission", "Allow"), + resource.TestCheckResourceAttr(dataSourceName, "subnet_overrides.0.use_public_ip_address_permission", "Allow"), + resource.TestCheckResourceAttr(dataSourceName, "subnet_overrides.0.virtual_network_pool_name", ""), + ), + }, + }, + }) +} + +func testAccDataSourceArmDevTestVirtualNetwork_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dev_test_lab" "test" { + name = "acctestdtl%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_dev_test_virtual_network" "test" { + name = "acctestdtvn%d" + lab_name = "${azurerm_dev_test_lab.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + + subnet { + use_public_ip_address = "Allow" + use_in_virtual_machine_creation = "Allow" + } +} + +data "azurerm_dev_test_virtual_network" "test" { + name = "${azurerm_dev_test_virtual_network.test.name}" + lab_name = "${azurerm_dev_test_lab.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + + +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/data_source_dns_zone.go b/azurerm/data_source_dns_zone.go index 333fc55ae03a..33de32d82c61 100644 --- a/azurerm/data_source_dns_zone.go +++ b/azurerm/data_source_dns_zone.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/preview/dns/mgmt/2018-03-01-preview/dns" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,8 +45,9 @@ func dataSourceArmDnsZone() *schema.Resource { }, "zone_type": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Deprecated: "Private DNS Zones are now supported through a separate resource in Azure & Terraform", }, "registration_virtual_network_ids": { @@ -60,7 +62,7 @@ func dataSourceArmDnsZone() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -85,7 +87,7 @@ func dataSourceArmDnsZoneRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error reading DNS Zone %q (Resource Group %q): %+v", name, resourceGroup, err) } } else { - rgClient := meta.(*ArmClient).resourceGroupsClient + rgClient := meta.(*ArmClient).resource.GroupsClient resp, resourceGroup, err = findZone(client, rgClient, ctx, name) if err != nil { @@ -135,12 +137,10 @@ func dataSourceArmDnsZoneRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } -func findZone(client dns.ZonesClient, rgClient resources.GroupsGroupClient, ctx context.Context, name string) (dns.Zone, string, error) { +func findZone(client *dns.ZonesClient, rgClient *resources.GroupsClient, ctx context.Context, name string) (dns.Zone, string, error) { groups, err := rgClient.List(ctx, "", nil) if err != nil { return dns.Zone{}, "", fmt.Errorf("Error listing Resource Groups: %+v", err) diff --git a/azurerm/data_source_eventhub_namespace.go b/azurerm/data_source_eventhub_namespace.go index be4949601cdd..0e2d98ecbfc4 100644 --- a/azurerm/data_source_eventhub_namespace.go +++ b/azurerm/data_source_eventhub_namespace.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -72,7 +73,7 @@ func dataSourceEventHubNamespace() *schema.Resource { Sensitive: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -120,7 +121,5 @@ func dataSourceEventHubNamespaceRead(d *schema.ResourceData, meta interface{}) e d.Set("maximum_throughput_units", int(*props.MaximumThroughputUnits)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_express_route_circuit.go b/azurerm/data_source_express_route_circuit.go index d15b3488bf25..0d3c0e16c4ae 100644 --- a/azurerm/data_source_express_route_circuit.go +++ b/azurerm/data_source_express_route_circuit.go @@ -3,7 +3,7 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" @@ -115,7 +115,7 @@ func dataSourceArmExpressRouteCircuit() *schema.Resource { func dataSourceArmExpressRouteCircuitRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).expressRouteCircuitClient + client := meta.(*ArmClient).network.ExpressRouteCircuitsClient name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) @@ -148,7 +148,6 @@ func dataSourceArmExpressRouteCircuitRead(d *schema.ResourceData, meta interface return fmt.Errorf("Error setting `service_provider_properties`: %+v", err) } } - } if resp.Sku != nil { diff --git a/azurerm/data_source_express_route_circuit_test.go b/azurerm/data_source_express_route_circuit_test.go index d2932c4816b3..f533beaf57b6 100644 --- a/azurerm/data_source_express_route_circuit_test.go +++ b/azurerm/data_source_express_route_circuit_test.go @@ -43,5 +43,5 @@ func testAccDataSourceAzureRMExpressRoute_basic(rInt int, location string) strin resource_group_name = "${azurerm_resource_group.test.name}" name = "${azurerm_express_route_circuit.test.name}" } - `, config) +`, config) } diff --git a/azurerm/data_source_firewall.go b/azurerm/data_source_firewall.go index 69e1fe85ab41..2fafb96ae980 100644 --- a/azurerm/data_source_firewall.go +++ b/azurerm/data_source_firewall.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -55,13 +56,13 @@ func dataSourceArmFirewall() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmFirewallRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -93,7 +94,5 @@ func dataSourceArmFirewallRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, read.Tags) - - return nil + return tags.FlattenAndSet(d, read.Tags) } diff --git a/azurerm/data_source_firewall_test.go b/azurerm/data_source_firewall_test.go index 1cebdb22cb14..3519fe4ec166 100644 --- a/azurerm/data_source_firewall_test.go +++ b/azurerm/data_source_firewall_test.go @@ -71,7 +71,7 @@ resource "azurerm_firewall" "test" { } data "azurerm_firewall" "test" { - name = "${azurerm_firewall.test.name}" + name = "${azurerm_firewall.test.name}" resource_group_name = "${azurerm_resource_group.test.name}" } `, rInt, location, rInt, rInt, rInt) diff --git a/azurerm/data_source_hdinsight_cluster.go b/azurerm/data_source_hdinsight_cluster.go index 526f20c0e4d2..04d15ff2e8b5 100644 --- a/azurerm/data_source_hdinsight_cluster.go +++ b/azurerm/data_source_hdinsight_cluster.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -29,6 +30,9 @@ func dataSourceArmHDInsightSparkCluster() *schema.Resource { "component_versions": { Type: schema.TypeMap, Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "kind": { @@ -63,7 +67,7 @@ func dataSourceArmHDInsightSparkCluster() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), "edge_ssh_endpoint": { Type: schema.TypeString, @@ -137,9 +141,7 @@ func dataSourceArmHDInsightClusterRead(d *schema.ResourceData, meta interface{}) d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenHDInsightsDataSourceComponentVersions(input map[string]*string) map[string]string { diff --git a/azurerm/data_source_image.go b/azurerm/data_source_image.go index ac90aa41cd8b..98be2e5abc27 100644 --- a/azurerm/data_source_image.go +++ b/azurerm/data_source_image.go @@ -6,10 +6,11 @@ import ( "regexp" "sort" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -108,13 +109,13 @@ func dataSourceArmImage() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmImageRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).imageClient + client := meta.(*ArmClient).compute.ImagesClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -175,7 +176,6 @@ func dataSourceArmImageRead(d *schema.ResourceData, meta interface{}) error { }) } img = list[0] - } d.SetId(*img.ID) @@ -201,7 +201,5 @@ func dataSourceArmImageRead(d *schema.ResourceData, meta interface{}) error { d.Set("zone_resilient", profile.ZoneResilient) } - flattenAndSetTags(d, img.Tags) - - return nil + return tags.FlattenAndSet(d, img.Tags) } diff --git a/azurerm/data_source_key_vault.go b/azurerm/data_source_key_vault.go index 8bcb7cb02cf7..b860cd153883 100644 --- a/azurerm/data_source_key_vault.go +++ b/azurerm/data_source_key_vault.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/set" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -26,6 +27,7 @@ func dataSourceArmKeyVault() *schema.Resource { "location": azure.SchemaLocationForDataSource(), + // Remove in 2.0 "sku": { Type: schema.TypeList, Computed: true, @@ -39,6 +41,11 @@ func dataSourceArmKeyVault() *schema.Resource { }, }, + "sku_name": { + Type: schema.TypeString, + Computed: true, + }, + "vault_uri": { Type: schema.TypeString, Computed: true, @@ -142,13 +149,13 @@ func dataSourceArmKeyVault() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -177,8 +184,17 @@ func dataSourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { d.Set("enabled_for_template_deployment", props.EnabledForTemplateDeployment) d.Set("vault_uri", props.VaultURI) - if err := d.Set("sku", flattenKeyVaultDataSourceSku(props.Sku)); err != nil { - return fmt.Errorf("Error setting `sku` for KeyVault %q: %+v", *resp.Name, err) + if sku := props.Sku; sku != nil { + // Remove in 2.0 + if err := d.Set("sku", flattenKeyVaultDataSourceSku(sku)); err != nil { + return fmt.Errorf("Error setting `sku` for KeyVault %q: %+v", *resp.Name, err) + } + + if err := d.Set("sku_name", string(sku.Name)); err != nil { + return fmt.Errorf("Error setting `sku_name` for KeyVault %q: %+v", *resp.Name, err) + } + } else { + return fmt.Errorf("Error making Read request on KeyVault %q: Unable to retrieve 'sku' value", *resp.Name) } flattenedPolicies := azure.FlattenKeyVaultAccessPolicies(props.AccessPolicies) @@ -191,11 +207,10 @@ func dataSourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } +// Remove in 2.0 func flattenKeyVaultDataSourceSku(sku *keyvault.Sku) []interface{} { result := map[string]interface{}{ "name": string(sku.Name), diff --git a/azurerm/data_source_key_vault_key.go b/azurerm/data_source_key_vault_key.go index f7b39e9d16be..29255402df6a 100644 --- a/azurerm/data_source_key_vault_key.go +++ b/azurerm/data_source_key_vault_key.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -73,14 +74,14 @@ func dataSourceArmKeyVaultKey() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext keyVaultBaseUri := d.Get("vault_uri").(string) @@ -140,9 +141,7 @@ func dataSourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) erro d.Set("version", parsedId.Version) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenKeyVaultKeyDataSourceOptions(input *[]string) []interface{} { diff --git a/azurerm/data_source_key_vault_secret.go b/azurerm/data_source_key_vault_secret.go index 98ab090d01ca..b03d2177e674 100644 --- a/azurerm/data_source_key_vault_secret.go +++ b/azurerm/data_source_key_vault_secret.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -57,14 +58,14 @@ func dataSourceArmKeyVaultSecret() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmKeyVaultSecretRead(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -118,6 +119,5 @@ func dataSourceArmKeyVaultSecretRead(d *schema.ResourceData, meta interface{}) e d.Set("version", respID.Version) d.Set("content_type", resp.ContentType) - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_key_vault_test.go b/azurerm/data_source_key_vault_test.go index be6e9f587667..288273fe631e 100644 --- a/azurerm/data_source_key_vault_test.go +++ b/azurerm/data_source_key_vault_test.go @@ -14,6 +14,34 @@ func TestAccDataSourceAzureRMKeyVault_basic(t *testing.T) { location := testLocation() config := testAccDataSourceAzureRMKeyVault_basic(ri, location) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultExists(dataSourceName), + resource.TestCheckResourceAttrSet(dataSourceName, "tenant_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "sku_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "access_policy.0.tenant_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "access_policy.0.object_id"), + resource.TestCheckResourceAttr(dataSourceName, "access_policy.0.key_permissions.0", "create"), + resource.TestCheckResourceAttr(dataSourceName, "access_policy.0.secret_permissions.0", "set"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMKeyVault_basicClassic(t *testing.T) { + dataSourceName := "data.azurerm_key_vault.test" + ri := tf.AccRandTimeInt() + location := testLocation() + config := testAccDataSourceAzureRMKeyVault_basic(ri, location) + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, diff --git a/azurerm/data_source_kubernetes_cluster.go b/azurerm/data_source_kubernetes_cluster.go index 0edf806433d9..5b3a00bd3e43 100644 --- a/azurerm/data_source_kubernetes_cluster.go +++ b/azurerm/data_source_kubernetes_cluster.go @@ -4,10 +4,11 @@ import ( "fmt" "strings" - "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-02-01/containerservice" + "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-06-01/containerservice" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/kubernetes" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -63,6 +64,19 @@ func dataSourceArmKubernetesCluster() *schema.Resource { }, }, }, + + "kube_dashboard": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, }, }, }, @@ -87,6 +101,29 @@ func dataSourceArmKubernetesCluster() *schema.Resource { Computed: true, }, + "max_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "min_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "enable_auto_scaling": { + Type: schema.TypeBool, + Computed: true, + }, + + "availability_zones": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + // TODO: remove this in a future version "dns_prefix": { Type: schema.TypeString, @@ -118,6 +155,12 @@ func dataSourceArmKubernetesCluster() *schema.Resource { Type: schema.TypeInt, Computed: true, }, + + "node_taints": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, }, }, @@ -245,6 +288,19 @@ func dataSourceArmKubernetesCluster() *schema.Resource { }, }, + "windows_profile": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "admin_username": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "network_profile": { Type: schema.TypeList, Computed: true, @@ -279,6 +335,11 @@ func dataSourceArmKubernetesCluster() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "load_balancer_sku": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -336,7 +397,7 @@ func dataSourceArmKubernetesCluster() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -391,6 +452,11 @@ func dataSourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("Error setting `linux_profile`: %+v", err) } + windowsProfile := flattenKubernetesClusterDataSourceWindowsProfile(props.WindowsProfile) + if err := d.Set("windows_profile", windowsProfile); err != nil { + return fmt.Errorf("Error setting `windows_profile`: %+v", err) + } + networkProfile := flattenKubernetesClusterDataSourceNetworkProfile(props.NetworkProfile) if err := d.Set("network_profile", networkProfile); err != nil { return fmt.Errorf("Error setting `network_profile`: %+v", err) @@ -430,9 +496,7 @@ func dataSourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("Error setting `kube_config`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenKubernetesClusterDataSourceRoleBasedAccessControl(input *containerservice.ManagedClusterProperties) []interface{} { @@ -544,6 +608,20 @@ func flattenKubernetesClusterDataSourceAddonProfiles(profile map[string]*contain } values["oms_agent"] = agents + kubeDashboards := make([]interface{}, 0) + if kubeDashboard := profile["kubeDashboard"]; kubeDashboard != nil { + enabled := false + if enabledVal := kubeDashboard.Enabled; enabledVal != nil { + enabled = *enabledVal + } + + output := map[string]interface{}{ + "enabled": enabled, + } + kubeDashboards = append(kubeDashboards, output) + } + values["kube_dashboard"] = kubeDashboards + return []interface{}{values} } @@ -565,6 +643,20 @@ func flattenKubernetesClusterDataSourceAgentPoolProfiles(input *[]containerservi agentPoolProfile["count"] = int(*profile.Count) } + if profile.MinCount != nil { + agentPoolProfile["min_count"] = int(*profile.MinCount) + } + + if profile.MaxCount != nil { + agentPoolProfile["max_count"] = int(*profile.MaxCount) + } + + if profile.EnableAutoScaling != nil { + agentPoolProfile["enable_auto_scaling"] = *profile.EnableAutoScaling + } + + agentPoolProfile["availability_zones"] = utils.FlattenStringSlice(profile.AvailabilityZones) + if profile.Name != nil { agentPoolProfile["name"] = *profile.Name } @@ -589,6 +681,10 @@ func flattenKubernetesClusterDataSourceAgentPoolProfiles(input *[]containerservi agentPoolProfile["max_pods"] = int(*profile.MaxPods) } + if profile.NodeTaints != nil { + agentPoolProfile["node_taints"] = *profile.NodeTaints + } + agentPoolProfiles = append(agentPoolProfiles, agentPoolProfile) } @@ -622,6 +718,19 @@ func flattenKubernetesClusterDataSourceLinuxProfile(input *containerservice.Linu return []interface{}{values} } +func flattenKubernetesClusterDataSourceWindowsProfile(input *containerservice.ManagedClusterWindowsProfile) []interface{} { + if input == nil { + return []interface{}{} + } + values := make(map[string]interface{}) + + if username := input.AdminUsername; username != nil { + values["admin_username"] = *username + } + + return []interface{}{values} +} + func flattenKubernetesClusterDataSourceNetworkProfile(profile *containerservice.NetworkProfileType) []interface{} { values := make(map[string]interface{}) @@ -647,6 +756,10 @@ func flattenKubernetesClusterDataSourceNetworkProfile(profile *containerservice. values["pod_cidr"] = *profile.PodCidr } + if profile.LoadBalancerSku != "" { + values["load_balancer_sku"] = string(profile.LoadBalancerSku) + } + return []interface{}{values} } diff --git a/azurerm/data_source_kubernetes_cluster_test.go b/azurerm/data_source_kubernetes_cluster_test.go index 1d093bbdad27..bfef2b5d7904 100644 --- a/azurerm/data_source_kubernetes_cluster_test.go +++ b/azurerm/data_source_kubernetes_cluster_test.go @@ -390,6 +390,32 @@ func TestAccDataSourceAzureRMKubernetesCluster_addOnProfileOMS(t *testing.T) { }) } +func TestAccDataSourceAzureRMKubernetesCluster_addOnProfileKubeDashboard(t *testing.T) { + dataSourceName := "data.azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + location := testLocation() + config := testAccDataSourceAzureRMKubernetesCluster_addOnProfileKubeDashboard(ri, clientId, clientSecret, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(dataSourceName), + resource.TestCheckResourceAttr(dataSourceName, "addon_profile.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "addon_profile.0.kube_dashboard.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "addon_profile.0.kube_dashboard.0.enabled", "false"), + ), + }, + }, + }) +} + func TestAccDataSourceAzureRMKubernetesCluster_addOnProfileRouting(t *testing.T) { dataSourceName := "data.azurerm_kubernetes_cluster.test" ri := tf.AccRandTimeInt() @@ -417,6 +443,88 @@ func TestAccDataSourceAzureRMKubernetesCluster_addOnProfileRouting(t *testing.T) }) } +func TestAccDataSourceAzureRMKubernetesCluster_autoscalingNoAvailabilityZones(t *testing.T) { + dataSourceName := "data.azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + + config := testAccDataSourceAzureRMKubernetesCluster_autoScalingNoAvailabilityZones(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(dataSourceName), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.min_count", "1"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.max_count", "2"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.type", "VirtualMachineScaleSets"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.enable_auto_scaling", "true"), + resource.TestCheckNoResourceAttr(dataSourceName, "agent_pool_profile.0.availability_zones"), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMKubernetesCluster_autoscalingWithAvailabilityZones(t *testing.T) { + dataSourceName := "data.azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + + config := testAccDataSourceAzureRMKubernetesCluster_autoScalingWithAvailabilityZones(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(dataSourceName), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.min_count", "1"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.max_count", "2"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.type", "VirtualMachineScaleSets"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.enable_auto_scaling", "true"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.availability_zones.#", "2"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.availability_zones.0", "1"), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.0.availability_zones.1", "2"), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMKubernetesCluster_nodeTaints(t *testing.T) { + dataSourceName := "data.azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + + config := testAccDataSourceAzureRMKubernetesCluster_nodeTaints(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(dataSourceName), + resource.TestCheckResourceAttr(dataSourceName, "agent_pool_profile.1.node_taints.0", "key=value:NoSchedule"), + ), + }, + }, + }) +} + func testAccDataSourceAzureRMKubernetesCluster_basic(rInt int, clientId string, clientSecret string, location string) string { r := testAccAzureRMKubernetesCluster_basic(rInt, clientId, clientSecret, location) return fmt.Sprintf(` @@ -573,6 +681,18 @@ data "azurerm_kubernetes_cluster" "test" { `, r) } +func testAccDataSourceAzureRMKubernetesCluster_addOnProfileKubeDashboard(rInt int, clientId string, clientSecret string, location string) string { + r := testAccAzureRMKubernetesCluster_addonProfileKubeDashboard(rInt, clientId, clientSecret, location) + return fmt.Sprintf(` +%s + +data "azurerm_kubernetes_cluster" "test" { + name = "${azurerm_kubernetes_cluster.test.name}" + resource_group_name = "${azurerm_kubernetes_cluster.test.resource_group_name}" +} +`, r) +} + func testAccDataSourceAzureRMKubernetesCluster_addOnProfileRouting(rInt int, clientId string, clientSecret string, location string) string { r := testAccAzureRMKubernetesCluster_addonProfileRouting(rInt, clientId, clientSecret, location) return fmt.Sprintf(` @@ -584,3 +704,39 @@ data "azurerm_kubernetes_cluster" "test" { } `, r) } + +func testAccDataSourceAzureRMKubernetesCluster_autoScalingNoAvailabilityZones(rInt int, clientId string, clientSecret string, location string) string { + r := testAccAzureRMKubernetesCluster_autoscaleNoAvailabilityZones(rInt, clientId, clientSecret, location) + return fmt.Sprintf(` +%s + +data "azurerm_kubernetes_cluster" "test" { + name = "${azurerm_kubernetes_cluster.test.name}" + resource_group_name = "${azurerm_kubernetes_cluster.test.resource_group_name}" +} +`, r) +} + +func testAccDataSourceAzureRMKubernetesCluster_autoScalingWithAvailabilityZones(rInt int, clientId string, clientSecret string, location string) string { + r := testAccAzureRMKubernetesCluster_autoscaleWithAvailabilityZones(rInt, clientId, clientSecret, location) + return fmt.Sprintf(` +%s + +data "azurerm_kubernetes_cluster" "test" { + name = "${azurerm_kubernetes_cluster.test.name}" + resource_group_name = "${azurerm_kubernetes_cluster.test.resource_group_name}" +} +`, r) +} + +func testAccDataSourceAzureRMKubernetesCluster_nodeTaints(rInt int, clientId string, clientSecret string, location string) string { + r := testAccAzureRMKubernetesCluster_nodeTaints(rInt, clientId, clientSecret, location) + return fmt.Sprintf(` +%s + +data "azurerm_kubernetes_cluster" "test" { + name = "${azurerm_kubernetes_cluster.test.name}" + resource_group_name = "${azurerm_kubernetes_cluster.test.resource_group_name}" +} +`, r) +} diff --git a/azurerm/data_source_loadbalancer.go b/azurerm/data_source_loadbalancer.go index d26d4522bb9d..a6d737baf14b 100644 --- a/azurerm/data_source_loadbalancer.go +++ b/azurerm/data_source_loadbalancer.go @@ -3,10 +3,11 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -79,7 +80,7 @@ func dataSourceArmLoadBalancer() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -88,7 +89,7 @@ func dataSourceArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) err name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name, "") @@ -135,9 +136,7 @@ func dataSourceArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) err } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenLoadBalancerDataSourceFrontendIpConfiguration(ipConfigs *[]network.FrontendIPConfiguration) []interface{} { diff --git a/azurerm/data_source_log_analytics_workspace.go b/azurerm/data_source_log_analytics_workspace.go index 1caa30216051..126671c1a88b 100644 --- a/azurerm/data_source_log_analytics_workspace.go +++ b/azurerm/data_source_log_analytics_workspace.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -54,10 +55,9 @@ func dataSourceLogAnalyticsWorkspace() *schema.Resource { Sensitive: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } - } func dataSourceLogAnalyticsWorkspaceRead(d *schema.ResourceData, meta interface{}) error { @@ -98,6 +98,5 @@ func dataSourceLogAnalyticsWorkspaceRead(d *schema.ResourceData, meta interface{ d.Set("secondary_shared_key", sharedKeys.SecondarySharedKey) } - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_logic_app_workflow.go b/azurerm/data_source_logic_app_workflow.go index a37d32fc1634..483adf994ce3 100644 --- a/azurerm/data_source_logic_app_workflow.go +++ b/azurerm/data_source_logic_app_workflow.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/logic/mgmt/2016-06-01/logic" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -28,6 +29,9 @@ func dataSourceArmLogicAppWorkflow() *schema.Resource { "parameters": { Type: schema.TypeMap, Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "workflow_schema": { @@ -40,7 +44,7 @@ func dataSourceArmLogicAppWorkflow() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), "access_endpoint": { Type: schema.TypeString, @@ -50,7 +54,7 @@ func dataSourceArmLogicAppWorkflow() *schema.Resource { } } func dataSourceArmLogicAppWorkflowRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -87,9 +91,7 @@ func dataSourceArmLogicAppWorkflowRead(d *schema.ResourceData, meta interface{}) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenLogicAppDataSourceWorkflowParameters(input map[string]*logic.WorkflowParameter) map[string]interface{} { diff --git a/azurerm/data_source_managed_disk.go b/azurerm/data_source_managed_disk.go index fb232bb6e451..964314279cf3 100644 --- a/azurerm/data_source_managed_disk.go +++ b/azurerm/data_source_managed_disk.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -52,13 +53,23 @@ func dataSourceArmManagedDisk() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "disk_iops_read_write": { + Type: schema.TypeInt, + Computed: true, + }, + + "disk_mbps_read_write": { + Type: schema.TypeInt, + Computed: true, + }, + + "tags": tags.Schema(), }, } } func dataSourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).diskClient + client := meta.(*ArmClient).compute.DisksClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -79,12 +90,10 @@ func dataSourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) erro } if props := resp.DiskProperties; props != nil { - if diskSize := props.DiskSizeGB; diskSize != nil { - d.Set("disk_size_gb", *diskSize) - } - if osType := props.OsType; osType != "" { - d.Set("os_type", string(osType)) - } + d.Set("disk_size_gb", props.DiskSizeGB) + d.Set("disk_iops_read_write", props.DiskIOPSReadWrite) + d.Set("disk_mbps_read_write", props.DiskMBpsReadWrite) + d.Set("os_type", props.OsType) } if resp.CreationData != nil { @@ -93,7 +102,5 @@ func dataSourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) erro d.Set("zones", resp.Zones) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_managed_disk_test.go b/azurerm/data_source_managed_disk_test.go index edfae14365c1..2b9d221d8272 100644 --- a/azurerm/data_source_managed_disk_test.go +++ b/azurerm/data_source_managed_disk_test.go @@ -38,6 +38,31 @@ func TestAccDataSourceAzureRMManagedDisk_basic(t *testing.T) { }) } +func TestAccDataSourceAzureRMManagedDisk_basic_withUltraSSD(t *testing.T) { + dataSourceName := "data.azurerm_managed_disk.test" + location := "eastus2" + ri := tf.AccRandTimeInt() + + name := fmt.Sprintf("acctestmanageddisk-%d", ri) + resourceGroupName := fmt.Sprintf("acctestRG-%d", ri) + + config := testAccDataSourceAzureRMManagedDisk_basic_withUltraSSD(name, resourceGroupName, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "disk_iops_read_write", "101"), + resource.TestCheckResourceAttr(dataSourceName, "disk_mbps_read_write", "10"), + ), + }, + }, + }) +} + func testAccDataSourceAzureRMManagedDiskBasic(name string, resourceGroupName string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -65,3 +90,33 @@ data "azurerm_managed_disk" "test" { } `, resourceGroupName, location, name) } + +func testAccDataSourceAzureRMManagedDisk_basic_withUltraSSD(name string, resourceGroupName string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "%s" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "UltraSSD_LRS" + create_option = "Empty" + disk_size_gb = "4" + disk_iops_read_write = "101" + disk_mbps_read_write = "10" + zones = ["1"] + + tags = { + environment = "acctest" + } +} + +data "azurerm_managed_disk" "test" { + name = "${azurerm_managed_disk.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, resourceGroupName, location, name) +} diff --git a/azurerm/data_source_management_group.go b/azurerm/data_source_management_group.go index d10c20ab0c03..a1bb0bdf1561 100644 --- a/azurerm/data_source_management_group.go +++ b/azurerm/data_source_management_group.go @@ -39,7 +39,7 @@ func dataSourceArmManagementGroup() *schema.Resource { } func dataSourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementGroupsClient + client := meta.(*ArmClient).managementGroups.GroupsClient ctx := meta.(*ArmClient).StopContext groupId := d.Get("group_id").(string) @@ -79,7 +79,6 @@ func dataSourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) } } d.Set("parent_management_group_id", parentId) - } return nil @@ -98,7 +97,7 @@ func flattenArmManagementGroupDataSourceSubscriptionIds(input *[]managementgroup id, err := parseManagementGroupSubscriptionID(*child.ID) if err != nil { - return nil, fmt.Errorf("Unable to parse child subscription ID %+v", err) + return nil, fmt.Errorf("Unable to parse child Subscription ID %+v", err) } if id != nil { diff --git a/azurerm/data_source_maps_account.go b/azurerm/data_source_maps_account.go new file mode 100644 index 000000000000..55f446a185ea --- /dev/null +++ b/azurerm/data_source_maps_account.go @@ -0,0 +1,89 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maps" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmMapsAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMapsAccountRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: maps.ValidateName(), + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "sku_name": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + + "x_ms_client_id": { + Type: schema.TypeString, + Computed: true, + }, + + "primary_access_key": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + + "secondary_access_key": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + } +} + +func dataSourceMapsAccountRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + client := meta.(*ArmClient).maps.AccountsClient + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Maps Account %q was not found in Resource Group %q", name, resourceGroup) + } + + return fmt.Errorf("Error making Read request on Maps Account %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.SetId(*resp.ID) + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + if sku := resp.Sku; sku != nil { + d.Set("sku_name", sku.Name) + } + if props := resp.Properties; props != nil { + d.Set("x_ms_client_id", props.XMsClientID) + } + + keysResp, err := client.ListKeys(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error reading Access Keys request for Maps Account %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("primary_access_key", keysResp.PrimaryKey) + d.Set("secondary_access_key", keysResp.SecondaryKey) + + return tags.FlattenAndSet(d, resp.Tags) +} diff --git a/azurerm/data_source_maps_account_test.go b/azurerm/data_source_maps_account_test.go new file mode 100644 index 000000000000..e3fd12ed9fe6 --- /dev/null +++ b/azurerm/data_source_maps_account_test.go @@ -0,0 +1,48 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceAzureRMMapsAccount_basic(t *testing.T) { + dataSourceName := "data.azurerm_maps_account.test" + rInt := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMMapsAccount_basic(rInt, location), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "id"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(dataSourceName, "tags.environment", "testing"), + resource.TestCheckResourceAttr(dataSourceName, "sku_name", "s0"), + resource.TestCheckResourceAttrSet(dataSourceName, "x_ms_client_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "primary_access_key"), + resource.TestCheckResourceAttrSet(dataSourceName, "secondary_access_key"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMMapsAccount_basic(rInt int, location string) string { + template := testAccAzureRMMapsAccount_tags(rInt, location) + return fmt.Sprintf(` +%s + +data "azurerm_maps_account" "test" { + name = azurerm_maps_account.test.name + resource_group_name = azurerm_resource_group.test.name +} +`, template) +} diff --git a/azurerm/data_source_monitor_action_group.go b/azurerm/data_source_monitor_action_group.go index 5adc39fb8656..b5b5fb22b15f 100644 --- a/azurerm/data_source_monitor_action_group.go +++ b/azurerm/data_source_monitor_action_group.go @@ -90,7 +90,7 @@ func dataSourceArmMonitorActionGroup() *schema.Resource { } func dataSourceArmMonitorActionGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActionGroupsClient + client := meta.(*ArmClient).monitor.ActionGroupsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_monitor_diagnostic_categories.go b/azurerm/data_source_monitor_diagnostic_categories.go index 473458aff2d5..6c610774ed40 100644 --- a/azurerm/data_source_monitor_diagnostic_categories.go +++ b/azurerm/data_source_monitor_diagnostic_categories.go @@ -37,7 +37,7 @@ func dataSourceArmMonitorDiagnosticCategories() *schema.Resource { } func dataSourceArmMonitorDiagnosticCategoriesRead(d *schema.ResourceData, meta interface{}) error { - categoriesClient := meta.(*ArmClient).monitorDiagnosticSettingsCategoryClient + categoriesClient := meta.(*ArmClient).monitor.DiagnosticSettingsCategoryClient ctx := meta.(*ArmClient).StopContext actualResourceId := d.Get("resource_id").(string) diff --git a/azurerm/data_source_monitor_log_profile.go b/azurerm/data_source_monitor_log_profile.go index fd6e8526a3a9..fe939e509640 100644 --- a/azurerm/data_source_monitor_log_profile.go +++ b/azurerm/data_source_monitor_log_profile.go @@ -59,7 +59,7 @@ func dataSourceArmMonitorLogProfile() *schema.Resource { } func dataSourceArmLogProfileRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorLogProfilesClient + client := meta.(*ArmClient).monitor.LogProfilesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_mssql_elasticpool.go b/azurerm/data_source_mssql_elasticpool.go new file mode 100644 index 000000000000..423a5b6a5c86 --- /dev/null +++ b/azurerm/data_source_mssql_elasticpool.go @@ -0,0 +1,102 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmMsSqlElasticpool() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmMsSqlElasticpoolRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "server_name": { + Type: schema.TypeString, + Required: true, + }, + + "location": azure.SchemaLocationForDataSource(), + + "max_size_bytes": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_size_gb": { + Type: schema.TypeFloat, + Computed: true, + }, + + "per_db_min_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + + "per_db_max_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + + "tags": tags.SchemaDataSource(), + + "zone_redundant": { + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func dataSourceArmMsSqlElasticpoolRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mssql.ElasticPoolsClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + elasticPoolName := d.Get("name").(string) + serverName := d.Get("server_name").(string) + + resp, err := client.Get(ctx, resourceGroup, serverName, elasticPoolName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: Elasticpool %q (Resource Group %q, SQL Server %q) was not found", elasticPoolName, resourceGroup, serverName) + } + + return fmt.Errorf("Error making Read request on AzureRM Elasticpool %s (Resource Group %q, SQL Server %q): %+v", elasticPoolName, resourceGroup, serverName, err) + } + + if id := resp.ID; id != nil { + d.SetId(*resp.ID) + } + d.Set("name", elasticPoolName) + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if props := resp.ElasticPoolProperties; props != nil { + d.Set("max_size_gb", float64(*props.MaxSizeBytes/int64(1073741824))) + d.Set("max_size_bytes", props.MaxSizeBytes) + + d.Set("zone_redundant", props.ZoneRedundant) + + if perDbSettings := props.PerDatabaseSettings; perDbSettings != nil { + d.Set("per_db_min_capacity", perDbSettings.MinCapacity) + d.Set("per_db_max_capacity", perDbSettings.MaxCapacity) + } + } + + return tags.FlattenAndSet(d, resp.Tags) +} diff --git a/azurerm/data_source_mssql_elasticpool_test.go b/azurerm/data_source_mssql_elasticpool_test.go new file mode 100644 index 000000000000..64d0cdcde7ed --- /dev/null +++ b/azurerm/data_source_mssql_elasticpool_test.go @@ -0,0 +1,83 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceAzureRMMsSqlElasticPool_basic(t *testing.T) { + dataSourceName := "data.azurerm_mssql_elasticpool.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMsSqlElasticPoolDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMMsSqlElasticPool_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlElasticPoolExists(dataSourceName), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "server_name"), + resource.TestCheckResourceAttr(dataSourceName, "location", location), + resource.TestCheckResourceAttr(dataSourceName, "max_size_gb", "50"), + resource.TestCheckResourceAttr(dataSourceName, "per_db_min_capacity", "0"), + resource.TestCheckResourceAttr(dataSourceName, "per_db_max_capacity", "4"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_redundant", "false"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMMsSqlElasticPool_basic(rInt int, location string) string { + return fmt.Sprintf(` + resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%s" + } + + resource "azurerm_sql_server" "test" { + name = "acctest%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "4dm1n157r470r" + administrator_login_password = "4-v3ry-53cr37-p455w0rd" + } + + resource "azurerm_mssql_elasticpool" "test" { + name = "acctest-pool-dtu-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + server_name = "${azurerm_sql_server.test.name}" + max_size_gb = 50 + zone_redundant = false + + sku { + name = "GP_Gen5" + tier = "GeneralPurpose" + capacity = 4 + family = "Gen5" + } + + per_database_settings { + min_capacity = 0 + max_capacity = 4 + } + } + + data "azurerm_mssql_elasticpool" "test" { + name = "${azurerm_mssql_elasticpool.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test.name}" + } + `, rInt, location) +} diff --git a/azurerm/data_source_network_ddos_protection_plan.go b/azurerm/data_source_network_ddos_protection_plan.go new file mode 100644 index 000000000000..4432a03dd18a --- /dev/null +++ b/azurerm/data_source_network_ddos_protection_plan.go @@ -0,0 +1,69 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceNetworkDDoSProtectionPlan() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNetworkDDoSProtectionPlanRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "location": azure.SchemaLocationForDataSource(), + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "virtual_network_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "tags": tags.Schema(), + }, + } +} + +func dataSourceNetworkDDoSProtectionPlanRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).network.DDOSProtectionPlansClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + plan, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(plan.Response) { + return fmt.Errorf("Error DDoS Protection Plan %q (Resource Group %q) was not found", name, resourceGroup) + } + + return fmt.Errorf("Error making Read request on DDoS Protection Plan %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", plan.Name) + d.Set("resource_group_name", resourceGroup) + if location := plan.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if props := plan.DdosProtectionPlanPropertiesFormat; props != nil { + vNetIDs := flattenArmNetworkDDoSProtectionPlanVirtualNetworkIDs(props.VirtualNetworks) + if err := d.Set("virtual_network_ids", vNetIDs); err != nil { + return fmt.Errorf("Error setting `virtual_network_ids`: %+v", err) + } + } + + return tags.FlattenAndSet(d, plan.Tags) +} diff --git a/azurerm/data_source_network_ddos_protection_plan_test.go b/azurerm/data_source_network_ddos_protection_plan_test.go new file mode 100644 index 000000000000..93e79be97719 --- /dev/null +++ b/azurerm/data_source_network_ddos_protection_plan_test.go @@ -0,0 +1,41 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func testAccAzureRMNetworkDDoSProtectionPlanDataSource_basic(t *testing.T) { + dsn := "azurerm_network_ddos_protection_plan.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkDDoSProtectionPlanDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkDDoSProtectionPlanDataSource_basicConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkDDoSProtectionPlanExists(dsn), + resource.TestCheckResourceAttrSet(dsn, "virtual_network_ids.#"), + ), + }, + }, + }) +} + +func testAccAzureRMNetworkDDoSProtectionPlanDataSource_basicConfig(rInt int, location string) string { + return fmt.Sprintf(` + %s + +data "azurerm_network_ddos_protection_plan" "test" { + name = "${azurerm_network_ddos_protection_plan.test.name}" + resource_group_name = "${azurerm_network_ddos_protection_plan.test.resource_group_name}" +} +`, testAccAzureRMNetworkDDoSProtectionPlan_basicConfig(rInt, location)) +} diff --git a/azurerm/data_source_network_interface.go b/azurerm/data_source_network_interface.go index 6562dfd17c1e..2c85470c4891 100644 --- a/azurerm/data_source_network_interface.go +++ b/azurerm/data_source_network_interface.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -168,13 +169,13 @@ func dataSourceArmNetworkInterface() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -197,15 +198,12 @@ func dataSourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) if iface.IPConfigurations != nil && len(*iface.IPConfigurations) > 0 { configs := *iface.IPConfigurations - if configs[0].InterfaceIPConfigurationPropertiesFormat != nil { - privateIPAddress := configs[0].InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress - d.Set("private_ip_address", *privateIPAddress) - } + d.Set("private_ip_address", configs[0].InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress) addresses := make([]interface{}, 0) for _, config := range configs { if config.InterfaceIPConfigurationPropertiesFormat != nil { - addresses = append(addresses, *config.InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress) + addresses = append(addresses, config.InterfaceIPConfigurationPropertiesFormat.PrivateIPAddress) } } @@ -219,7 +217,7 @@ func dataSourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) } if iface.VirtualMachine != nil { - d.Set("virtual_machine_id", *iface.VirtualMachine.ID) + d.Set("virtual_machine_id", iface.VirtualMachine.ID) } else { d.Set("virtual_machine_id", "") } @@ -256,7 +254,5 @@ func dataSourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) d.Set("enable_ip_forwarding", resp.EnableIPForwarding) d.Set("enable_accelerated_networking", resp.EnableAcceleratedNetworking) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_network_security_group.go b/azurerm/data_source_network_security_group.go index 3f9170391cc3..1bce3f009c39 100644 --- a/azurerm/data_source_network_security_group.go +++ b/azurerm/data_source_network_security_group.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -121,13 +122,13 @@ func dataSourceArmNetworkSecurityGroup() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).network.SecurityGroupClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -156,7 +157,5 @@ func dataSourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interfac } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_network_watcher.go b/azurerm/data_source_network_watcher.go index 17dd3a1459d1..e5332b03a2b2 100644 --- a/azurerm/data_source_network_watcher.go +++ b/azurerm/data_source_network_watcher.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -22,13 +23,13 @@ func dataSourceArmNetworkWatcher() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), "location": azure.SchemaLocationForDataSource(), - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).watcherClient + client := meta.(*ArmClient).network.WatcherClient name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) @@ -49,7 +50,5 @@ func dataSourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) e if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_notification_hub.go b/azurerm/data_source_notification_hub.go index 6b37a78a68f7..e712185d048f 100644 --- a/azurerm/data_source_notification_hub.go +++ b/azurerm/data_source_notification_hub.go @@ -75,7 +75,7 @@ func dataSourceNotificationHub() *schema.Resource { // NOTE: skipping tags as there's a bug in the API where the Keys for Tags are returned in lower-case // Azure Rest API Specs issue: https://github.com/Azure/azure-sdk-for-go/issues/2239 - //"tags": tagsForDataSourceSchema(), + // "tags": tags.SchemaDataSource(), }, } } diff --git a/azurerm/data_source_notification_hub_namespace.go b/azurerm/data_source_notification_hub_namespace.go index b7b9314cc614..6398b9dad42b 100644 --- a/azurerm/data_source_notification_hub_namespace.go +++ b/azurerm/data_source_notification_hub_namespace.go @@ -48,7 +48,7 @@ func dataSourceNotificationHubNamespace() *schema.Resource { // NOTE: skipping tags as there's a bug in the API where the Keys for Tags are returned in lower-case // Azure Rest API Specs issue: https://github.com/Azure/azure-sdk-for-go/issues/2239 - //"tags": tagsForDataSourceSchema(), + // "tags": tags.SchemaDataSource(), "servicebus_endpoint": { Type: schema.TypeString, diff --git a/azurerm/data_source_platform_image.go b/azurerm/data_source_platform_image.go index ad8d2ef4465e..8772dab88df9 100644 --- a/azurerm/data_source_platform_image.go +++ b/azurerm/data_source_platform_image.go @@ -38,7 +38,7 @@ func dataSourceArmPlatformImage() *schema.Resource { } func dataSourceArmPlatformImageRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).vmImageClient + client := meta.(*ArmClient).compute.VMImageClient ctx := meta.(*ArmClient).StopContext location := azure.NormalizeLocation(d.Get("location").(string)) diff --git a/azurerm/data_source_policy_definition_test.go b/azurerm/data_source_policy_definition_test.go index 0cd94b0e486d..d7163daebd40 100644 --- a/azurerm/data_source_policy_definition_test.go +++ b/azurerm/data_source_policy_definition_test.go @@ -79,7 +79,6 @@ data "azurerm_policy_definition" "test" { func testAccDataSourceBuiltInPolicyDefinitionAtManagementGroup(name string) string { return fmt.Sprintf(` - data "azurerm_client_config" "current" {} data "azurerm_policy_definition" "test" { @@ -97,7 +96,7 @@ resource "azurerm_policy_definition" "test_policy" { mode = "All" display_name = "acctestpol-%d" - policy_rule = < 0 { - pcs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *accessKeys[0].Value, endpointSuffix) - d.Set("primary_connection_string", pcs) - } + if accessKeys := accountKeys; accessKeys != nil { + storageAccessKeys := *accessKeys + if len(storageAccessKeys) > 0 { + pcs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *storageAccessKeys[0].Value, endpointSuffix) + d.Set("primary_connection_string", pcs) + } - if len(accessKeys) > 1 { - scs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *accessKeys[1].Value, endpointSuffix) - d.Set("secondary_connection_string", scs) + if len(storageAccessKeys) > 1 { + scs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *storageAccessKeys[1].Value, endpointSuffix) + d.Set("secondary_connection_string", scs) + } } if err := flattenAndSetAzureRmStorageAccountPrimaryEndpoints(d, props.PrimaryEndpoints); err != nil { return fmt.Errorf("error setting primary endpoints and hosts for blob, queue, table and file: %+v", err) } - var primaryBlobConnectStr string - if v := props.PrimaryEndpoints; v != nil { - primaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, accessKeys[0].Value) + if accessKeys := accountKeys; accessKeys != nil { + var primaryBlobConnectStr string + if v := props.PrimaryEndpoints; v != nil { + primaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, (*accessKeys)[0].Value) + } + d.Set("primary_blob_connection_string", primaryBlobConnectStr) } - d.Set("primary_blob_connection_string", primaryBlobConnectStr) if err := flattenAndSetAzureRmStorageAccountSecondaryEndpoints(d, props.SecondaryEndpoints); err != nil { return fmt.Errorf("error setting secondary endpoints and hosts for blob, queue, table: %+v", err) } - var secondaryBlobConnectStr string - if v := props.SecondaryEndpoints; v != nil { - secondaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, accessKeys[1].Value) + if accessKeys := accountKeys; accessKeys != nil { + var secondaryBlobConnectStr string + if v := props.SecondaryEndpoints; v != nil { + secondaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, (*accessKeys)[1].Value) + } + d.Set("secondary_blob_connection_string", secondaryBlobConnectStr) } - d.Set("secondary_blob_connection_string", secondaryBlobConnectStr) } - d.Set("primary_access_key", accessKeys[0].Value) - d.Set("secondary_access_key", accessKeys[1].Value) - - flattenAndSetTags(d, resp.Tags) + if accessKeys := accountKeys; accessKeys != nil { + storageAccountKeys := *accessKeys + d.Set("primary_access_key", storageAccountKeys[0].Value) + d.Set("secondary_access_key", storageAccountKeys[1].Value) + } - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_storage_account_blob_container_sas.go b/azurerm/data_source_storage_account_blob_container_sas.go new file mode 100644 index 000000000000..8e0efbe50b39 --- /dev/null +++ b/azurerm/data_source_storage_account_blob_container_sas.go @@ -0,0 +1,204 @@ +package azurerm + +import ( + "crypto/sha256" + "encoding/hex" + + "github.com/hashicorp/go-azure-helpers/storage" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" +) + +func dataSourceArmStorageAccountBlobContainerSharedAccessSignature() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmStorageContainerSasRead, + + Schema: map[string]*schema.Schema{ + "connection_string": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "container_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "https_only": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "ip_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.SharedAccessSignatureIP, + }, + + "start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.ISO8601DateTime, + }, + + "expiry": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.ISO8601DateTime, + }, + + "permissions": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "read": { + Type: schema.TypeBool, + Required: true, + }, + + "add": { + Type: schema.TypeBool, + Required: true, + }, + + "create": { + Type: schema.TypeBool, + Required: true, + }, + + "write": { + Type: schema.TypeBool, + Required: true, + }, + + "delete": { + Type: schema.TypeBool, + Required: true, + }, + + "list": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, + + "cache_control": { + Type: schema.TypeString, + Optional: true, + }, + + "content_disposition": { + Type: schema.TypeString, + Optional: true, + }, + + "content_encoding": { + Type: schema.TypeString, + Optional: true, + }, + + "content_language": { + Type: schema.TypeString, + Optional: true, + }, + + "content_type": { + Type: schema.TypeString, + Optional: true, + }, + + "sas": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + } +} + +func dataSourceArmStorageContainerSasRead(d *schema.ResourceData, _ interface{}) error { + connString := d.Get("connection_string").(string) + containerName := d.Get("container_name").(string) + httpsOnly := d.Get("https_only").(bool) + ip := d.Get("ip_address").(string) + start := d.Get("start").(string) + expiry := d.Get("expiry").(string) + permissionsIface := d.Get("permissions").([]interface{}) + + // response headers + cacheControl := d.Get("cache_control").(string) + contentDisposition := d.Get("content_disposition").(string) + contentEncoding := d.Get("content_encoding").(string) + contentLanguage := d.Get("content_language").(string) + contentType := d.Get("content_type").(string) + + permissions := buildContainerPermissionsString(permissionsIface[0].(map[string]interface{})) + + // Parse the connection string + kvp, err := storage.ParseAccountSASConnectionString(connString) + if err != nil { + return err + } + + // Create the string to sign with the key... + accountName := kvp[connStringAccountNameKey] + accountKey := kvp[connStringAccountKeyKey] + var signedProtocol = "https,http" + if httpsOnly { + signedProtocol = "https" + } + signedIp := ip + signedIdentifier := "" + signedSnapshotTime := "" + + sasToken, err := storage.ComputeContainerSASToken(permissions, start, expiry, accountName, accountKey, + containerName, signedIdentifier, signedIp, signedProtocol, signedSnapshotTime, cacheControl, + contentDisposition, contentEncoding, contentLanguage, contentType) + if err != nil { + return err + } + + d.Set("sas", sasToken) + tokenHash := sha256.Sum256([]byte(sasToken)) + d.SetId(hex.EncodeToString(tokenHash[:])) + + return nil +} + +func buildContainerPermissionsString(perms map[string]interface{}) string { + retVal := "" + + if val, pres := perms["read"].(bool); pres && val { + retVal += "r" + } + + if val, pres := perms["add"].(bool); pres && val { + retVal += "a" + } + + if val, pres := perms["create"].(bool); pres && val { + retVal += "c" + } + + if val, pres := perms["write"].(bool); pres && val { + retVal += "w" + } + + if val, pres := perms["delete"].(bool); pres && val { + retVal += "d" + } + + if val, pres := perms["list"].(bool); pres && val { + retVal += "l" + } + + return retVal +} diff --git a/azurerm/data_source_storage_account_blob_container_sas_test.go b/azurerm/data_source_storage_account_blob_container_sas_test.go new file mode 100644 index 000000000000..e807906db6d5 --- /dev/null +++ b/azurerm/data_source_storage_account_blob_container_sas_test.go @@ -0,0 +1,123 @@ +package azurerm + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceArmStorageAccountBlobContainerSas_basic(t *testing.T) { + dataSourceName := "data.azurerm_storage_account_blob_container_sas.test" + rInt := tf.AccRandTimeInt() + rString := acctest.RandString(4) + location := testLocation() + utcNow := time.Now().UTC() + startDate := utcNow.Format(time.RFC3339) + endDate := utcNow.Add(time.Hour * 24).Format(time.RFC3339) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMStorageAccountBlobContainerSas_basic(rInt, rString, location, startDate, endDate), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "https_only", "true"), + resource.TestCheckResourceAttr(dataSourceName, "start", startDate), + resource.TestCheckResourceAttr(dataSourceName, "expiry", endDate), + resource.TestCheckResourceAttr(dataSourceName, "ip_address", "168.1.5.65"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.read", "true"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.add", "true"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.create", "false"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.write", "false"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.delete", "true"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.list", "true"), + resource.TestCheckResourceAttr(dataSourceName, "cache_control", "max-age=5"), + resource.TestCheckResourceAttr(dataSourceName, "content_disposition", "inline"), + resource.TestCheckResourceAttr(dataSourceName, "content_encoding", "deflate"), + resource.TestCheckResourceAttr(dataSourceName, "content_language", "en-US"), + resource.TestCheckResourceAttr(dataSourceName, "content_type", "application/json"), + resource.TestCheckResourceAttrSet(dataSourceName, "sas"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMStorageAccountBlobContainerSas_basic(rInt int, rString string, location string, startDate string, endDate string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestsa-%d" + location = "%s" +} + +resource "azurerm_storage_account" "storage" { + name = "acctestsads%s" + resource_group_name = "${azurerm_resource_group.rg.name}" + + location = "${azurerm_resource_group.rg.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "container" { + name = "sas-test" + resource_group_name = "${azurerm_resource_group.rg.name}" + storage_account_name = "${azurerm_storage_account.storage.name}" + container_access_type = "private" +} + +data "azurerm_storage_account_blob_container_sas" "test" { + connection_string = "${azurerm_storage_account.storage.primary_connection_string}" + container_name = "${azurerm_storage_container.container.name}" + https_only = true + + ip_address = "168.1.5.65" + + start = "%s" + expiry = "%s" + + permissions { + read = true + add = true + create = false + write = false + delete = true + list = true + } + + cache_control = "max-age=5" + content_disposition = "inline" + content_encoding = "deflate" + content_language = "en-US" + content_type = "application/json" +} +`, rInt, location, rString, startDate, endDate) +} + +func TestAccDataSourceArmStorageAccountBlobContainerSas_permissionsString(t *testing.T) { + testCases := []struct { + input map[string]interface{} + expected string + }{ + {map[string]interface{}{"read": true}, "r"}, + {map[string]interface{}{"add": true}, "a"}, + {map[string]interface{}{"create": true}, "c"}, + {map[string]interface{}{"write": true}, "w"}, + {map[string]interface{}{"delete": true}, "d"}, + {map[string]interface{}{"list": true}, "l"}, + {map[string]interface{}{"add": true, "write": true, "read": true, "delete": true}, "rawd"}, + } + + for _, test := range testCases { + result := buildContainerPermissionsString(test.input) + if test.expected != result { + t.Fatalf("Failed to build resource type string: expected: %s, result: %s", test.expected, result) + } + } +} diff --git a/azurerm/data_source_storage_account_sas.go b/azurerm/data_source_storage_account_sas.go index f34ee83072e3..17eee47d45d6 100644 --- a/azurerm/data_source_storage_account_sas.go +++ b/azurerm/data_source_storage_account_sas.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/go-azure-helpers/storage" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" ) const ( @@ -99,16 +100,18 @@ func dataSourceArmStorageAccountSharedAccessSignature() *schema.Resource { // Always in UTC and must be ISO-8601 format "start": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ISO8601DateTime, }, // Always in UTC and must be ISO-8601 format "expiry": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ISO8601DateTime, }, "permissions": { @@ -176,11 +179,9 @@ func dataSourceArmStorageAccountSharedAccessSignature() *schema.Resource { }, }, } - } func dataSourceArmStorageAccountSasRead(d *schema.ResourceData, _ interface{}) error { - connString := d.Get("connection_string").(string) httpsOnly := d.Get("https_only").(bool) resourceTypesIface := d.Get("resource_types").([]interface{}) diff --git a/azurerm/data_source_storage_account_sas_test.go b/azurerm/data_source_storage_account_sas_test.go index ade1871226ea..5f2b890e93bb 100644 --- a/azurerm/data_source_storage_account_sas_test.go +++ b/azurerm/data_source_storage_account_sas_test.go @@ -127,7 +127,6 @@ func TestAccDataSourceArmStorageAccountSas_servicesString(t *testing.T) { t.Fatalf("Failed to build resource type string: expected: %s, result: %s", test.expected, result) } } - } func TestAccDataSourceArmStorageAccountSas_permissionsString(t *testing.T) { diff --git a/azurerm/data_source_storage_account_test.go b/azurerm/data_source_storage_account_test.go index c8cd99423097..3bfc2b93c5a3 100644 --- a/azurerm/data_source_storage_account_test.go +++ b/azurerm/data_source_storage_account_test.go @@ -38,6 +38,37 @@ func TestAccDataSourceAzureRMStorageAccount_basic(t *testing.T) { }) } +func TestAccDataSourceAzureRMStorageAccount_withWriteLock(t *testing.T) { + dataSourceName := "data.azurerm_storage_account.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMStorageAccount_basicWriteLock(ri, rs, location), + }, + { + Config: testAccDataSourceAzureRMStorageAccount_basicWriteLockWithDataSource(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "account_tier", "Standard"), + resource.TestCheckResourceAttr(dataSourceName, "account_replication_type", "LRS"), + resource.TestCheckResourceAttr(dataSourceName, "primary_connection_string", ""), + resource.TestCheckResourceAttr(dataSourceName, "secondary_connection_string", ""), + resource.TestCheckResourceAttr(dataSourceName, "primary_blob_connection_string", ""), + resource.TestCheckResourceAttr(dataSourceName, "secondary_blob_connection_string", ""), + resource.TestCheckResourceAttr(dataSourceName, "primary_access_key", ""), + resource.TestCheckResourceAttr(dataSourceName, "secondary_access_key", ""), + ), + }, + }, + }) +} + func testAccDataSourceAzureRMStorageAccount_basic(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -60,6 +91,19 @@ resource "azurerm_storage_account" "test" { `, rInt, location, rString) } +func testAccDataSourceAzureRMStorageAccount_basicWriteLock(rInt int, rString string, location string) string { + template := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_storage_account.test.id}" + lock_level = "ReadOnly" +} +`, template, rInt) +} + func testAccDataSourceAzureRMStorageAccount_basicWithDataSource(rInt int, rString string, location string) string { config := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location) return fmt.Sprintf(` @@ -71,3 +115,15 @@ data "azurerm_storage_account" "test" { } `, config) } + +func testAccDataSourceAzureRMStorageAccount_basicWriteLockWithDataSource(rInt int, rString string, location string) string { + config := testAccDataSourceAzureRMStorageAccount_basicWriteLock(rInt, rString, location) + return fmt.Sprintf(` +%s + +data "azurerm_storage_account" "test" { + name = "${azurerm_storage_account.test.name}" + resource_group_name = "${azurerm_storage_account.test.resource_group_name}" +} +`, config) +} diff --git a/azurerm/data_source_storage_management_policy.go b/azurerm/data_source_storage_management_policy.go new file mode 100644 index 000000000000..64195abfd924 --- /dev/null +++ b/azurerm/data_source_storage_management_policy.go @@ -0,0 +1,132 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceArmStorageManagementPolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmStorageManagementPolicyRead, + + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "rule": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "filters": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prefix_match": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "blob_types": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + }, + }, + "actions": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base_blob": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tier_to_cool_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Computed: true, + }, + "tier_to_archive_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Computed: true, + }, + "delete_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "snapshot": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delete_after_days_since_creation_greater_than": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceArmStorageManagementPolicyRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.ManagementPoliciesClient + ctx := meta.(*ArmClient).StopContext + + storageAccountId := d.Get("storage_account_id").(string) + + rid, err := parseAzureResourceID(storageAccountId) + if err != nil { + return err + } + resourceGroupName := rid.ResourceGroup + storageAccountName := rid.Path["storageAccounts"] + + result, err := client.Get(ctx, resourceGroupName, storageAccountName) + if err != nil { + return err + } + d.SetId(*result.ID) + + if result.Policy != nil { + policy := result.Policy + if policy.Rules != nil { + if err := d.Set("rule", flattenStorageManagementPolicyRules(policy.Rules)); err != nil { + return fmt.Errorf("Error flattening `rule`: %+v", err) + } + } + } + + return nil +} diff --git a/azurerm/data_source_storage_management_policy_test.go b/azurerm/data_source_storage_management_policy_test.go new file mode 100644 index 000000000000..8e8354e44679 --- /dev/null +++ b/azurerm/data_source_storage_management_policy_test.go @@ -0,0 +1,92 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceAzureRMStorageManagementPolicy_basic(t *testing.T) { + dataSourceName := "data.azurerm_storage_management_policy.testpolicy" + ri := tf.AccRandTimeInt() + + rs := acctest.RandString(4) + location := testLocation() + config := testAccDataSourceAzureRMStorageManagementPolicy_basic(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.name", "rule1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.enabled", "true"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.filters.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.filters.0.prefix_match.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.filters.0.prefix_match.3439697764", "container1/prefix1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "10"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "50"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "100"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "rule.0.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "30"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMStorageManagementPolicy_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "BlobStorage" +} + +resource "azurerm_storage_management_policy" "testpolicy" { + storage_account_id = "${azurerm_storage_account.testsa.id}" + + rule { + name = "rule1" + enabled = true + filters { + prefix_match = [ "container1/prefix1" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 10 + tier_to_archive_after_days_since_modification_greater_than = 50 + delete_after_days_since_modification_greater_than = 100 + } + snapshot { + delete_after_days_since_creation_greater_than = 30 + } + } + } +} + +data "azurerm_storage_management_policy" "testpolicy" { + storage_account_id = "${azurerm_storage_management_policy.testpolicy.storage_account_id}" +} +`, rInt, location, rString) +} diff --git a/azurerm/data_source_stream_analytics_job.go b/azurerm/data_source_stream_analytics_job.go index f8b65b9c2707..4106d90cb2d7 100644 --- a/azurerm/data_source_stream_analytics_job.go +++ b/azurerm/data_source_stream_analytics_job.go @@ -72,8 +72,8 @@ func dataSourceArmStreamAnalyticsJob() *schema.Resource { } func dataSourceArmStreamAnalyticsJobRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).streamAnalyticsJobsClient - transformationsClient := meta.(*ArmClient).streamAnalyticsTransformationsClient + client := meta.(*ArmClient).StreamAnalytics.JobsClient + transformationsClient := meta.(*ArmClient).StreamAnalytics.TransformationsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_subnet.go b/azurerm/data_source_subnet.go index 9a6c5e3faf69..aee60e9dce2e 100644 --- a/azurerm/data_source_subnet.go +++ b/azurerm/data_source_subnet.go @@ -61,7 +61,7 @@ func dataSourceArmSubnet() *schema.Resource { } func dataSourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).subnetClient + client := meta.(*ArmClient).network.SubnetsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_subscription.go b/azurerm/data_source_subscription.go index 5d0848467df8..fdce78573fb4 100644 --- a/azurerm/data_source_subscription.go +++ b/azurerm/data_source_subscription.go @@ -17,7 +17,7 @@ func dataSourceArmSubscription() *schema.Resource { func dataSourceArmSubscriptionRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient) - groupClient := client.subscriptionsClient + groupClient := client.Subscription.Client ctx := client.StopContext subscriptionId := d.Get("subscription_id").(string) @@ -31,12 +31,13 @@ func dataSourceArmSubscriptionRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error: Subscription %q was not found", subscriptionId) } - return fmt.Errorf("Error reading subscription: %+v", err) + return fmt.Errorf("Error reading Subscription: %+v", err) } d.SetId(*resp.ID) d.Set("subscription_id", resp.SubscriptionID) d.Set("display_name", resp.DisplayName) + d.Set("tenant_id", resp.TenantID) d.Set("state", resp.State) if resp.SubscriptionPolicies != nil { d.Set("location_placement_id", resp.SubscriptionPolicies.LocationPlacementID) diff --git a/azurerm/data_source_subscription_test.go b/azurerm/data_source_subscription_test.go index 138986346ba4..44c8d179002d 100644 --- a/azurerm/data_source_subscription_test.go +++ b/azurerm/data_source_subscription_test.go @@ -22,6 +22,7 @@ func TestAccDataSourceAzureRMSubscription_current(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "subscription_id"), testCheckAzureRMSubscriptionId(resourceName), resource.TestCheckResourceAttrSet(resourceName, "display_name"), + resource.TestCheckResourceAttrSet(resourceName, "tenant_id"), resource.TestCheckResourceAttr(resourceName, "state", "Enabled"), ), }, @@ -42,6 +43,7 @@ func TestAccDataSourceAzureRMSubscription_specific(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "subscription_id"), testCheckAzureRMSubscriptionId(resourceName), resource.TestCheckResourceAttrSet(resourceName, "display_name"), + resource.TestCheckResourceAttrSet(resourceName, "tenant_id"), resource.TestCheckResourceAttrSet(resourceName, "location_placement_id"), resource.TestCheckResourceAttrSet(resourceName, "quota_id"), resource.TestCheckResourceAttrSet(resourceName, "spending_limit"), diff --git a/azurerm/data_source_subscriptions.go b/azurerm/data_source_subscriptions.go index 9006b0f6eef5..218e8c68f58c 100644 --- a/azurerm/data_source_subscriptions.go +++ b/azurerm/data_source_subscriptions.go @@ -34,7 +34,7 @@ func dataSourceArmSubscriptions() *schema.Resource { func dataSourceArmSubscriptionsRead(d *schema.ResourceData, meta interface{}) error { armClient := meta.(*ArmClient) - subClient := armClient.subscriptionsClient + subClient := armClient.Subscription.Client ctx := armClient.StopContext displayNamePrefix := strings.ToLower(d.Get("display_name_prefix").(string)) @@ -56,6 +56,9 @@ func dataSourceArmSubscriptionsRead(d *schema.ResourceData, meta interface{}) er if v := val.SubscriptionID; v != nil { s["subscription_id"] = *v } + if v := val.TenantID; v != nil { + s["tenant_id"] = *v + } if v := val.DisplayName; v != nil { s["display_name"] = *v } diff --git a/azurerm/data_source_subscriptions_test.go b/azurerm/data_source_subscriptions_test.go index 387ff9ebf4dd..899ccfe2af64 100644 --- a/azurerm/data_source_subscriptions_test.go +++ b/azurerm/data_source_subscriptions_test.go @@ -18,6 +18,7 @@ func TestAccDataSourceAzureRMSubscriptions_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "subscriptions.0.subscription_id"), resource.TestCheckResourceAttrSet(resourceName, "subscriptions.0.display_name"), + resource.TestCheckResourceAttrSet(resourceName, "subscriptions.0.tenant_id"), resource.TestCheckResourceAttrSet(resourceName, "subscriptions.0.state"), ), }, diff --git a/azurerm/data_source_traffic_manager_geographical_location.go b/azurerm/data_source_traffic_manager_geographical_location.go index 42299ff5bf09..fe176aaebeeb 100644 --- a/azurerm/data_source_traffic_manager_geographical_location.go +++ b/azurerm/data_source_traffic_manager_geographical_location.go @@ -21,7 +21,7 @@ func dataSourceArmTrafficManagerGeographicalLocation() *schema.Resource { } func dataSourceArmTrafficManagerGeographicalLocationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).trafficManagerGeographialHierarchiesClient + client := meta.(*ArmClient).TrafficManager.GeographialHierarchiesClient ctx := meta.(*ArmClient).StopContext results, err := client.GetDefault(ctx) diff --git a/azurerm/data_source_user_assigned_identity.go b/azurerm/data_source_user_assigned_identity.go index 9d6e08269875..9f541d6cfdae 100644 --- a/azurerm/data_source_user_assigned_identity.go +++ b/azurerm/data_source_user_assigned_identity.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -17,7 +18,7 @@ func dataSourceArmUserAssignedIdentity() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringLenBetween(1, 24), + ValidateFunc: validation.StringLenBetween(3, 128), }, "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), @@ -34,7 +35,7 @@ func dataSourceArmUserAssignedIdentity() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } @@ -71,7 +72,5 @@ func dataSourceArmUserAssignedIdentityRead(d *schema.ResourceData, meta interfac } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/data_source_virtual_machine.go b/azurerm/data_source_virtual_machine.go index 47fbb02d8d2f..cf880da80042 100644 --- a/azurerm/data_source_virtual_machine.go +++ b/azurerm/data_source_virtual_machine.go @@ -25,7 +25,7 @@ func dataSourceArmVirtualMachine() *schema.Resource { } func dataSourceArmVirtualMachineRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).vmClient + client := meta.(*ArmClient).compute.VMClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) diff --git a/azurerm/data_source_virtual_network.go b/azurerm/data_source_virtual_network.go index c7c72ee5ae59..77ac072fefb6 100644 --- a/azurerm/data_source_virtual_network.go +++ b/azurerm/data_source_virtual_network.go @@ -3,7 +3,7 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" @@ -22,6 +22,8 @@ func dataSourceArmVirtualNetwork() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + "location": azure.SchemaLocationForDataSource(), + "address_spaces": { Type: schema.TypeList, Computed: true, @@ -58,13 +60,16 @@ func dataSourceArmVirtualNetwork() *schema.Resource { "vnet_peerings": { Type: schema.TypeMap, Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } } func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).vnetClient + client := meta.(*ArmClient).network.VnetClient ctx := meta.(*ArmClient).StopContext resGroup := d.Get("resource_group_name").(string) @@ -84,6 +89,10 @@ func dataSourceArmVnetRead(d *schema.ResourceData, meta interface{}) error { } d.SetId(*resp.ID) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + if props := resp.VirtualNetworkPropertiesFormat; props != nil { if as := props.AddressSpace; as != nil { if err := d.Set("address_spaces", utils.FlattenStringSlice(as.AddressPrefixes)); err != nil { //todo remove in 2.0 diff --git a/azurerm/data_source_virtual_network_gateway.go b/azurerm/data_source_virtual_network_gateway.go index cc98b58b3547..091f21afdb83 100644 --- a/azurerm/data_source_virtual_network_gateway.go +++ b/azurerm/data_source_virtual_network_gateway.go @@ -3,10 +3,11 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -176,13 +177,13 @@ func dataSourceArmVirtualNetworkGateway() *schema.Resource { Computed: true, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmVirtualNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).vnetGatewayClient + client := meta.(*ArmClient).network.VnetGatewayClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -239,9 +240,7 @@ func dataSourceArmVirtualNetworkGatewayRead(d *schema.ResourceData, meta interfa } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func flattenArmVirtualNetworkGatewayDataSourceIPConfigurations(ipConfigs *[]network.VirtualNetworkGatewayIPConfiguration) []interface{} { diff --git a/azurerm/data_source_virtual_network_gateway_connection.go b/azurerm/data_source_virtual_network_gateway_connection.go index db9dc37349df..1a37be08558a 100644 --- a/azurerm/data_source_virtual_network_gateway_connection.go +++ b/azurerm/data_source_virtual_network_gateway_connection.go @@ -3,8 +3,9 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" @@ -142,13 +143,13 @@ func dataSourceArmVirtualNetworkGatewayConnection() *schema.Resource { }, }, - "tags": tagsForDataSourceSchema(), + "tags": tags.SchemaDataSource(), }, } } func dataSourceArmVirtualNetworkGatewayConnectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).vnetGatewayConnectionsClient + client := meta.(*ArmClient).network.VnetGatewayConnectionsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) diff --git a/azurerm/data_source_virtual_network_gateway_connection_test.go b/azurerm/data_source_virtual_network_gateway_connection_test.go index 6136845c67cc..fda634ee8c21 100644 --- a/azurerm/data_source_virtual_network_gateway_connection_test.go +++ b/azurerm/data_source_virtual_network_gateway_connection_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" @@ -99,35 +99,41 @@ func testAccAzureRMDataSourceVirtualNetworkGatewayConnection_sitetosite(rInt int variable "random" { default = "%d" } + resource "azurerm_resource_group" "test" { name = "acctestRG-${var.random}" location = "%s" } + resource "azurerm_virtual_network" "test" { name = "acctestvn-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" address_space = ["10.0.0.0/16"] } + resource "azurerm_subnet" "test" { 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" } + resource "azurerm_public_ip" "test" { name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" allocation_method = "Dynamic" } + resource "azurerm_virtual_network_gateway" "test" { name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - type = "Vpn" - vpn_type = "RouteBased" - sku = "Basic" + type = "Vpn" + vpn_type = "RouteBased" + sku = "Basic" + ip_configuration { name = "vnetGatewayConfig" public_ip_address_id = "${azurerm_public_ip.test.id}" @@ -135,28 +141,29 @@ resource "azurerm_virtual_network_gateway" "test" { subnet_id = "${azurerm_subnet.test.id}" } } + resource "azurerm_local_network_gateway" "test" { name = "acctest-${var.random}" 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_virtual_network_gateway_connection" "test" { - name = "acctest-${var.random}" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" type = "IPsec" virtual_network_gateway_id = "${azurerm_virtual_network_gateway.test.id}" local_network_gateway_id = "${azurerm_local_network_gateway.test.id}" - shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" + shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" } data "azurerm_virtual_network_gateway_connection" "test" { - name = "${azurerm_virtual_network_gateway_connection.test.name}" - resource_group_name = "${azurerm_virtual_network_gateway_connection.test.resource_group_name}" + name = "${azurerm_virtual_network_gateway_connection.test.name}" + resource_group_name = "${azurerm_virtual_network_gateway_connection.test.resource_group_name}" } - `, rInt, location) } @@ -285,13 +292,13 @@ resource "azurerm_virtual_network_gateway_connection" "test_2" { } data "azurerm_virtual_network_gateway_connection" "test_1" { - name = "${azurerm_virtual_network_gateway_connection.test_1.name}" - resource_group_name = "${azurerm_virtual_network_gateway_connection.test_1.resource_group_name}" + name = "${azurerm_virtual_network_gateway_connection.test_1.name}" + resource_group_name = "${azurerm_virtual_network_gateway_connection.test_1.resource_group_name}" } data "azurerm_virtual_network_gateway_connection" "test_2" { - name = "${azurerm_virtual_network_gateway_connection.test_2.name}" - resource_group_name = "${azurerm_virtual_network_gateway_connection.test_2.resource_group_name}" + name = "${azurerm_virtual_network_gateway_connection.test_2.name}" + resource_group_name = "${azurerm_virtual_network_gateway_connection.test_2.resource_group_name}" } `, rInt, rInt2, sharedKey, location, altLocation) } @@ -301,35 +308,41 @@ func testAccAzureRMDataSourceVirtualNetworkGatewayConnection_ipsecpolicy(rInt in variable "random" { default = "%d" } + resource "azurerm_resource_group" "test" { name = "acctestRG-${var.random}" location = "%s" } + resource "azurerm_virtual_network" "test" { name = "acctestvn-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" address_space = ["10.0.0.0/16"] } + resource "azurerm_subnet" "test" { 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" } + resource "azurerm_public_ip" "test" { name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" allocation_method = "Dynamic" } + resource "azurerm_virtual_network_gateway" "test" { name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - type = "Vpn" - vpn_type = "RouteBased" - sku = "VpnGw1" + type = "Vpn" + vpn_type = "RouteBased" + sku = "VpnGw1" + ip_configuration { name = "vnetGatewayConfig" public_ip_address_id = "${azurerm_public_ip.test.id}" @@ -337,22 +350,25 @@ resource "azurerm_virtual_network_gateway" "test" { subnet_id = "${azurerm_subnet.test.id}" } } + resource "azurerm_local_network_gateway" "test" { name = "acctest-${var.random}" 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_virtual_network_gateway_connection" "test" { - name = "acctest-${var.random}" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - type = "IPsec" - virtual_network_gateway_id = "${azurerm_virtual_network_gateway.test.id}" - local_network_gateway_id = "${azurerm_local_network_gateway.test.id}" + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + type = "IPsec" + virtual_network_gateway_id = "${azurerm_virtual_network_gateway.test.id}" + local_network_gateway_id = "${azurerm_local_network_gateway.test.id}" use_policy_based_traffic_selectors = true routing_weight = 20 + ipsec_policy { dh_group = "DHGroup14" ike_encryption = "AES256" @@ -363,12 +379,13 @@ resource "azurerm_virtual_network_gateway_connection" "test" { sa_datasize = 102400000 sa_lifetime = 27000 } + shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" } data "azurerm_virtual_network_gateway_connection" "test" { - name = "${azurerm_virtual_network_gateway_connection.test.name}" - resource_group_name = "${azurerm_virtual_network_gateway_connection.test.resource_group_name}" + name = "${azurerm_virtual_network_gateway_connection.test.name}" + resource_group_name = "${azurerm_virtual_network_gateway_connection.test.resource_group_name}" } `, rInt, location) } diff --git a/azurerm/data_source_virtual_network_test.go b/azurerm/data_source_virtual_network_test.go index 38d39e6fea5c..6424146b9f22 100644 --- a/azurerm/data_source_virtual_network_test.go +++ b/azurerm/data_source_virtual_network_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" ) @@ -13,6 +14,7 @@ func TestAccDataSourceArmVirtualNetwork_basic(t *testing.T) { ri := tf.AccRandTimeInt() name := fmt.Sprintf("acctestvnet-%d", ri) + location := testLocation() config := testAccDataSourceArmVirtualNetwork_basic(ri, testLocation()) resource.ParallelTest(t, resource.TestCase{ @@ -23,6 +25,7 @@ func TestAccDataSourceArmVirtualNetwork_basic(t *testing.T) { Config: config, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(dataSourceName, "name", name), + resource.TestCheckResourceAttr(dataSourceName, "location", azure.NormalizeLocation(location)), resource.TestCheckResourceAttr(dataSourceName, "dns_servers.0", "10.0.0.4"), resource.TestCheckResourceAttr(dataSourceName, "address_spaces.0", "10.0.0.0/16"), resource.TestCheckResourceAttr(dataSourceName, "subnets.0", "subnet1"), diff --git a/azurerm/deprecated.go b/azurerm/deprecated.go new file mode 100644 index 000000000000..aab11e29c917 --- /dev/null +++ b/azurerm/deprecated.go @@ -0,0 +1,62 @@ +package azurerm + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" +) + +// NOTE: these methods are deprecated, but provided to ease compatibility for open PR's + +// nolint: deadcode unused +var requireResourcesToBeImported = features.ShouldResourcesBeImported() + +// nolint: deadcode unused +func flattenAndSetTags(d *schema.ResourceData, tagMap map[string]*string) { + // we intentionally ignore the error here, since this method doesn't expose it + _ = tags.FlattenAndSet(d, tagMap) +} + +// nolint: deadcode unused +func expandTags(tagsMap map[string]interface{}) map[string]*string { + return tags.Expand(tagsMap) +} + +// nolint: deadcode unused +func tagsForDataSourceSchema() *schema.Schema { + return tags.SchemaDataSource() +} + +// nolint: deadcode unused +func tagsSchema() *schema.Schema { + return tags.Schema() +} + +// nolint: deadcode unused +func tagsForceNewSchema() *schema.Schema { + return tags.ForceNewSchema() +} + +// nolint: deadcode unused +func parseAzureResourceID(id string) (*azure.ResourceID, error) { + return azure.ParseAzureResourceID(id) +} + +func evaluateSchemaValidateFunc(i interface{}, k string, validateFunc schema.SchemaValidateFunc) (bool, error) { // nolint: unparam + _, errors := validateFunc(i, k) + + errorStrings := []string{} + for _, e := range errors { + errorStrings = append(errorStrings, e.Error()) + } + + if len(errors) > 0 { + return false, fmt.Errorf(strings.Join(errorStrings, "\n")) + } + + return true, nil +} diff --git a/azurerm/encryption_settings.go b/azurerm/encryption_settings.go index 1dae90ab4092..7aa9bbfb7400 100644 --- a/azurerm/encryption_settings.go +++ b/azurerm/encryption_settings.go @@ -1,7 +1,7 @@ package azurerm import ( - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -63,18 +63,21 @@ func encryptionSettingsSchema() *schema.Schema { } } -func expandManagedDiskEncryptionSettings(settings map[string]interface{}) *compute.EncryptionSettings { +func expandManagedDiskEncryptionSettings(settings map[string]interface{}) *compute.EncryptionSettingsCollection { enabled := settings["enabled"].(bool) - config := &compute.EncryptionSettings{ + config := &compute.EncryptionSettingsCollection{ Enabled: utils.Bool(enabled), } + var diskEncryptionKey *compute.KeyVaultAndSecretReference + var keyEncryptionKey *compute.KeyVaultAndKeyReference + if v := settings["disk_encryption_key"].([]interface{}); len(v) > 0 { dek := v[0].(map[string]interface{}) secretURL := dek["secret_url"].(string) sourceVaultId := dek["source_vault_id"].(string) - config.DiskEncryptionKey = &compute.KeyVaultAndSecretReference{ + diskEncryptionKey = &compute.KeyVaultAndSecretReference{ SecretURL: utils.String(secretURL), SourceVault: &compute.SourceVault{ ID: utils.String(sourceVaultId), @@ -87,7 +90,7 @@ func expandManagedDiskEncryptionSettings(settings map[string]interface{}) *compu secretURL := kek["key_url"].(string) sourceVaultId := kek["source_vault_id"].(string) - config.KeyEncryptionKey = &compute.KeyVaultAndKeyReference{ + keyEncryptionKey = &compute.KeyVaultAndKeyReference{ KeyURL: utils.String(secretURL), SourceVault: &compute.SourceVault{ ID: utils.String(sourceVaultId), @@ -95,38 +98,53 @@ func expandManagedDiskEncryptionSettings(settings map[string]interface{}) *compu } } + // at this time we only support a single element + config.EncryptionSettings = &[]compute.EncryptionSettingsElement{ + { + DiskEncryptionKey: diskEncryptionKey, + KeyEncryptionKey: keyEncryptionKey, + }, + } return config } -func flattenManagedDiskEncryptionSettings(encryptionSettings *compute.EncryptionSettings) []interface{} { +func flattenManagedDiskEncryptionSettings(encryptionSettings *compute.EncryptionSettingsCollection) []interface{} { + if encryptionSettings == nil { + return []interface{}{} + } + value := map[string]interface{}{ "enabled": *encryptionSettings.Enabled, } - if key := encryptionSettings.DiskEncryptionKey; key != nil { - keys := make(map[string]interface{}) + if encryptionSettings.EncryptionSettings != nil && len(*encryptionSettings.EncryptionSettings) > 0 { + // at this time we only support a single element + settings := (*encryptionSettings.EncryptionSettings)[0] + if key := settings.DiskEncryptionKey; key != nil { + keys := make(map[string]interface{}) - keys["secret_url"] = *key.SecretURL - if vault := key.SourceVault; vault != nil { - keys["source_vault_id"] = *vault.ID + keys["secret_url"] = *key.SecretURL + if vault := key.SourceVault; vault != nil { + keys["source_vault_id"] = *vault.ID + } + + value["disk_encryption_key"] = []interface{}{keys} } - value["disk_encryption_key"] = []interface{}{keys} - } + if key := settings.KeyEncryptionKey; key != nil { + keys := make(map[string]interface{}) - if key := encryptionSettings.KeyEncryptionKey; key != nil { - keys := make(map[string]interface{}) + keys["key_url"] = *key.KeyURL - keys["key_url"] = *key.KeyURL + if vault := key.SourceVault; vault != nil { + keys["source_vault_id"] = *vault.ID + } - if vault := key.SourceVault; vault != nil { - keys["source_vault_id"] = *vault.ID + value["key_encryption_key"] = []interface{}{keys} } - - value["key_encryption_key"] = []interface{}{keys} } - output := make([]interface{}, 0) - output = append(output, value) - return output + return []interface{}{ + value, + } } diff --git a/azurerm/express_route_circuit.go b/azurerm/express_route_circuit.go deleted file mode 100644 index 7e2870e1e898..000000000000 --- a/azurerm/express_route_circuit.go +++ /dev/null @@ -1,37 +0,0 @@ -package azurerm - -import ( - "fmt" - - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" -) - -func extractResourceGroupAndErcName(resourceId string) (resourceGroup string, name string, e error) { - id, err := parseAzureResourceID(resourceId) - if err != nil { - return "", "", err - } - - return id.ResourceGroup, id.Path["expressRouteCircuits"], err -} - -func retrieveErcByResourceId(resourceId string, meta interface{}) (erc *network.ExpressRouteCircuit, resourceGroup string, e error) { - ercClient := meta.(*ArmClient).expressRouteCircuitClient - ctx := meta.(*ArmClient).StopContext - - resGroup, name, err := extractResourceGroupAndErcName(resourceId) - if err != nil { - return nil, "", fmt.Errorf("Error Parsing Azure Resource ID -: %+v", err) - } - - resp, err := ercClient.Get(ctx, resGroup, name) - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return nil, "", nil - } - return nil, "", fmt.Errorf("Error making Read request on Express Route Circuit %s: %+v", name, err) - } - - return &resp, resGroup, nil -} diff --git a/azurerm/feature_flags.go b/azurerm/feature_flags.go deleted file mode 100644 index 04f4e816c95d..000000000000 --- a/azurerm/feature_flags.go +++ /dev/null @@ -1,10 +0,0 @@ -package azurerm - -import "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/flags" - -// NOTE: we'll need to add an infobox to MySQL|PostgreSQL Configuration when this goes live -// since these resources can't support import -// in addition the virtual resources will need adjusting - -// This file contains feature flags for functionality which will prove more challenging to implement en-mass -var requireResourcesToBeImported = flags.RequireResourcesToBeImported diff --git a/azurerm/helpers/azure/app_service.go b/azurerm/helpers/azure/app_service.go index 16e2727d58e1..ae74a16268c8 100644 --- a/azurerm/helpers/azure/app_service.go +++ b/azurerm/helpers/azure/app_service.go @@ -154,6 +154,9 @@ func SchemaAppServiceAuthSettings() *schema.Schema { "additional_login_params": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "allowed_external_redirect_urls": { Type: schema.TypeList, @@ -208,6 +211,47 @@ func SchemaAppServiceAuthSettings() *schema.Schema { } } +func SchemaAppServiceIdentity() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(web.ManagedServiceIdentityTypeNone), + string(web.ManagedServiceIdentityTypeSystemAssigned), + string(web.ManagedServiceIdentityTypeSystemAssignedUserAssigned), + string(web.ManagedServiceIdentityTypeUserAssigned), + }, true), + DiffSuppressFunc: suppress.CaseDifference, + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "identity_ids": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + } +} + func SchemaAppServiceSiteConfig() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, @@ -259,12 +303,23 @@ func SchemaAppServiceSiteConfig() *schema.Schema { Schema: map[string]*schema.Schema{ "ip_address": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + "virtual_network_subnet_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, }, "subnet_mask": { Type: schema.TypeString, Optional: true, - Default: "255.255.255.255", + Computed: true, + // TODO we should fix this in 2.0 + // This attribute was made with the assumption that `ip_address` was the only valid option + // but `virtual_network_subnet_id` is being added and doesn't need a `subnet_mask`. + // We'll assume a default of "255.255.255.255" in the expand code when `ip_address` is specified + // and `subnet_mask` is not. + // Default: "255.255.255.255", }, }, }, @@ -427,22 +482,104 @@ func SchemaAppServiceSiteConfig() *schema.Schema { Optional: true, }, - "cors": { + "cors": SchemaWebCorsSettings(), + }, + }, + } +} + +func SchemaAppServiceLogsConfig() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "application_logs": { Type: schema.TypeList, Optional: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "allowed_origins": { - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{Type: schema.TypeString}, + "azure_blob_storage": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "level": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(web.Error), + string(web.Information), + string(web.Off), + string(web.Verbose), + string(web.Warning), + }, false), + }, + "sas_url": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "retention_in_days": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, }, - "support_credentials": { - Type: schema.TypeBool, + }, + }, + }, + "http_logs": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "file_system": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "retention_in_mb": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(25, 100), + }, + "retention_in_days": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + ConflictsWith: []string{"logs.0.http_logs.0.azure_blob_storage"}, + }, + "azure_blob_storage": { + Type: schema.TypeList, Optional: true, - Default: false, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sas_url": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "retention_in_days": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + ConflictsWith: []string{"logs.0.http_logs.0.file_system"}, }, }, }, @@ -452,6 +589,56 @@ func SchemaAppServiceSiteConfig() *schema.Schema { } } +func SchemaAppServiceStorageAccounts() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(web.AzureBlob), + string(web.AzureFiles), + }, false), + }, + + "account_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "share_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "access_key": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "mount_path": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + func SchemaAppServiceDataSourceSiteConfig() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, @@ -493,6 +680,10 @@ func SchemaAppServiceDataSourceSiteConfig() *schema.Schema { Type: schema.TypeString, Computed: true, }, + "virtual_network_subnet_id": { + Type: schema.TypeString, + Computed: true, + }, "subnet_mask": { Type: schema.TypeString, Computed: true, @@ -609,57 +800,6 @@ func SchemaAppServiceDataSourceSiteConfig() *schema.Schema { } } -func ExpandAppServiceCorsSettings(input interface{}) web.CorsSettings { - settings := input.([]interface{}) - corsSettings := web.CorsSettings{} - - if len(settings) == 0 { - return corsSettings - } - - setting := settings[0].(map[string]interface{}) - - if v, ok := setting["allowed_origins"]; ok { - input := v.(*schema.Set).List() - - allowedOrigins := make([]string, 0) - for _, param := range input { - allowedOrigins = append(allowedOrigins, param.(string)) - } - - corsSettings.AllowedOrigins = &allowedOrigins - } - - if v, ok := setting["support_credentials"]; ok { - corsSettings.SupportCredentials = utils.Bool(v.(bool)) - } - - return corsSettings -} - -func FlattenAppServiceCorsSettings(input *web.CorsSettings) []interface{} { - results := make([]interface{}, 0) - if input == nil { - return results - } - - result := make(map[string]interface{}) - - allowedOrigins := make([]interface{}, 0) - if s := input.AllowedOrigins; s != nil { - for _, v := range *s { - allowedOrigins = append(allowedOrigins, v) - } - } - result["allowed_origins"] = schema.NewSet(schema.HashString, allowedOrigins) - - if input.SupportCredentials != nil { - result["support_credentials"] = *input.SupportCredentials - } - - return append(results, result) -} - func ExpandAppServiceAuthSettings(input []interface{}) web.SiteAuthSettingsProperties { siteAuthSettingsProperties := web.SiteAuthSettingsProperties{} @@ -1014,12 +1154,222 @@ func FlattenAppServiceAuthSettings(input *web.SiteAuthSettingsProperties) []inte return append(results, result) } -func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig { +func FlattenAppServiceLogs(input *web.SiteLogsConfigProperties) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + result := make(map[string]interface{}) + + appLogs := make([]interface{}, 0) + if input.ApplicationLogs != nil { + appLogsItem := make(map[string]interface{}) + + blobStorage := make([]interface{}, 0) + if blobStorageInput := input.ApplicationLogs.AzureBlobStorage; blobStorageInput != nil { + blobStorageItem := make(map[string]interface{}) + + blobStorageItem["level"] = string(blobStorageInput.Level) + + if blobStorageInput.SasURL != nil { + blobStorageItem["sas_url"] = *blobStorageInput.SasURL + } + + if blobStorageInput.RetentionInDays != nil { + blobStorageItem["retention_in_days"] = *blobStorageInput.RetentionInDays + } + + // The API returns a non nil application logs object when other logs are specified so we'll check that this structure is empty before adding it to the statefile. + if blobStorageInput.SasURL != nil && *blobStorageInput.SasURL != "" { + blobStorage = append(blobStorage, blobStorageItem) + } + } + appLogsItem["azure_blob_storage"] = blobStorage + appLogs = append(appLogs, appLogsItem) + } + result["application_logs"] = appLogs + + httpLogs := make([]interface{}, 0) + if input.HTTPLogs != nil { + httpLogsItem := make(map[string]interface{}) + + fileSystem := make([]interface{}, 0) + if fileSystemInput := input.HTTPLogs.FileSystem; fileSystemInput != nil { + fileSystemItem := make(map[string]interface{}) + + if fileSystemInput.RetentionInDays != nil { + fileSystemItem["retention_in_days"] = *fileSystemInput.RetentionInDays + } + + if fileSystemInput.RetentionInMb != nil { + fileSystemItem["retention_in_mb"] = *fileSystemInput.RetentionInMb + } + + // The API returns a non nil filesystem logs object when other logs are specified so we'll check that this is disabled before adding it to the statefile. + if fileSystemInput.Enabled != nil && *fileSystemInput.Enabled { + fileSystem = append(fileSystem, fileSystemItem) + } + } + + blobStorage := make([]interface{}, 0) + if blobStorageInput := input.HTTPLogs.AzureBlobStorage; blobStorageInput != nil { + blobStorageItem := make(map[string]interface{}) + + if blobStorageInput.SasURL != nil { + blobStorageItem["sas_url"] = *blobStorageInput.SasURL + } + + if blobStorageInput.RetentionInDays != nil { + blobStorageItem["retention_in_days"] = *blobStorageInput.RetentionInDays + } + + // The API returns a non nil blob logs object when other logs are specified so we'll check that this is disabled before adding it to the statefile. + if blobStorageInput.Enabled != nil && *blobStorageInput.Enabled { + blobStorage = append(blobStorage, blobStorageItem) + } + } + + httpLogsItem["file_system"] = fileSystem + httpLogsItem["azure_blob_storage"] = blobStorage + httpLogs = append(httpLogs, httpLogsItem) + } + result["http_logs"] = httpLogs + + return append(results, result) +} + +func ExpandAppServiceLogs(input interface{}) web.SiteLogsConfigProperties { + configs := input.([]interface{}) + logs := web.SiteLogsConfigProperties{} + + if len(configs) == 0 { + return logs + } + + config := configs[0].(map[string]interface{}) + + if v, ok := config["application_logs"]; ok { + appLogsConfigs := v.([]interface{}) + + for _, config := range appLogsConfigs { + appLogsConfig := config.(map[string]interface{}) + + logs.ApplicationLogs = &web.ApplicationLogsConfig{} + + if v, ok := appLogsConfig["azure_blob_storage"]; ok { + storageConfigs := v.([]interface{}) + + for _, config := range storageConfigs { + storageConfig := config.(map[string]interface{}) + + logs.ApplicationLogs.AzureBlobStorage = &web.AzureBlobStorageApplicationLogsConfig{ + Level: web.LogLevel(storageConfig["level"].(string)), + SasURL: utils.String(storageConfig["sas_url"].(string)), + RetentionInDays: utils.Int32(int32(storageConfig["retention_in_days"].(int))), + } + } + } + } + } + + if v, ok := config["http_logs"]; ok { + httpLogsConfigs := v.([]interface{}) + + for _, config := range httpLogsConfigs { + httpLogsConfig := config.(map[string]interface{}) + + logs.HTTPLogs = &web.HTTPLogsConfig{} + + if v, ok := httpLogsConfig["file_system"]; ok { + fileSystemConfigs := v.([]interface{}) + + for _, config := range fileSystemConfigs { + fileSystemConfig := config.(map[string]interface{}) + + logs.HTTPLogs.FileSystem = &web.FileSystemHTTPLogsConfig{ + RetentionInMb: utils.Int32(int32(fileSystemConfig["retention_in_mb"].(int))), + RetentionInDays: utils.Int32(int32(fileSystemConfig["retention_in_days"].(int))), + Enabled: utils.Bool(true), + } + } + } + + if v, ok := httpLogsConfig["azure_blob_storage"]; ok { + storageConfigs := v.([]interface{}) + + for _, config := range storageConfigs { + storageConfig := config.(map[string]interface{}) + + logs.HTTPLogs.AzureBlobStorage = &web.AzureBlobStorageHTTPLogsConfig{ + SasURL: utils.String(storageConfig["sas_url"].(string)), + RetentionInDays: utils.Int32(int32(storageConfig["retention_in_days"].(int))), + Enabled: utils.Bool(true), + } + } + } + } + } + + return logs +} + +func ExpandAppServiceIdentity(d *schema.ResourceData) *web.ManagedServiceIdentity { + identities := d.Get("identity").([]interface{}) + if len(identities) == 0 { + return nil + } + identity := identities[0].(map[string]interface{}) + identityType := web.ManagedServiceIdentityType(identity["type"].(string)) + + identityIds := make(map[string]*web.ManagedServiceIdentityUserAssignedIdentitiesValue) + for _, id := range identity["identity_ids"].([]interface{}) { + identityIds[id.(string)] = &web.ManagedServiceIdentityUserAssignedIdentitiesValue{} + } + + managedServiceIdentity := web.ManagedServiceIdentity{ + Type: identityType, + } + + if managedServiceIdentity.Type == web.ManagedServiceIdentityTypeUserAssigned || managedServiceIdentity.Type == web.ManagedServiceIdentityTypeSystemAssignedUserAssigned { + managedServiceIdentity.UserAssignedIdentities = identityIds + } + + return &managedServiceIdentity +} + +func FlattenAppServiceIdentity(identity *web.ManagedServiceIdentity) []interface{} { + if identity == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + result["type"] = string(identity.Type) + + if identity.PrincipalID != nil { + result["principal_id"] = *identity.PrincipalID + } + if identity.TenantID != nil { + result["tenant_id"] = *identity.TenantID + } + + identityIds := make([]string, 0) + if identity.UserAssignedIdentities != nil { + for key := range identity.UserAssignedIdentities { + identityIds = append(identityIds, key) + } + } + result["identity_ids"] = identityIds + + return []interface{}{result} +} + +func ExpandAppServiceSiteConfig(input interface{}) (*web.SiteConfig, error) { configs := input.([]interface{}) - siteConfig := web.SiteConfig{} + siteConfig := &web.SiteConfig{} if len(configs) == 0 { - return siteConfig + return siteConfig, nil } config := configs[0].(map[string]interface{}) @@ -1074,26 +1424,44 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig { if v, ok := config["ip_restriction"]; ok { ipSecurityRestrictions := v.([]interface{}) restrictions := make([]web.IPSecurityRestriction, 0) - for _, ipSecurityRestriction := range ipSecurityRestrictions { + for i, ipSecurityRestriction := range ipSecurityRestrictions { restriction := ipSecurityRestriction.(map[string]interface{}) ipAddress := restriction["ip_address"].(string) - mask := restriction["subnet_mask"].(string) - // the 2018-02-01 API expects a blank subnet mask and an IP address in CIDR format: a.b.c.d/x - // so translate the IP and mask if necessary - restrictionMask := "" - cidrAddress := ipAddress - if mask != "" { - ipNet := net.IPNet{IP: net.ParseIP(ipAddress), Mask: net.IPMask(net.ParseIP(mask))} - cidrAddress = ipNet.String() - } else if !strings.Contains(ipAddress, "/") { - cidrAddress += "/32" + vNetSubnetID := restriction["virtual_network_subnet_id"].(string) + if vNetSubnetID != "" && ipAddress != "" { + return siteConfig, fmt.Errorf(fmt.Sprintf("only one of `ip_address` or `virtual_network_subnet_id` can set set for `site_config.0.ip_restriction.%d`", i)) + } + + if vNetSubnetID == "" && ipAddress == "" { + return siteConfig, fmt.Errorf(fmt.Sprintf("one of `ip_address` or `virtual_network_subnet_id` must be set set for `site_config.0.ip_restriction.%d`", i)) } - restrictions = append(restrictions, web.IPSecurityRestriction{ - IPAddress: &cidrAddress, - SubnetMask: &restrictionMask, - }) + ipSecurityRestriction := web.IPSecurityRestriction{} + if ipAddress != "" { + mask := restriction["subnet_mask"].(string) + if mask == "" { + mask = "255.255.255.255" + } + // the 2018-02-01 API expects a blank subnet mask and an IP address in CIDR format: a.b.c.d/x + // so translate the IP and mask if necessary + restrictionMask := "" + cidrAddress := ipAddress + if mask != "" { + ipNet := net.IPNet{IP: net.ParseIP(ipAddress), Mask: net.IPMask(net.ParseIP(mask))} + cidrAddress = ipNet.String() + } else if !strings.Contains(ipAddress, "/") { + cidrAddress += "/32" + } + ipSecurityRestriction.IPAddress = &cidrAddress + ipSecurityRestriction.SubnetMask = &restrictionMask + } + + if vNetSubnetID != "" { + ipSecurityRestriction.VnetSubnetResourceID = &vNetSubnetID + } + + restrictions = append(restrictions, ipSecurityRestriction) } siteConfig.IPSecurityRestrictions = &restrictions } @@ -1148,11 +1516,11 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig { if v, ok := config["cors"]; ok { corsSettings := v.(interface{}) - expand := ExpandAppServiceCorsSettings(corsSettings) + expand := ExpandWebCorsSettings(corsSettings) siteConfig.Cors = &expand } - return siteConfig + return siteConfig, nil } func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} { @@ -1220,6 +1588,9 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} { if subnet := v.SubnetMask; subnet != nil { block["subnet_mask"] = *subnet } + if vNetSubnetID := v.VnetSubnetResourceID; vNetSubnetID != nil { + block["virtual_network_subnet_id"] = *vNetSubnetID + } restrictions = append(restrictions, block) } } @@ -1267,7 +1638,58 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} { result["ftps_state"] = string(input.FtpsState) result["min_tls_version"] = string(input.MinTLSVersion) - result["cors"] = FlattenAppServiceCorsSettings(input.Cors) + result["cors"] = FlattenWebCorsSettings(input.Cors) return append(results, result) } + +func ExpandAppServiceStorageAccounts(d *schema.ResourceData) map[string]*web.AzureStorageInfoValue { + input := d.Get("storage_account").(*schema.Set).List() + output := make(map[string]*web.AzureStorageInfoValue, len(input)) + + for _, v := range input { + vals := v.(map[string]interface{}) + + saName := vals["name"].(string) + saType := vals["type"].(string) + saAccountName := vals["account_name"].(string) + saShareName := vals["share_name"].(string) + saAccessKey := vals["access_key"].(string) + saMountPath := vals["mount_path"].(string) + + output[saName] = &web.AzureStorageInfoValue{ + Type: web.AzureStorageType(saType), + AccountName: utils.String(saAccountName), + ShareName: utils.String(saShareName), + AccessKey: utils.String(saAccessKey), + MountPath: utils.String(saMountPath), + } + } + + return output +} + +func FlattenAppServiceStorageAccounts(input map[string]*web.AzureStorageInfoValue) []interface{} { + results := make([]interface{}, 0) + + for k, v := range input { + result := make(map[string]interface{}) + result["name"] = k + result["type"] = string(v.Type) + if v.AccountName != nil { + result["account_name"] = *v.AccountName + } + if v.ShareName != nil { + result["share_name"] = *v.ShareName + } + if v.AccessKey != nil { + result["access_key"] = *v.AccessKey + } + if v.MountPath != nil { + result["mount_path"] = *v.MountPath + } + results = append(results, result) + } + + return results +} diff --git a/azurerm/helpers/azure/app_service_schedule_backup.go b/azurerm/helpers/azure/app_service_schedule_backup.go new file mode 100644 index 000000000000..e4d19a852579 --- /dev/null +++ b/azurerm/helpers/azure/app_service_schedule_backup.go @@ -0,0 +1,188 @@ +package azure + +import ( + "time" + + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + + "github.com/Azure/go-autorest/autorest/date" + + "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" +) + +func SchemaAppServiceBackup() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "storage_account_url": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validate.URLIsHTTPS, + }, + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "schedule": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "frequency_interval": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1000), + }, + + "frequency_unit": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Day", + "Hour", + }, false), + }, + + "keep_at_least_one_backup": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "retention_period_in_days": { + Type: schema.TypeInt, + Optional: true, + Default: 30, + ValidateFunc: validation.IntBetween(0, 9999999), + }, + + "start_time": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validate.RFC3339Time, + }, + }, + }, + }, + }, + }, + } +} + +func ExpandAppServiceBackup(input []interface{}) *web.BackupRequest { + if len(input) == 0 { + return nil + } + + vals := input[0].(map[string]interface{}) + + name := vals["name"].(string) + storageAccountUrl := vals["storage_account_url"].(string) + enabled := vals["enabled"].(bool) + + request := &web.BackupRequest{ + BackupRequestProperties: &web.BackupRequestProperties{ + BackupName: utils.String(name), + StorageAccountURL: utils.String(storageAccountUrl), + Enabled: utils.Bool(enabled), + }, + } + + scheduleRaw := vals["schedule"].([]interface{}) + if len(scheduleRaw) > 0 { + schedule := scheduleRaw[0].(map[string]interface{}) + backupSchedule := web.BackupSchedule{} + + if v, ok := schedule["frequency_interval"].(int); ok { + backupSchedule.FrequencyInterval = utils.Int32(int32(v)) + } + + if v, ok := schedule["frequency_unit"]; ok { + backupSchedule.FrequencyUnit = web.FrequencyUnit(v.(string)) + } + + if v, ok := schedule["keep_at_least_one_backup"]; ok { + backupSchedule.KeepAtLeastOneBackup = utils.Bool(v.(bool)) + } + + if v, ok := schedule["retention_period_in_days"].(int); ok { + backupSchedule.RetentionPeriodInDays = utils.Int32(int32(v)) + } + + if v, ok := schedule["start_time"].(string); ok { + dateTimeToStart, _ := time.Parse(time.RFC3339, v) //validated by schema + backupSchedule.StartTime = &date.Time{Time: dateTimeToStart} + } + + request.BackupRequestProperties.BackupSchedule = &backupSchedule + } + + return request +} + +func FlattenAppServiceBackup(input *web.BackupRequestProperties) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + if input.BackupName != nil { + output["name"] = *input.BackupName + } + if input.Enabled != nil { + output["enabled"] = *input.Enabled + } + if input.StorageAccountURL != nil { + output["storage_account_url"] = *input.StorageAccountURL + } + + schedules := make([]interface{}, 0) + if input.BackupSchedule != nil { + v := *input.BackupSchedule + + schedule := make(map[string]interface{}) + + if v.FrequencyInterval != nil { + schedule["frequency_interval"] = int(*v.FrequencyInterval) + } + + schedule["frequency_unit"] = string(v.FrequencyUnit) + + if v.KeepAtLeastOneBackup != nil { + schedule["keep_at_least_one_backup"] = *v.KeepAtLeastOneBackup + } + if v.RetentionPeriodInDays != nil { + schedule["retention_period_in_days"] = int(*v.RetentionPeriodInDays) + } + if v.StartTime != nil && !v.StartTime.IsZero() { + schedule["start_time"] = v.StartTime.Format(time.RFC3339) + } + + schedules = append(schedules, schedule) + } + output["schedule"] = schedules + + return []interface{}{ + output, + } +} diff --git a/azurerm/helpers/azure/automation_variable.go b/azurerm/helpers/azure/automation_variable.go deleted file mode 100644 index 906b62c1b0ed..000000000000 --- a/azurerm/helpers/azure/automation_variable.go +++ /dev/null @@ -1,42 +0,0 @@ -package azure - -import ( - "fmt" - "regexp" - "strconv" - "time" -) - -func ParseAzureRmAutomationVariableValue(resource string, input *string) (interface{}, error) { - if input == nil { - if resource != "azurerm_automation_variable_null" { - return nil, fmt.Errorf("Expected value \"nil\" to be %q, actual type is \"azurerm_automation_variable_null\"", resource) - } - return nil, nil - } - - var value interface{} - var err error - actualResource := "Unknown" - datePattern := regexp.MustCompile(`"\\/Date\((-?[0-9]+)\)\\/"`) - matches := datePattern.FindStringSubmatch(*input) - - if len(matches) == 2 && matches[0] == *input { - if ticks, err := strconv.ParseInt(matches[1], 10, 64); err == nil { - value = time.Unix(ticks/1000, ticks%1000*1000000).In(time.UTC) - actualResource = "azurerm_automation_variable_datetime" - } - } else if value, err = strconv.Unquote(*input); err == nil { - actualResource = "azurerm_automation_variable_string" - } else if value, err = strconv.ParseBool(*input); err == nil { - actualResource = "azurerm_automation_variable_bool" - } else if value, err = strconv.ParseInt(*input, 10, 32); err == nil { - value = int32(value.(int64)) - actualResource = "azurerm_automation_variable_int" - } - - if actualResource != resource { - return nil, fmt.Errorf("Expected value %q to be %q, actual type is %q", *input, resource, actualResource) - } - return value, nil -} diff --git a/azurerm/helpers/azure/batch_account.go b/azurerm/helpers/azure/batch_account.go new file mode 100644 index 000000000000..464792a9ecc2 --- /dev/null +++ b/azurerm/helpers/azure/batch_account.go @@ -0,0 +1,45 @@ +package azure + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" +) + +// ExpandBatchAccountKeyVaultReference expands Batch account KeyVault reference +func ExpandBatchAccountKeyVaultReference(list []interface{}) (*batch.KeyVaultReference, error) { + if len(list) == 0 { + return nil, fmt.Errorf("Error: key vault reference should be defined") + } + + keyVaultRef := list[0].(map[string]interface{}) + + keyVaultRefID := keyVaultRef["id"].(string) + keyVaultRefURL := keyVaultRef["url"].(string) + + ref := &batch.KeyVaultReference{ + ID: &keyVaultRefID, + URL: &keyVaultRefURL, + } + + return ref, nil +} + +// FlattenBatchAccountKeyvaultReference flattens a Batch account keyvault reference +func FlattenBatchAccountKeyvaultReference(keyVaultReference *batch.KeyVaultReference) interface{} { + result := make(map[string]interface{}) + + if keyVaultReference == nil { + return []interface{}{} + } + + if keyVaultReference.ID != nil { + result["id"] = *keyVaultReference.ID + } + + if keyVaultReference.URL != nil { + result["url"] = *keyVaultReference.URL + } + + return []interface{}{result} +} diff --git a/azurerm/helpers/azure/batch_pool.go b/azurerm/helpers/azure/batch_pool.go index 967e88b3db01..b008a27940f9 100644 --- a/azurerm/helpers/azure/batch_pool.go +++ b/azurerm/helpers/azure/batch_pool.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) // FlattenBatchPoolAutoScaleSettings flattens the auto scale settings for a Batch pool @@ -192,8 +193,7 @@ func FlattenBatchPoolCertificateReferences(armCertificates *[]batch.CertificateR } // FlattenBatchPoolContainerConfiguration flattens a Batch pool container configuration -func FlattenBatchPoolContainerConfiguration(armContainerConfiguration *batch.ContainerConfiguration) interface{} { - +func FlattenBatchPoolContainerConfiguration(d *schema.ResourceData, armContainerConfiguration *batch.ContainerConfiguration) interface{} { result := make(map[string]interface{}) if armContainerConfiguration == nil { @@ -203,10 +203,68 @@ func FlattenBatchPoolContainerConfiguration(armContainerConfiguration *batch.Con if armContainerConfiguration.Type != nil { result["type"] = *armContainerConfiguration.Type } + result["container_registries"] = flattenBatchPoolContainerRegistries(d, armContainerConfiguration.ContainerRegistries) return []interface{}{result} } +func flattenBatchPoolContainerRegistries(d *schema.ResourceData, armContainerRegistries *[]batch.ContainerRegistry) []interface{} { + results := make([]interface{}, 0) + + if armContainerRegistries == nil { + return results + } + for _, armContainerRegistry := range *armContainerRegistries { + result := flattenBatchPoolContainerRegistry(d, &armContainerRegistry) + results = append(results, result) + } + return results +} + +func flattenBatchPoolContainerRegistry(d *schema.ResourceData, armContainerRegistry *batch.ContainerRegistry) map[string]interface{} { + result := make(map[string]interface{}) + + if armContainerRegistry == nil { + return result + } + if registryServer := armContainerRegistry.RegistryServer; registryServer != nil { + result["registry_server"] = *registryServer + } + if userName := armContainerRegistry.UserName; userName != nil { + result["user_name"] = *userName + } + + // If we didn't specify a registry server and user name, just return what we have now rather than trying to locate the password + if len(result) != 2 { + return result + } + + result["password"] = findBatchPoolContainerRegistryPassword(d, result["registry_server"].(string), result["user_name"].(string)) + + return result +} + +func findBatchPoolContainerRegistryPassword(d *schema.ResourceData, armServer string, armUsername string) interface{} { + numContainerRegistries := 0 + if n, ok := d.GetOk("container_configuration.0.container_registries.#"); ok { + numContainerRegistries = n.(int) + } else { + return "" + } + + for i := 0; i < numContainerRegistries; i++ { + if server, ok := d.GetOk(fmt.Sprintf("container_configuration.0.container_registries.%d.registry_server", i)); !ok || server != armServer { + continue + } + if username, ok := d.GetOk(fmt.Sprintf("container_configuration.0.container_registries.%d.user_name", i)); !ok || username != armUsername { + continue + } + return d.Get(fmt.Sprintf("container_configuration.0.container_registries.%d.password", i)) + } + + return "" +} + // ExpandBatchPoolImageReference expands Batch pool image reference func ExpandBatchPoolImageReference(list []interface{}) (*batch.ImageReference, error) { if len(list) == 0 { @@ -214,17 +272,31 @@ func ExpandBatchPoolImageReference(list []interface{}) (*batch.ImageReference, e } storageImageRef := list[0].(map[string]interface{}) + imageRef := &batch.ImageReference{} + + if storageImageRef["id"] != nil && storageImageRef["id"] != "" { + storageImageRefID := storageImageRef["id"].(string) + imageRef.ID = &storageImageRefID + } - storageImageRefOffer := storageImageRef["offer"].(string) - storageImageRefPublisher := storageImageRef["publisher"].(string) - storageImageRefSku := storageImageRef["sku"].(string) - storageImageRefVersion := storageImageRef["version"].(string) + if storageImageRef["offer"] != nil && storageImageRef["offer"] != "" { + storageImageRefOffer := storageImageRef["offer"].(string) + imageRef.Offer = &storageImageRefOffer + } - imageRef := &batch.ImageReference{ - Offer: &storageImageRefOffer, - Publisher: &storageImageRefPublisher, - Sku: &storageImageRefSku, - Version: &storageImageRefVersion, + if storageImageRef["publisher"] != nil && storageImageRef["publisher"] != "" { + storageImageRefPublisher := storageImageRef["publisher"].(string) + imageRef.Publisher = &storageImageRefPublisher + } + + if storageImageRef["sku"] != nil && storageImageRef["sku"] != "" { + storageImageRefSku := storageImageRef["sku"].(string) + imageRef.Sku = &storageImageRefSku + } + + if storageImageRef["version"] != nil && storageImageRef["version"] != "" { + storageImageRefVersion := storageImageRef["version"].(string) + imageRef.Version = &storageImageRefVersion } return imageRef, nil @@ -238,17 +310,49 @@ func ExpandBatchPoolContainerConfiguration(list []interface{}) (*batch.Container containerConfiguration := list[0].(map[string]interface{}) containerType := containerConfiguration["type"].(string) + containerRegistries, err := expandBatchPoolContainerRegistries(containerConfiguration["container_registries"].([]interface{})) + if err != nil { + return nil, err + } containerConf := &batch.ContainerConfiguration{ - Type: &containerType, + Type: &containerType, + ContainerRegistries: containerRegistries, } return containerConf, nil } +func expandBatchPoolContainerRegistries(list []interface{}) (*[]batch.ContainerRegistry, error) { + result := []batch.ContainerRegistry{} + + for _, tempItem := range list { + item := tempItem.(map[string]interface{}) + containerRegistry, err := expandBatchPoolContainerRegistry(item) + if err != nil { + return nil, err + } + result = append(result, *containerRegistry) + } + return &result, nil +} + +func expandBatchPoolContainerRegistry(ref map[string]interface{}) (*batch.ContainerRegistry, error) { + if len(ref) == 0 { + return nil, fmt.Errorf("Error: container registry reference should be defined") + } + + containerRegistry := batch.ContainerRegistry{ + RegistryServer: utils.String(ref["registry_server"].(string)), + UserName: utils.String(ref["user_name"].(string)), + Password: utils.String(ref["password"].(string)), + } + return &containerRegistry, nil +} + // ExpandBatchPoolCertificateReferences expands Batch pool certificate references func ExpandBatchPoolCertificateReferences(list []interface{}) (*[]batch.CertificateReference, error) { - result := []batch.CertificateReference{} + var result []batch.CertificateReference for _, tempItem := range list { item := tempItem.(map[string]interface{}) @@ -270,7 +374,7 @@ func expandBatchPoolCertificateReference(ref map[string]interface{}) (*batch.Cer storeLocation := ref["store_location"].(string) storeName := ref["store_name"].(string) visibilityRefs := ref["visibility"].(*schema.Set) - visibility := []batch.CertificateVisibility{} + var visibility []batch.CertificateVisibility if visibilityRefs != nil { for _, visibilityRef := range visibilityRefs.List() { visibility = append(visibility, batch.CertificateVisibility(visibilityRef.(string))) @@ -314,7 +418,6 @@ func ExpandBatchPoolStartTask(list []interface{}) (*batch.StartTask, error) { ElevationLevel: batch.ElevationLevel(autoUserMap["elevation_level"].(string)), Scope: batch.AutoUserScope(autoUserMap["scope"].(string)), } - } } else if userNameValue, ok := userIdentityValue["username"]; ok { userName := userNameValue.(string) diff --git a/azurerm/helpers/azure/cosmos.go b/azurerm/helpers/azure/cosmos.go index f7f21f8c743c..c3a9b09675a6 100644 --- a/azurerm/helpers/azure/cosmos.go +++ b/azurerm/helpers/azure/cosmos.go @@ -90,6 +90,28 @@ func ParseCosmosDatabaseCollectionID(id string) (*CosmosDatabaseCollectionID, er }, nil } +type CosmosDatabaseContainerID struct { + CosmosDatabaseID + Container string +} + +func ParseCosmosDatabaseContainerID(id string) (*CosmosDatabaseContainerID, error) { + subid, err := ParseCosmosDatabaseID(id) + if err != nil { + return nil, err + } + + container, ok := subid.Path["containers"] + if !ok { + return nil, fmt.Errorf("Error: Unable to parse Cosmos Database Container Resource ID: containers is missing from: %s", id) + } + + return &CosmosDatabaseContainerID{ + CosmosDatabaseID: *subid, + Container: container, + }, nil +} + type CosmosKeyspaceID struct { CosmosAccountID Keyspace string diff --git a/azurerm/helpers/azure/elasticpool.go b/azurerm/helpers/azure/elasticpool.go index 9d9b150a843a..d0934991a782 100644 --- a/azurerm/helpers/azure/elasticpool.go +++ b/azurerm/helpers/azure/elasticpool.go @@ -187,7 +187,6 @@ var getTierFromName = map[string]string{ } func MSSQLElasticPoolValidateSKU(diff *schema.ResourceDiff) error { - name := diff.Get("sku.0.name") tier := diff.Get("sku.0.tier") capacity := diff.Get("sku.0.capacity") @@ -324,7 +323,6 @@ func buildErrorString(stub string, m map[int]float64) string { } func doDTUSKUValidation(s sku) error { - if s.MaxAllowedGB == 0 { return fmt.Errorf(getDTUCapacityErrorMsg(s)) } @@ -363,7 +361,6 @@ func doDTUSKUValidation(s sku) error { } func doVCoreSKUValidation(s sku) error { - if s.MaxAllowedGB == 0 { return fmt.Errorf(getVCoreCapacityErrorMsg(s)) } diff --git a/azurerm/helpers/azure/eventhub.go b/azurerm/helpers/azure/eventhub.go index db1bd149e8b6..cc137ab43015 100644 --- a/azurerm/helpers/azure/eventhub.go +++ b/azurerm/helpers/azure/eventhub.go @@ -81,7 +81,6 @@ func FlattenEventHubAuthorizationRuleRights(rights *[]eventhub.AccessRights) (li } func EventHubAuthorizationRuleSchemaFrom(s map[string]*schema.Schema) map[string]*schema.Schema { - authSchema := map[string]*schema.Schema{ "listen": { Type: schema.TypeBool, diff --git a/azurerm/helpers/azure/hdinsight.go b/azurerm/helpers/azure/hdinsight.go index 4388c80fef16..13e30f12af32 100644 --- a/azurerm/helpers/azure/hdinsight.go +++ b/azurerm/helpers/azure/hdinsight.go @@ -54,7 +54,7 @@ func SchemaHDInsightClusterVersion() *schema.Schema { } } -func hdinsightClusterVersionDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { +func hdinsightClusterVersionDiffSuppressFunc(_, old, new string, _ *schema.ResourceData) bool { // `3.6` gets converted to `3.6.1000.67`; so let's just compare major/minor if possible o := strings.Split(old, ".") n := strings.Split(new, ".") @@ -94,6 +94,10 @@ func SchemaHDInsightsGateway() *schema.Schema { Required: true, ForceNew: true, Sensitive: true, + // Azure returns the key as *****. We'll suppress that here. + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return (new == d.Get(k).(string)) && (old == "*****") + }, }, }, }, diff --git a/azurerm/helpers/azure/key_vault.go b/azurerm/helpers/azure/key_vault.go index 75be0c037c3d..7957380bb594 100644 --- a/azurerm/helpers/azure/key_vault.go +++ b/azurerm/helpers/azure/key_vault.go @@ -8,8 +8,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func GetKeyVaultBaseUrlFromID(ctx context.Context, client keyvault.VaultsClient, keyVaultId string) (string, error) { - +func GetKeyVaultBaseUrlFromID(ctx context.Context, client *keyvault.VaultsClient, keyVaultId string) (string, error) { if keyVaultId == "" { return "", fmt.Errorf("keyVaultId is empty") } @@ -40,7 +39,7 @@ func GetKeyVaultBaseUrlFromID(ctx context.Context, client keyvault.VaultsClient, return *resp.Properties.VaultURI, nil } -func GetKeyVaultIDFromBaseUrl(ctx context.Context, client keyvault.VaultsClient, keyVaultUrl string) (*string, error) { +func GetKeyVaultIDFromBaseUrl(ctx context.Context, client *keyvault.VaultsClient, keyVaultUrl string) (*string, error) { list, err := client.ListComplete(ctx, utils.Int32(1000)) if err != nil { return nil, fmt.Errorf("Error GetKeyVaultId unable to list Key Vaults %v", err) @@ -89,8 +88,7 @@ func GetKeyVaultIDFromBaseUrl(ctx context.Context, client keyvault.VaultsClient, return nil, nil } -func KeyVaultExists(ctx context.Context, client keyvault.VaultsClient, keyVaultId string) (bool, error) { - +func KeyVaultExists(ctx context.Context, client *keyvault.VaultsClient, keyVaultId string) (bool, error) { if keyVaultId == "" { return false, fmt.Errorf("keyVaultId is empty") } diff --git a/azurerm/helpers/azure/location.go b/azurerm/helpers/azure/location.go index 6dcfa091af6a..c2d0341aaefa 100644 --- a/azurerm/helpers/azure/location.go +++ b/azurerm/helpers/azure/location.go @@ -27,6 +27,7 @@ func SchemaLocationOptional() *schema.Schema { } } +// todo should we change this to SchemaLocationComputed func SchemaLocationForDataSource() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, @@ -54,7 +55,7 @@ func NormalizeLocation(location interface{}) string { return strings.Replace(strings.ToLower(input), " ", "", -1) } -func SuppressLocationDiff(k, old, new string, d *schema.ResourceData) bool { +func SuppressLocationDiff(_, old, new string, _ *schema.ResourceData) bool { return NormalizeLocation(old) == NormalizeLocation(new) } diff --git a/azurerm/helpers/azure/mssql.go b/azurerm/helpers/azure/mssql.go index 188bd78fdd8e..de9e37befe34 100644 --- a/azurerm/helpers/azure/mssql.go +++ b/azurerm/helpers/azure/mssql.go @@ -24,6 +24,14 @@ func ValidateMsSqlDatabaseName(i interface{}, k string) (_ []string, errors []er return nil, errors } +func ValidateMsSqlFailoverGroupName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[0-9a-z]([-0-9a-z]{0,61}[0-9a-z])?$`); !m { + errors = append(regexErrs, fmt.Errorf("%q can contain only lowercase letters, numbers, and '-', but can't start or end with '-'.", k)) + } + + return nil, errors +} + //Following characters and any control characters are not allowed for resource name '%,&,\\\\,?,/'.\" //The name can not end with characters: '. ' //TODO: unsure about length, was able to deploy one at 120 diff --git a/azurerm/helpers/azure/mysql.go b/azurerm/helpers/azure/mysql.go new file mode 100644 index 000000000000..a54fe7f79973 --- /dev/null +++ b/azurerm/helpers/azure/mysql.go @@ -0,0 +1,15 @@ +package azure + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" +) + +func ValidateMySqlServerName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[0-9a-z]([-0-9a-z]{0,61}[0-9a-z])?$`); !m { + errors = append(regexErrs, fmt.Errorf("%q can contain only lowercase letters, numbers, and '-', but can't start or end with '-' or have more than 63 characters.", k)) + } + + return nil, errors +} diff --git a/azurerm/helpers/azure/network_interface.go b/azurerm/helpers/azure/network_interface.go index f7df6ee3a2da..4766ff67053f 100644 --- a/azurerm/helpers/azure/network_interface.go +++ b/azurerm/helpers/azure/network_interface.go @@ -1,6 +1,6 @@ package azure -import "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" +import "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" func FindNetworkInterfaceIPConfiguration(input *[]network.InterfaceIPConfiguration, name string) *network.InterfaceIPConfiguration { if input == nil { diff --git a/azurerm/helpers/azure/recovery_services_vault.go b/azurerm/helpers/azure/recovery_services_vault.go new file mode 100644 index 000000000000..e25e3bcb0d97 --- /dev/null +++ b/azurerm/helpers/azure/recovery_services_vault.go @@ -0,0 +1,21 @@ +package azure + +import ( + "regexp" + "strings" + + "github.com/hashicorp/terraform/helper/validation" +) + +func ValidateRecoveryServicesVaultName(v interface{}, k string) (warnings []string, errors []error) { + regexpValidator := validation.StringMatch( + regexp.MustCompile("^[a-zA-Z][-a-zA-Z0-9]{1,49}$"), + "Recovery Service Vault name must be 2 - 50 characters long, start with a letter, contain only letters, numbers and hyphens.", + ) + return regexpValidator(v, k) +} + +// This code is a workaround for this bug https://github.com/Azure/azure-sdk-for-go/issues/2824 +func HandleAzureSdkForGoBug2824(id string) string { + return strings.Replace(id, "/Subscriptions/", "/subscriptions/", 1) +} diff --git a/azurerm/helpers/azure/resource_group.go b/azurerm/helpers/azure/resource_group.go index 2a782c129f10..cdc903807572 100644 --- a/azurerm/helpers/azure/resource_group.go +++ b/azurerm/helpers/azure/resource_group.go @@ -18,6 +18,15 @@ func SchemaResourceGroupName() *schema.Schema { } } +func SchemaResourceGroupNameDeprecated() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Deprecated: "This field has been deprecated and is no longer used - will be removed in 2.0 of the Azure Provider", + } +} + func SchemaResourceGroupNameDiffSuppress() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, @@ -38,8 +47,8 @@ func SchemaResourceGroupNameForDataSource() *schema.Schema { func validateResourceGroupName(v interface{}, k string) (warnings []string, errors []error) { value := v.(string) - if len(value) > 80 { - errors = append(errors, fmt.Errorf("%q may not exceed 80 characters in length", k)) + if len(value) > 90 { + errors = append(errors, fmt.Errorf("%q may not exceed 90 characters in length", k)) } if strings.HasSuffix(value, ".") { diff --git a/azurerm/helpers/azure/resource_group_test.go b/azurerm/helpers/azure/resource_group_test.go index 004beb31151a..d608b5d11b32 100644 --- a/azurerm/helpers/azure/resource_group_test.go +++ b/azurerm/helpers/azure/resource_group_test.go @@ -48,11 +48,11 @@ func TestValidateResourceGroupName(t *testing.T) { ErrCount: 1, }, { - Value: acctest.RandString(80), + Value: acctest.RandString(90), ErrCount: 0, }, { - Value: acctest.RandString(81), + Value: acctest.RandString(91), ErrCount: 1, }, } @@ -61,7 +61,8 @@ func TestValidateResourceGroupName(t *testing.T) { _, errors := validateResourceGroupName(tc.Value, "azurerm_resource_group") if len(errors) != tc.ErrCount { - t.Fatalf("Expected validateResourceGroupName to trigger '%d' errors for '%s' - got '%d'", tc.ErrCount, tc.Value, len(errors)) + t.Fatalf("Expected "+ + "validateResourceGroupName to trigger '%d' errors for '%s' - got '%d'", tc.ErrCount, tc.Value, len(errors)) } } } diff --git a/azurerm/helpers/azure/resourceid.go b/azurerm/helpers/azure/resourceid.go index 7872d0ae12c2..d426ceeda4c5 100644 --- a/azurerm/helpers/azure/resourceid.go +++ b/azurerm/helpers/azure/resourceid.go @@ -81,8 +81,6 @@ func ParseAzureResourceID(id string) (*ResourceID, error) { if resourceGroup, ok := componentMap["resourcegroups"]; ok { idObj.ResourceGroup = resourceGroup delete(componentMap, "resourcegroups") - } else { - return nil, fmt.Errorf("No resource group name found in: %q", path) } } diff --git a/azurerm/helpers/azure/resourceid_test.go b/azurerm/helpers/azure/resourceid_test.go index 5bc6f5b63aef..08d12f661f59 100644 --- a/azurerm/helpers/azure/resourceid_test.go +++ b/azurerm/helpers/azure/resourceid_test.go @@ -30,8 +30,13 @@ func TestParseAzureResourceID(t *testing.T) { }, { "/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038", - nil, - true, + &ResourceID{ + SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038", + ResourceGroup: "", + Provider: "", + Path: map[string]string{}, + }, + false, }, { "subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038", @@ -140,9 +145,23 @@ func TestParseAzureResourceID(t *testing.T) { }, false, }, + { + // missing resource group + "/subscriptions/11111111-1111-1111-1111-111111111111/providers/Microsoft.ApiManagement/service/service1/subscriptions/22222222-2222-2222-2222-222222222222", + &ResourceID{ + SubscriptionID: "11111111-1111-1111-1111-111111111111", + Provider: "Microsoft.ApiManagement", + Path: map[string]string{ + "service": "service1", + "subscriptions": "22222222-2222-2222-2222-222222222222", + }, + }, + false, + }, } for _, test := range testCases { + t.Logf("[DEBUG] Testing %q", test.id) parsed, err := ParseAzureResourceID(test.id) if test.expectError && err != nil { continue diff --git a/azurerm/helpers/azure/sender.go b/azurerm/helpers/azure/sender.go deleted file mode 100644 index 901e0068d676..000000000000 --- a/azurerm/helpers/azure/sender.go +++ /dev/null @@ -1,57 +0,0 @@ -package azure - -import ( - "log" - "net/http" - "net/http/httputil" - - "github.com/Azure/go-autorest/autorest" -) - -func BuildSender() autorest.Sender { - return autorest.DecorateSender(&http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - }, - }, withRequestLogging()) -} - -func withRequestLogging() autorest.SendDecorator { - return func(s autorest.Sender) autorest.Sender { - return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { - // strip the authorization header prior to printing - authHeaderName := "Authorization" - auth := r.Header.Get(authHeaderName) - if auth != "" { - r.Header.Del(authHeaderName) - } - - // dump request to wire format - if dump, err := httputil.DumpRequestOut(r, true); err == nil { - log.Printf("[DEBUG] AzureRM Request: \n%s\n", dump) - } else { - // fallback to basic message - log.Printf("[DEBUG] AzureRM Request: %s to %s\n", r.Method, r.URL) - } - - // add the auth header back - if auth != "" { - r.Header.Add(authHeaderName, auth) - } - - resp, err := s.Do(r) - if resp != nil { - // dump response to wire format - if dump, err2 := httputil.DumpResponse(resp, true); err2 == nil { - log.Printf("[DEBUG] AzureRM Response for %s: \n%s\n", r.URL, dump) - } else { - // fallback to basic message - log.Printf("[DEBUG] AzureRM Response: %s for %s\n", resp.Status, r.URL) - } - } else { - log.Printf("[DEBUG] Request to %s completed with no response", r.URL) - } - return resp, err - }) - } -} diff --git a/azurerm/helpers/azure/servicebus.go b/azurerm/helpers/azure/servicebus.go index d98d54b36804..370ee72baf8d 100644 --- a/azurerm/helpers/azure/servicebus.go +++ b/azurerm/helpers/azure/servicebus.go @@ -102,7 +102,6 @@ func MergeSchema(a map[string]*schema.Schema, b map[string]*schema.Schema) map[s } func ServiceBusAuthorizationRuleSchemaFrom(s map[string]*schema.Schema) map[string]*schema.Schema { - authSchema := map[string]*schema.Schema{ "listen": { Type: schema.TypeBool, diff --git a/azurerm/helpers/azure/sku.go b/azurerm/helpers/azure/sku.go new file mode 100644 index 000000000000..ab9bdc137e92 --- /dev/null +++ b/azurerm/helpers/azure/sku.go @@ -0,0 +1,58 @@ +package azure + +import ( + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform/helper/schema" +) + +func SplitSku(sku string) (string, int32, error) { + skuParts := strings.Split(sku, "_") + + if len(skuParts) != 2 { + return "", -1, fmt.Errorf("sku_name(%s) is not formatted properly.", sku) + } + + capacity, err := strconv.Atoi(skuParts[1]) + + if err != nil { + return "", -1, fmt.Errorf("%s in sku_name is not a valid value.", skuParts[1]) + } + + return skuParts[0], int32(capacity), nil +} + +// MinCapacitySkuNameInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type string and matches the value of an element in the valid slice +// will test with in lower case if ignoreCase is true will also validate if the +// capacity if above passed minCapacity value +func MinCapacitySkuNameInSlice(valid []string, minCapacity int32, ignoreCase bool) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + name, capacity, err := SplitSku(v) + + if err != nil { + es = append(es, err) + return + } + + for _, str := range valid { + if name == str || (ignoreCase && strings.EqualFold(name, str)) { + if capacity < minCapacity { + es = append(es, fmt.Errorf("expected %s capacity value to be greater that %d, got %d", k, minCapacity, capacity)) + } + return + } + } + + es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, name)) + return + } +} diff --git a/azurerm/helpers/azure/subscription.go b/azurerm/helpers/azure/subscription.go index 38bf5c23a46d..b4333375b824 100644 --- a/azurerm/helpers/azure/subscription.go +++ b/azurerm/helpers/azure/subscription.go @@ -12,6 +12,11 @@ func SchemaSubscription(subscriptionIDOptional bool) map[string]*schema.Schema { Computed: true, }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { Type: schema.TypeString, Computed: true, diff --git a/azurerm/helpers/azure/web.go b/azurerm/helpers/azure/web.go new file mode 100644 index 000000000000..beeeb2298c6b --- /dev/null +++ b/azurerm/helpers/azure/web.go @@ -0,0 +1,81 @@ +package azure + +import ( + "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func SchemaWebCorsSettings() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_origins": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "support_credentials": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + } +} + +func ExpandWebCorsSettings(input interface{}) web.CorsSettings { + settings := input.([]interface{}) + corsSettings := web.CorsSettings{} + + if len(settings) == 0 { + return corsSettings + } + + setting := settings[0].(map[string]interface{}) + + if v, ok := setting["allowed_origins"]; ok { + input := v.(*schema.Set).List() + + allowedOrigins := make([]string, 0) + for _, param := range input { + allowedOrigins = append(allowedOrigins, param.(string)) + } + + corsSettings.AllowedOrigins = &allowedOrigins + } + + if v, ok := setting["support_credentials"]; ok { + corsSettings.SupportCredentials = utils.Bool(v.(bool)) + } + + return corsSettings +} + +func FlattenWebCorsSettings(input *web.CorsSettings) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + result := make(map[string]interface{}) + + allowedOrigins := make([]interface{}, 0) + if s := input.AllowedOrigins; s != nil { + for _, v := range *s { + allowedOrigins = append(allowedOrigins, v) + } + } + result["allowed_origins"] = schema.NewSet(schema.HashString, allowedOrigins) + + if input.SupportCredentials != nil { + result["support_credentials"] = *input.SupportCredentials + } + + return append(results, result) +} diff --git a/azurerm/helpers/tf/acctest_test.go b/azurerm/helpers/tf/acctest_test.go index bcc98e0281b3..800338cfe1e2 100644 --- a/azurerm/helpers/tf/acctest_test.go +++ b/azurerm/helpers/tf/acctest_test.go @@ -13,6 +13,5 @@ func TestAccRandTimeInt(t *testing.T) { if ri > 999999999999999999 { t.Fatalf("AccRandTimeInt returned a value (%d) longer then expected", ri) } - }) } diff --git a/azurerm/helpers/validate/api_management.go b/azurerm/helpers/validate/api_management.go index 70054fac3390..66f850cb896b 100644 --- a/azurerm/helpers/validate/api_management.go +++ b/azurerm/helpers/validate/api_management.go @@ -76,3 +76,14 @@ func ApiManagementApiPath(v interface{}, k string) (ws []string, es []error) { } return ws, es } + +func ApiManagementBackendName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + // From https://docs.microsoft.com/en-us/rest/api/apimanagement/2018-01-01/backend/createorupdate#uri-parameters + if matched := regexp.MustCompile(`(^[\w]+$)|(^[\w][\w\-]+[\w]$)`).Match([]byte(value)); !matched { + errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters and dashes up to 50 characters in length", k)) + } + + return warnings, errors +} diff --git a/azurerm/helpers/validate/compute.go b/azurerm/helpers/validate/compute.go index df6cf2982ab8..ab5978b1d79c 100644 --- a/azurerm/helpers/validate/compute.go +++ b/azurerm/helpers/validate/compute.go @@ -3,6 +3,9 @@ package validate import ( "fmt" "regexp" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func SharedImageGalleryName(v interface{}, k string) (warnings []string, errors []error) { @@ -48,3 +51,117 @@ func SharedImageVersionName(v interface{}, k string) (warnings []string, errors return warnings, errors } + +func VirtualMachineTimeZone() schema.SchemaValidateFunc { + // Candidates are listed here: http://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/ + candidates := []string{ + "", + "Afghanistan Standard Time", + "Alaskan Standard Time", + "Arab Standard Time", + "Arabian Standard Time", + "Arabic Standard Time", + "Argentina Standard Time", + "Atlantic Standard Time", + "AUS Central Standard Time", + "AUS Eastern Standard Time", + "Azerbaijan Standard Time", + "Azores Standard Time", + "Bahia Standard Time", + "Bangladesh Standard Time", + "Belarus Standard Time", + "Canada Central Standard Time", + "Cape Verde Standard Time", + "Caucasus Standard Time", + "Cen. Australia Standard Time", + "Central America Standard Time", + "Central Asia Standard Time", + "Central Brazilian Standard Time", + "Central Europe Standard Time", + "Central European Standard Time", + "Central Pacific Standard Time", + "Central Standard Time (Mexico)", + "Central Standard Time", + "China Standard Time", + "Dateline Standard Time", + "E. Africa Standard Time", + "E. Australia Standard Time", + "E. Europe Standard Time", + "E. South America Standard Time", + "Eastern Standard Time (Mexico)", + "Eastern Standard Time", + "Egypt Standard Time", + "Ekaterinburg Standard Time", + "Fiji Standard Time", + "FLE Standard Time", + "Georgian Standard Time", + "GMT Standard Time", + "Greenland Standard Time", + "Greenwich Standard Time", + "GTB Standard Time", + "Hawaiian Standard Time", + "India Standard Time", + "Iran Standard Time", + "Israel Standard Time", + "Jordan Standard Time", + "Kaliningrad Standard Time", + "Korea Standard Time", + "Libya Standard Time", + "Line Islands Standard Time", + "Magadan Standard Time", + "Mauritius Standard Time", + "Middle East Standard Time", + "Montevideo Standard Time", + "Morocco Standard Time", + "Mountain Standard Time (Mexico)", + "Mountain Standard Time", + "Myanmar Standard Time", + "N. Central Asia Standard Time", + "Namibia Standard Time", + "Nepal Standard Time", + "New Zealand Standard Time", + "Newfoundland Standard Time", + "North Asia East Standard Time", + "North Asia Standard Time", + "Pacific SA Standard Time", + "Pacific Standard Time (Mexico)", + "Pacific Standard Time", + "Pakistan Standard Time", + "Paraguay Standard Time", + "Romance Standard Time", + "Russia Time Zone 10", + "Russia Time Zone 11", + "Russia Time Zone 3", + "Russian Standard Time", + "SA Eastern Standard Time", + "SA Pacific Standard Time", + "SA Western Standard Time", + "Samoa Standard Time", + "SE Asia Standard Time", + "Singapore Standard Time", + "South Africa Standard Time", + "Sri Lanka Standard Time", + "Syria Standard Time", + "Taipei Standard Time", + "Tasmania Standard Time", + "Tokyo Standard Time", + "Tonga Standard Time", + "Turkey Standard Time", + "Ulaanbaatar Standard Time", + "US Eastern Standard Time", + "US Mountain Standard Time", + "UTC", + "UTC+12", + "UTC-02", + "UTC-11", + "Venezuela Standard Time", + "Vladivostok Standard Time", + "W. Australia Standard Time", + "W. Central Africa Standard Time", + "W. Europe Standard Time", + "West Asia Standard Time", + "West Pacific Standard Time", + "Yakutsk Standard Time", + } + return validation.StringInSlice(candidates, true) +} diff --git a/azurerm/helpers/validate/compute_test.go b/azurerm/helpers/validate/compute_test.go index 6131a4d24cdc..d7118287ec79 100644 --- a/azurerm/helpers/validate/compute_test.go +++ b/azurerm/helpers/validate/compute_test.go @@ -174,3 +174,41 @@ func TestSharedImageVersionName(t *testing.T) { }) } } + +func TestVirtualMachineTimeZone(t *testing.T) { + cases := []struct { + Value string + Errors int + }{ + { + Value: "", + Errors: 0, + }, + { + Value: "UTC", + Errors: 0, + }, + { + Value: "China Standard Time", + Errors: 0, + }, + { + // Valid UTC time zone + Value: "utc-11", + Errors: 0, + }, + { + // Invalid UTC time zone + Value: "UTC-30", + Errors: 1, + }, + } + + for _, tc := range cases { + _, errors := VirtualMachineTimeZone()(tc.Value, "unittest") + + if len(errors) != tc.Errors { + t.Fatalf("Expected VirtualMachineTimeZone to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) + } + } +} diff --git a/azurerm/helpers/validate/database.go b/azurerm/helpers/validate/database.go new file mode 100644 index 000000000000..bda83f52b22a --- /dev/null +++ b/azurerm/helpers/validate/database.go @@ -0,0 +1,23 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func DatabaseCollation(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + matched, _ := regexp.MatchString(`^[A-Za-z0-9_. ]+$`, v) + + if !matched { + errors = append(errors, fmt.Errorf("%s contains invalid characters, only underscores are supported, got %s", k, v)) + return + } + + return warnings, errors +} diff --git a/azurerm/helpers/validate/database_test.go b/azurerm/helpers/validate/database_test.go new file mode 100644 index 000000000000..773ee5a203d0 --- /dev/null +++ b/azurerm/helpers/validate/database_test.go @@ -0,0 +1,34 @@ +package validate + +import "testing" + +func TestDatabaseCollation(t *testing.T) { + cases := []struct { + Value string + Errors int + }{ + { + Value: "en-US", + Errors: 1, + }, + { + Value: "en_US", + Errors: 0, + }, + { + Value: "en US", + Errors: 0, + }, + { + Value: "English_United States.1252", + Errors: 0, + }, + } + + for _, tc := range cases { + _, errors := DatabaseCollation(tc.Value, "collation") + if len(errors) != tc.Errors { + t.Fatalf("Expected DatabaseCollation to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) + } + } +} diff --git a/azurerm/helpers/validate/int_test.go b/azurerm/helpers/validate/int_test.go index 5a482b87c492..ea08a3fc438d 100644 --- a/azurerm/helpers/validate/int_test.go +++ b/azurerm/helpers/validate/int_test.go @@ -100,7 +100,6 @@ func TestIntDivisibleBy(t *testing.T) { } func TestIntInSlice(t *testing.T) { - cases := []struct { Input []int Value int @@ -140,5 +139,4 @@ func TestIntInSlice(t *testing.T) { t.Fatalf("Expected the validateIntInSlice trigger a validation error for input: %+v looking for %+v", tc.Input, tc.Value) } } - } diff --git a/azurerm/helpers/validate/mariadb.go b/azurerm/helpers/validate/mariadb.go new file mode 100644 index 000000000000..0cd951947bf6 --- /dev/null +++ b/azurerm/helpers/validate/mariadb.go @@ -0,0 +1,28 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func MariaDBFirewallRuleName(v interface{}, _ string) (warnings []string, errors []error) { + value := v.(string) + + // Firewall rule name can contain alphanumeric characters and hyphens and must be 1 - 128 characters long + if matched := regexp.MustCompile("^[-a-z0-9]{1,128}$").Match([]byte(value)); !matched { + errors = append(errors, fmt.Errorf("Firewall rule name must be 1 - 128 characters long, contain only letters, numbers and hyphens.")) + } + + return warnings, errors +} + +func MariaDBServerName(v interface{}, _ string) (warnings []string, errors []error) { + value := v.(string) + + // MariaDB server name can contain alphanumeric characters and hyphens and must be 3 - 63 characters long + if matched := regexp.MustCompile("^[-a-z0-9]{3,63}$").Match([]byte(value)); !matched { + errors = append(errors, fmt.Errorf("Server name must be 3 - 63 characters long, contain only letters, numbers and hyphens.")) + } + + return warnings, errors +} diff --git a/azurerm/helpers/validate/network.go b/azurerm/helpers/validate/network.go index fac8c52d1e6e..cfc4d6ecde07 100644 --- a/azurerm/helpers/validate/network.go +++ b/azurerm/helpers/validate/network.go @@ -27,7 +27,6 @@ func validateIpv6Address(i interface{}, k string, allowEmpty bool) (warnings []s } return warnings, errors - } func CIDR(i interface{}, k string) (warnings []string, errors []error) { diff --git a/azurerm/helpers/validate/network_test.go b/azurerm/helpers/validate/network_test.go index 263b288aaad1..61e4db8c0499 100644 --- a/azurerm/helpers/validate/network_test.go +++ b/azurerm/helpers/validate/network_test.go @@ -91,7 +91,6 @@ func TestIPv6Address(t *testing.T) { } }) } - } func TestIPv4Address(t *testing.T) { diff --git a/azurerm/helpers/validate/shared_access_signature.go b/azurerm/helpers/validate/shared_access_signature.go new file mode 100644 index 000000000000..ed3e3078dd2e --- /dev/null +++ b/azurerm/helpers/validate/shared_access_signature.go @@ -0,0 +1,32 @@ +package validate + +import ( + "fmt" + "net" + "strings" +) + +func SharedAccessSignatureIP(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if net.ParseIP(value) != nil { + return warnings, errors + } + + ipRange := strings.Split(value, "-") + + if len(ipRange) != 2 || net.ParseIP(ipRange[0]) == nil || net.ParseIP(ipRange[1]) == nil { + errors = append(errors, fmt.Errorf("%q must be a valid ipv4 address or a range of ipv4 addresses separated by a hyphen", k)) + return warnings, errors + } + + ip1 := ipRange[0] + ip2 := ipRange[1] + + if ip1 == ip2 { + errors = append(errors, fmt.Errorf("IP addresses in a range for %q must be not be identical", k)) + return warnings, errors + } + + return warnings, errors +} diff --git a/azurerm/helpers/validate/shared_access_signature_test.go b/azurerm/helpers/validate/shared_access_signature_test.go new file mode 100644 index 000000000000..f2206723ecd4 --- /dev/null +++ b/azurerm/helpers/validate/shared_access_signature_test.go @@ -0,0 +1,52 @@ +package validate + +import ( + "testing" +) + +func TestSharedAccessSignatureIP(t *testing.T) { + cases := []struct { + Input string + ShouldError bool + }{ + { + Input: "dfdsdfds", + ShouldError: true, + }, + { + Input: "192.168.0.1", + ShouldError: false, + }, + { + Input: "sdfsdf-4334", + ShouldError: true, + }, + { + Input: "172.77.62-abc", + ShouldError: true, + }, + { + Input: "66.247.118.148-66.247.118.148", + ShouldError: true, + }, + { + Input: "66.247.118.148-66.247.118.149", + ShouldError: false, + }, + } + + for _, tc := range cases { + t.Run(tc.Input, func(t *testing.T) { + _, errors := SharedAccessSignatureIP(tc.Input, "ip") + + hasErrors := len(errors) > 0 + if !hasErrors && tc.ShouldError { + t.Fatalf("Expected an error but didn't get one for %q", tc.Input) + } + + if hasErrors && !tc.ShouldError { + t.Fatalf("Expected to get no errors for %q but got %d", tc.Input, len(errors)) + } + }) + } +} diff --git a/azurerm/helpers/validate/storage.go b/azurerm/helpers/validate/storage.go new file mode 100644 index 000000000000..445bac547097 --- /dev/null +++ b/azurerm/helpers/validate/storage.go @@ -0,0 +1,25 @@ +package validate + +import ( + "fmt" + "regexp" + "strings" +) + +func StorageShareDirectoryName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + // File share names can contain only uppercase and lowercase letters, numbers, and hyphens, + // and must begin and end with a letter or a number. + // however they can be nested (e.g. foo/bar) + if !regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9-]+[A-Za-z0-9]$`).MatchString(value) && !regexp.MustCompile(`^[A-Za-z0-9]{1,}/[A-Za-z0-9]{1,}$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%s must contain only uppercase and lowercase alphanumeric characters, numbers and hyphens. It must start and end with a letter and end only with a number or letter", k)) + } + + // The name cannot contain two consecutive hyphens. + if strings.Contains(value, "--") { + errors = append(errors, fmt.Errorf("%s cannot contain two concecutive hyphens", k)) + } + + return warnings, errors +} diff --git a/azurerm/helpers/validate/storage_test.go b/azurerm/helpers/validate/storage_test.go new file mode 100644 index 000000000000..d0b29c96b1a3 --- /dev/null +++ b/azurerm/helpers/validate/storage_test.go @@ -0,0 +1,73 @@ +package validate + +import "testing" + +func TestValidateStorageShareDirectoryName(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "abc123", + Expected: true, + }, + { + Input: "123abc", + Expected: true, + }, + { + Input: "123-abc", + Expected: true, + }, + { + Input: "-123-abc", + Expected: false, + }, + { + Input: "123-abc-", + Expected: false, + }, + { + Input: "123--abc", + Expected: false, + }, + { + Input: "hello/world", + Expected: true, + }, + { + Input: "hello/", + Expected: false, + }, + { + Input: "Abc123", + Expected: true, + }, + { + Input: "abc123A", + Expected: true, + }, + { + Input: "abC123", + Expected: true, + }, + } + + for _, v := range testCases { + t.Logf("[DEBUG] Test Input %q", v.Input) + + warnings, errors := StorageShareDirectoryName(v.Input, "name") + if len(warnings) != 0 { + t.Fatalf("Expected no warnings but got %d", len(warnings)) + } + + result := len(errors) == 0 + if result != v.Expected { + t.Fatalf("Expected the result to be %t but got %t (and %d errors)", v.Expected, result, len(errors)) + } + } +} diff --git a/azurerm/helpers/validate/time.go b/azurerm/helpers/validate/time.go index 5e694c682fc8..c4ee63dff049 100644 --- a/azurerm/helpers/validate/time.go +++ b/azurerm/helpers/validate/time.go @@ -2,13 +2,30 @@ package validate import ( "fmt" + "regexp" "time" "github.com/Azure/go-autorest/autorest/date" + iso8601 "github.com/btubbs/datetime" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" ) +func ISO8601Duration(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + matched, _ := regexp.MatchString(`^P([0-9]+Y)?([0-9]+M)?([0-9]+W)?([0-9]+D)?(T([0-9]+H)?([0-9]+M)?([0-9]+(\.?[0-9]+)?S)?)?$`, v) + + if !matched { + errors = append(errors, fmt.Errorf("expected %s to be in ISO 8601 duration format, got %s", k, v)) + } + return warnings, errors +} + //todo, now in terraform helper, switch over once vended // -> https://github.com/hashicorp/terraform/blob/master/helper/validation/validation.go#L263 func RFC3339Time(i interface{}, k string) (warnings []string, errors []error) { @@ -25,6 +42,20 @@ func RFC3339Time(i interface{}, k string) (warnings []string, errors []error) { return warnings, errors } +func ISO8601DateTime(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := iso8601.Parse(v, time.UTC); err != nil { + errors = append(errors, fmt.Errorf("%q has the invalid ISO8601 date format %q: %+v", k, i, err)) + } + + return warnings, errors +} + // RFC3339 date is duration d or greater into the future func RFC3339DateInFutureBy(d time.Duration) schema.SchemaValidateFunc { return func(i interface{}, k string) (warnings []string, errors []error) { diff --git a/azurerm/helpers/validate/time_test.go b/azurerm/helpers/validate/time_test.go index 7daf5a0ca545..b9751d6398f9 100644 --- a/azurerm/helpers/validate/time_test.go +++ b/azurerm/helpers/validate/time_test.go @@ -47,7 +47,61 @@ func TestRFC3339Time(t *testing.T) { } } -func TestRfc3339DateInFutureBy(t *testing.T) { +func TestISO8601DateTime(t *testing.T) { + cases := []struct { + Time string + Errors int + }{ + { + Time: "", + Errors: 1, + }, + { + Time: "this is not a date", + Errors: 1, + }, + { + Time: "2000-06-31", // No 31st of 6th + Errors: 1, + }, + { + Time: "01/21/2015", // not valid US date with slashes + Errors: 1, + }, + { + Time: "01-21-2015", // not valid US date with dashes + Errors: 1, + }, + { + Time: "2000-01-01", + Errors: 0, + }, + { + Time: "2000-01-01T01:23:45", + Errors: 0, + }, + { + Time: "2000-01-01T01:23:45Z", + Errors: 0, + }, + { + Time: "2000-01-01T01:23:45+00:00", + Errors: 0, + }, + } + + for _, tc := range cases { + t.Run(tc.Time, func(t *testing.T) { + _, errors := ISO8601DateTime(tc.Time, "test") + + if len(errors) != tc.Errors { + t.Fatalf("Expected ISO8601DateTime to have %d but got %d errors for %q", tc.Errors, len(errors), tc.Time) + } + }) + } +} + +func TestRFC3339DateInFutureBy(t *testing.T) { cases := []struct { Name string Time string @@ -102,3 +156,43 @@ func TestRfc3339DateInFutureBy(t *testing.T) { }) } } +func TestISO8601Duration(t *testing.T) { + cases := []struct { + Value string + Errors int + }{ + { + // Date components only + Value: "P1Y2M3D", + Errors: 0, + }, + { + // Time components only + Value: "PT7H42M3S", + Errors: 0, + }, + { + // Date and time components + Value: "P1Y2M3DT7H42M3S", + Errors: 0, + }, + { + // Invalid prefix + Value: "1Y2M3DT7H42M3S", + Errors: 1, + }, + { + // Wrong order of components, i.e. invalid format + Value: "PT7H42M3S1Y2M3D", + Errors: 1, + }, + } + + for _, tc := range cases { + _, errors := ISO8601Duration(tc.Value, "example") + + if len(errors) != tc.Errors { + t.Fatalf("Expected ISO8601Duration to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) + } + } +} diff --git a/azurerm/internal/authorizers/authorizer_shared_key.go b/azurerm/internal/authorizers/authorizer_shared_key.go new file mode 100644 index 000000000000..71c90487717f --- /dev/null +++ b/azurerm/internal/authorizers/authorizer_shared_key.go @@ -0,0 +1,111 @@ +package authorizers + +import ( + "fmt" + "net/http" + "strings" + + "github.com/Azure/go-autorest/autorest" +) + +// TODO: switch to using the version from github.com/Azure/go-autorest +// once https://github.com/Azure/go-autorest/pull/416 has been merged + +// SharedKeyAuthorizer implements an authorization for Shared Key +// this can be used for interaction with Blob, File and Queue Storage Endpoints +type SharedKeyAuthorizer struct { + storageAccountName string + storageAccountKey string +} + +// NewSharedKeyAuthorizer creates a SharedKeyAuthorizer using the given credentials +func NewSharedKeyAuthorizer(accountName, accountKey string) *SharedKeyAuthorizer { + return &SharedKeyAuthorizer{ + storageAccountName: accountName, + storageAccountKey: accountKey, + } +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "SharedKey " followed by the computed key. +// This can be used for the Blob, Queue, and File Services +// +// from: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +// You may use Shared Key Lite authorization to authorize a request made against the +// 2009-09-19 version and later of the Blob and Queue services, +// and version 2014-02-14 and later of the File services. +func (skl *SharedKeyAuthorizer) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err != nil { + return r, err + } + + key, err := buildSharedKey(skl.storageAccountName, skl.storageAccountKey, r) + if err != nil { + return r, err + } + + sharedKeyHeader := formatSharedKeyAuthorizationHeader(skl.storageAccountName, *key) + return autorest.Prepare(r, autorest.WithHeader(HeaderAuthorization, sharedKeyHeader)) + }) + } +} +func buildSharedKey(accountName, storageAccountKey string, r *http.Request) (*string, error) { + // first ensure the relevant headers are configured + prepareHeadersForRequest(r) + + sharedKey, err := computeSharedKey(r.Method, r.URL.String(), accountName, r.Header) + if err != nil { + return nil, err + } + + // we then need to HMAC that value + hmacdValue := hmacValue(storageAccountKey, *sharedKey) + return &hmacdValue, nil +} + +// computeSharedKeyLite computes the Shared Key Lite required for Storage Authentication +// NOTE: this function assumes that the `x-ms-date` field is set +func computeSharedKey(verb, url string, accountName string, headers http.Header) (*string, error) { + canonicalizedResource, err := buildCanonicalizedResource(url, accountName, false) + if err != nil { + return nil, err + } + + canonicalizedHeaders := buildCanonicalizedHeader(headers) + canonicalizedString := buildCanonicalizedStringForSharedKey(verb, headers, canonicalizedHeaders, *canonicalizedResource) + return &canonicalizedString, nil +} + +func buildCanonicalizedStringForSharedKey(verb string, headers http.Header, canonicalizedHeaders, canonicalizedResource string) string { + lengthString := headers.Get(HeaderContentLength) + + // empty string when zero + if lengthString == "0" { + lengthString = "" + } + + return strings.Join([]string{ + verb, // HTTP Verb + headers.Get(HeaderContentEncoding), // Content-Encoding + headers.Get(HeaderContentLanguage), // Content-Language + lengthString, // Content-Length (empty string when zero) + headers.Get(HeaderContentMD5), // Content-MD5 + headers.Get(HeaderContentType), // Content-Type + "", // date should be nil, apparently :shrug: + headers.Get(HeaderIfModifiedSince), // If-Modified-Since + headers.Get(HeaderIfMatch), // If-Match + headers.Get(HeaderIfNoneMatch), // If-None-Match + headers.Get(HeaderIfUnmodifiedSince), // If-Unmodified-Since + headers.Get(HeaderRange), // Range + canonicalizedHeaders, + canonicalizedResource, + }, "\n") +} + +func formatSharedKeyAuthorizationHeader(accountName, key string) string { + canonicalizedAccountName := primaryStorageAccountName(accountName) + return fmt.Sprintf("SharedKey %s:%s", canonicalizedAccountName, key) +} diff --git a/azurerm/internal/authorizers/authorizer_shared_key_lite.go b/azurerm/internal/authorizers/authorizer_shared_key_lite.go new file mode 100644 index 000000000000..be12bbd682d0 --- /dev/null +++ b/azurerm/internal/authorizers/authorizer_shared_key_lite.go @@ -0,0 +1,90 @@ +package authorizers + +import ( + "net/http" + "strings" + + "github.com/Azure/go-autorest/autorest" +) + +// TODO: switch to using the version from github.com/Azure/go-autorest +// once https://github.com/Azure/go-autorest/pull/416 has been merged + +// SharedKeyLiteAuthorizer implements an authorization for Shared Key Lite +// this can be used for interaction with Blob, File and Queue Storage Endpoints +type SharedKeyLiteAuthorizer struct { + storageAccountName string + storageAccountKey string +} + +// NewSharedKeyLiteAuthorizer creates a SharedKeyLiteAuthorizer using the given credentials +func NewSharedKeyLiteAuthorizer(accountName, accountKey string) *SharedKeyLiteAuthorizer { + return &SharedKeyLiteAuthorizer{ + storageAccountName: accountName, + storageAccountKey: accountKey, + } +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "SharedKeyLite " followed by the computed key. +// This can be used for the Blob, Queue, and File Services +// +// from: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +// You may use Shared Key Lite authorization to authorize a request made against the +// 2009-09-19 version and later of the Blob and Queue services, +// and version 2014-02-14 and later of the File services. +func (skl *SharedKeyLiteAuthorizer) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err != nil { + return r, err + } + + key, err := buildSharedKeyLite(skl.storageAccountName, skl.storageAccountKey, r) + if err != nil { + return r, err + } + + sharedKeyHeader := formatSharedKeyLiteAuthorizationHeader(skl.storageAccountName, *key) + return autorest.Prepare(r, autorest.WithHeader(HeaderAuthorization, sharedKeyHeader)) + }) + } +} +func buildSharedKeyLite(accountName, storageAccountKey string, r *http.Request) (*string, error) { + // first ensure the relevant headers are configured + prepareHeadersForRequest(r) + + sharedKey, err := computeSharedKeyLite(r.Method, r.URL.String(), accountName, r.Header) + if err != nil { + return nil, err + } + + // we then need to HMAC that value + hmacdValue := hmacValue(storageAccountKey, *sharedKey) + return &hmacdValue, nil +} + +// computeSharedKeyLite computes the Shared Key Lite required for Storage Authentication +// NOTE: this function assumes that the `x-ms-date` field is set +func computeSharedKeyLite(verb, url string, accountName string, headers http.Header) (*string, error) { + canonicalizedResource, err := buildCanonicalizedResource(url, accountName, true) + if err != nil { + return nil, err + } + + canonicalizedHeaders := buildCanonicalizedHeader(headers) + canonicalizedString := buildCanonicalizedStringForSharedKeyLite(verb, headers, canonicalizedHeaders, *canonicalizedResource) + return &canonicalizedString, nil +} + +func buildCanonicalizedStringForSharedKeyLite(verb string, headers http.Header, canonicalizedHeaders, canonicalizedResource string) string { + return strings.Join([]string{ + verb, + headers.Get(HeaderContentMD5), + headers.Get(HeaderContentType), + "", // date should be nil, apparently :shrug: + canonicalizedHeaders, + canonicalizedResource, + }, "\n") +} diff --git a/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go b/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go new file mode 100644 index 000000000000..b2cc0af8421f --- /dev/null +++ b/azurerm/internal/authorizers/authorizer_shared_key_lite_table.go @@ -0,0 +1,87 @@ +package authorizers + +import ( + "net/http" + "strings" + + "github.com/Azure/go-autorest/autorest" +) + +// TODO: switch to using the version from github.com/Azure/go-autorest +// once https://github.com/Azure/go-autorest/pull/416 has been merged + +// SharedKeyLiteTableAuthorizer implements an authorization for Shared Key Lite +// this can be used for interaction with Table Storage Endpoints +type SharedKeyLiteTableAuthorizer struct { + storageAccountName string + storageAccountKey string +} + +// NewSharedKeyLiteAuthorizer crates a SharedKeyLiteAuthorizer using the given credentials +func NewSharedKeyLiteTableAuthorizer(accountName, accountKey string) *SharedKeyLiteTableAuthorizer { + return &SharedKeyLiteTableAuthorizer{ + storageAccountName: accountName, + storageAccountKey: accountKey, + } +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "SharedKeyLite " followed by the computed key. +// This can be used for the Blob, Queue, and File Services +// +// from: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +// You may use Shared Key Lite authorization to authorize a request made against the +// 2009-09-19 version and later of the Blob and Queue services, +// and version 2014-02-14 and later of the File services. +func (skl *SharedKeyLiteTableAuthorizer) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err != nil { + return r, err + } + + key, err := buildSharedKeyLiteTable(skl.storageAccountName, skl.storageAccountKey, r) + if err != nil { + return r, err + } + + sharedKeyHeader := formatSharedKeyLiteAuthorizationHeader(skl.storageAccountName, *key) + return autorest.Prepare(r, autorest.WithHeader(HeaderAuthorization, sharedKeyHeader)) + }) + } +} + +func buildSharedKeyLiteTable(accountName, storageAccountKey string, r *http.Request) (*string, error) { + // first ensure the relevant headers are configured + prepareHeadersForRequest(r) + + sharedKey, err := computeSharedKeyLiteTable(r.URL.String(), accountName, r.Header) + if err != nil { + return nil, err + } + + // we then need to HMAC that value + hmacdValue := hmacValue(storageAccountKey, *sharedKey) + return &hmacdValue, nil +} + +// computeSharedKeyLite computes the Shared Key Lite required for Storage Authentication +// NOTE: this function assumes that the `x-ms-date` field is set +func computeSharedKeyLiteTable(url string, accountName string, headers http.Header) (*string, error) { + dateHeader := headers.Get("x-ms-date") + canonicalizedResource, err := buildCanonicalizedResource(url, accountName, true) + if err != nil { + return nil, err + } + + canonicalizedString := buildCanonicalizedStringForSharedKeyLiteTable(*canonicalizedResource, dateHeader) + return &canonicalizedString, nil +} + +func buildCanonicalizedStringForSharedKeyLiteTable(canonicalizedResource, dateHeader string) string { + return strings.Join([]string{ + dateHeader, + canonicalizedResource, + }, "\n") +} diff --git a/azurerm/internal/authorizers/authorizer_shared_key_lite_test.go b/azurerm/internal/authorizers/authorizer_shared_key_lite_test.go new file mode 100644 index 000000000000..2fa5d322b97c --- /dev/null +++ b/azurerm/internal/authorizers/authorizer_shared_key_lite_test.go @@ -0,0 +1,103 @@ +package authorizers + +import ( + "net/http" + "testing" +) + +func TestBuildCanonicalizedStringForSharedKeyLite(t *testing.T) { + testData := []struct { + name string + headers map[string][]string + canonicalizedHeaders string + canonicalizedResource string + verb string + expected string + }{ + { + name: "completed", + verb: "NOM", + headers: map[string][]string{ + "Content-MD5": {"abc123"}, + "Content-Type": {"vnd/panda-pops+v1"}, + }, + canonicalizedHeaders: "all-the-headers", + canonicalizedResource: "all-the-resources", + expected: "NOM\n\nvnd/panda-pops+v1\n\nall-the-headers\nall-the-resources", + }, + } + + for _, test := range testData { + t.Logf("Test: %q", test.name) + actual := buildCanonicalizedStringForSharedKeyLite(test.verb, test.headers, test.canonicalizedHeaders, test.canonicalizedResource) + if actual != test.expected { + t.Fatalf("Expected %q but got %q", test.expected, actual) + } + } +} + +func TestComputeSharedKey(t *testing.T) { + testData := []struct { + name string + accountName string + method string + url string + headers map[string]string + expected string + }{ + { + name: "No Path", + accountName: "unlikely23exst2acct23wi", + method: "GET", + url: "https://unlikely23exst2acct23wi.queue.core.windows.net?comp=properties&restype=service", + headers: map[string]string{ + "Content-Type": "application/xml; charset=utf-8", + "X-Ms-Date": "Wed, 21 Aug 2019 11:00:25 GMT", + "X-Ms-Version": "2018-11-09", + }, + expected: `GET + +application/xml; charset=utf-8 + +x-ms-date:Wed, 21 Aug 2019 11:00:25 GMT +x-ms-version:2018-11-09 +/unlikely23exst2acct23wi?comp=properties`, + }, + { + name: "With Path", + accountName: "unlikely23exst2accti1t0", + method: "GET", + url: "https://unlikely23exst2accti1t0.queue.core.windows.net/?comp=properties&restype=service", + headers: map[string]string{ + "Content-Type": "application/xml; charset=utf-8", + "X-Ms-Date": "Wed, 21 Aug 2019 11:53:48 GMT", + "X-Ms-Version": "2018-11-09", + }, + expected: `GET + +application/xml; charset=utf-8 + +x-ms-date:Wed, 21 Aug 2019 11:53:48 GMT +x-ms-version:2018-11-09 +/unlikely23exst2accti1t0/?comp=properties`, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Test %q", v.name) + + headers := http.Header{} + for hn, hv := range v.headers { + headers.Add(hn, hv) + } + + actual, err := computeSharedKeyLite(v.method, v.url, v.accountName, headers) + if err != nil { + t.Fatalf("Error computing shared key: %s", err) + } + + if *actual != v.expected { + t.Fatalf("Expected %q but got %q", v.expected, *actual) + } + } +} diff --git a/azurerm/internal/authorizers/consts.go b/azurerm/internal/authorizers/consts.go new file mode 100644 index 000000000000..86e73314029b --- /dev/null +++ b/azurerm/internal/authorizers/consts.go @@ -0,0 +1,19 @@ +package authorizers + +var ( + HeaderAuthorization = "Authorization" + HeaderContentLength = "Content-Length" + HeaderContentEncoding = "Content-Encoding" + HeaderContentLanguage = "Content-Language" + HeaderContentType = "Content-Type" + HeaderContentMD5 = "Content-MD5" + HeaderIfModifiedSince = "If-Modified-Since" + HeaderIfMatch = "If-Match" + HeaderIfNoneMatch = "If-None-Match" + HeaderIfUnmodifiedSince = "If-Unmodified-Since" + HeaderMSDate = "X-Ms-Date" + HeaderRange = "Range" + + StorageEmulatorAccountName = "devstoreaccount1" + StorageEmulatorAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" +) diff --git a/azurerm/internal/authorizers/helpers.go b/azurerm/internal/authorizers/helpers.go new file mode 100644 index 000000000000..1122a62039d6 --- /dev/null +++ b/azurerm/internal/authorizers/helpers.go @@ -0,0 +1,145 @@ +package authorizers + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "sort" + "strings" + "time" +) + +// buildCanonicalizedHeader builds the Canonicalized Header required to sign Storage Requests +func buildCanonicalizedHeader(headers http.Header) string { + cm := make(map[string]string) + + for k, v := range headers { + headerName := strings.TrimSpace(strings.ToLower(k)) + if strings.HasPrefix(headerName, "x-ms-") { + cm[headerName] = v[0] + } + } + + if len(cm) == 0 { + return "" + } + + var keys []string + for key := range cm { + keys = append(keys, key) + } + + sort.Strings(keys) + + ch := bytes.NewBufferString("") + + for _, key := range keys { + ch.WriteString(key) + ch.WriteRune(':') + ch.WriteString(cm[key]) + ch.WriteRune('\n') + } + + return strings.TrimSuffix(ch.String(), "\n") +} + +// buildCanonicalizedResource builds the Canonical Resource required for to sign Storage Account requests +func buildCanonicalizedResource(uri, accountName string, sharedKeyLite bool) (*string, error) { + u, err := url.Parse(uri) + if err != nil { + return nil, err + } + + cr := bytes.NewBufferString("") + if accountName != StorageEmulatorAccountName { + cr.WriteString("/") + cr.WriteString(primaryStorageAccountName(accountName)) + } + + if len(u.Path) > 0 { + // Any portion of the CanonicalizedResource string that is derived from + // the resource's URI should be encoded exactly as it is in the URI. + // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx + cr.WriteString(u.EscapedPath()) + } + + if sharedKeyLite { + if comp := u.Query().Get("comp"); comp != "" { + cr.WriteString(fmt.Sprintf("?comp=%s", comp)) + } + } else { + // Convert all parameter names to lowercase. + // URL-decode each query parameter name and value. + // Sort the query parameters lexicographically by parameter name, in ascending order. + keys := make([]string, 0) + keysWithValues := make(map[string]string, len(u.Query())) + for k, unsortedValues := range u.Query() { + key := strings.ToLower(k) + keys = append(keys, key) + + // If a query parameter has more than one value, sort all values lexicographically, then include them in a comma-separated list: + sortedValues := make([]string, 0) + sortedValues = append(sortedValues, unsortedValues...) + sort.Strings(sortedValues) + keysWithValues[key] = strings.Join(sortedValues, ",") + } + sort.Strings(keys) + + for _, key := range keys { + values := keysWithValues[key] + // Include a new-line character (\n) before each name-value pair. + // Append each query parameter name and value to the string in the following format, making sure to include the colon (:) between the name and the value + cr.WriteString(fmt.Sprintf("\n%s:%s", key, values)) + } + } + + out := cr.String() + return &out, nil +} + +func formatSharedKeyLiteAuthorizationHeader(accountName, key string) string { + canonicalizedAccountName := primaryStorageAccountName(accountName) + return fmt.Sprintf("SharedKeyLite %s:%s", canonicalizedAccountName, key) +} + +// hmacValue base-64 decodes the storageAccountKey, then signs the string with it +// as outlined here: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key +func hmacValue(storageAccountKey, canonicalizedString string) string { + key, err := base64.StdEncoding.DecodeString(storageAccountKey) + if err != nil { + return "" + } + + encr := hmac.New(sha256.New, key) + _, _ = encr.Write([]byte(canonicalizedString)) + return base64.StdEncoding.EncodeToString(encr.Sum(nil)) +} + +// prepareHeadersForRequest prepares a request so that it can be signed +// by ensuring the `date` and `x-ms-date` headers are set +func prepareHeadersForRequest(r *http.Request) { + if r.Header == nil { + r.Header = http.Header{} + } + + date := time.Now().UTC().Format(http.TimeFormat) + + // a date must be set, X-Ms-Date should be used when both are set; but let's set both for completeness + r.Header.Set("date", date) + r.Header.Set("x-ms-date", date) +} + +// primaryStorageAccountName returns the name of the primary for a given Storage Account +func primaryStorageAccountName(input string) string { + // from https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key + // If you are accessing the secondary location in a storage account for which + // read-access geo-replication (RA-GRS) is enabled, do not include the + // -secondary designation in the authorization header. + // For authorization purposes, the account name is always the name of the primary location, + // even for secondary access. + return strings.TrimSuffix(input, "-secondary") +} diff --git a/azurerm/internal/authorizers/helpers_test.go b/azurerm/internal/authorizers/helpers_test.go new file mode 100644 index 000000000000..6c49cb9a79a9 --- /dev/null +++ b/azurerm/internal/authorizers/helpers_test.go @@ -0,0 +1,302 @@ +package authorizers + +import ( + "encoding/base64" + "net/http" + "testing" +) + +func TestBuildCanonicalizedHeader(t *testing.T) { + testData := []struct { + Input http.Header + Expected string + }{ + { + // no headers + Expected: "", + Input: map[string][]string{ + "": {""}, + }, + }, + { + // no x-ms headers + Expected: "", + Input: map[string][]string{ + "panda": {"pops"}, + }, + }, + { + // only a single x-ms header + Expected: "x-ms-panda:nom", + Input: map[string][]string{ + "x-ms-panda": {"nom"}, + }, + }, + { + // multiple x-ms headers + Expected: "x-ms-panda:nom\nx-ms-tiger:rawr", + Input: map[string][]string{ + "x-ms-panda": {"nom"}, + "x-ms-tiger": {"rawr"}, + }, + }, + { + // multiple x-ms headers, out of order + Expected: "x-ms-panda:nom\nx-ms-tiger:rawr", + Input: map[string][]string{ + "x-ms-tiger": {"rawr"}, + "x-ms-panda": {"nom"}, + }, + }, + { + // mixed headers (some ms, some non-ms) + Expected: "x-ms-panda:nom\nx-ms-tiger:rawr", + Input: map[string][]string{ + "x-ms-tiger": {"rawr"}, + "panda": {"pops"}, + "x-ms-panda": {"nom"}, + }, + }, + { + // casing + Expected: "x-ms-panda:nom\nx-ms-tiger:rawr", + Input: map[string][]string{ + "X-Ms-Tiger": {"rawr"}, + "X-Ms-Panda": {"nom"}, + }, + }, + } + + for _, v := range testData { + actual := buildCanonicalizedHeader(v.Input) + if actual != v.Expected { + t.Fatalf("Expected %q but got %q", v.Expected, actual) + } + } +} + +func TestBuildCanonicalizedResource(t *testing.T) { + testData := []struct { + name string + accountName string + uri string + sharedKeyLite bool + expected string + expectError bool + }{ + { + name: "invalid uri", + accountName: "example", + uri: "://example.com", + sharedKeyLite: true, + expected: "", + expectError: true, + }, + { + name: "storage emulator doesn't get prefix", + accountName: StorageEmulatorAccountName, + uri: "http://www.example.com/foo", + sharedKeyLite: true, + expected: "/foo", + }, + { + name: "non storage emulator gets prefix", + accountName: StorageEmulatorAccountName + "test", + uri: "http://www.example.com/foo", + sharedKeyLite: true, + expected: "/" + StorageEmulatorAccountName + "test/foo", + }, + { + name: "uri encoding", + accountName: "example", + uri: "", + sharedKeyLite: true, + expected: "/example%3Chello%3E", + }, + { + name: "comp-arg", + accountName: "example", + uri: "/endpoint?first=true&comp=bar&second=false&third=panda", + sharedKeyLite: true, + expected: "/example/endpoint?comp=bar", + }, + { + name: "arguments", + accountName: "example", + uri: "/endpoint?first=true&second=false&third=panda", + sharedKeyLite: true, + expected: "/example/endpoint", + }, + { + name: "arguments-sharedkey", + accountName: "example", + uri: "/endpoint?first=true&second=false&third=panda", + sharedKeyLite: false, + expected: "/example/endpoint\nfirst:true\nsecond:false\nthird:panda", + expectError: false, + }, + { + name: "arguments-sharedkey", + accountName: "example", + uri: "/endpoint?comp=strawberries&restype=pandas", + sharedKeyLite: false, + expected: "/example/endpoint\ncomp:strawberries\nrestype:pandas", + expectError: false, + }, + { + name: "extra-arguments-sharedkey", + accountName: "myaccount", + uri: "/mycontainer?restype=container&comp=list&include=snapshots&include=metadata&include=uncommittedblobs", + expected: "/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,uncommittedblobs\nrestype:container", + sharedKeyLite: false, + expectError: false, + }, + } + + for _, test := range testData { + t.Logf("Test %q", test.name) + actual, err := buildCanonicalizedResource(test.uri, test.accountName, test.sharedKeyLite) + if err != nil { + if test.expectError { + continue + } + + t.Fatalf("Error: %s", err) + } + + if *actual != test.expected { + t.Fatalf("Expected %q but got %q", test.expected, *actual) + } + } +} + +func TestFormatSharedKeyLiteAuthorizationHeader(t *testing.T) { + testData := []struct { + name string + accountName string + accountKey string + expected string + }{ + { + name: "primary", + accountName: "account1", + accountKey: "examplekey", + expected: "SharedKeyLite account1:examplekey", + }, + { + name: "secondary", + accountName: "account1-secondary", + accountKey: "examplekey", + expected: "SharedKeyLite account1:examplekey", + }, + } + + for _, test := range testData { + t.Logf("Test: %q", test.name) + actual := formatSharedKeyLiteAuthorizationHeader(test.accountName, test.accountKey) + + if actual != test.expected { + t.Fatalf("Expected %q but got %q", test.expected, actual) + } + } +} + +func TestHMAC(t *testing.T) { + testData := []struct { + Expected string + StorageAccountKey string + CanonicalizedString string + }{ + { + // When Storage Key isn't base-64 encoded + Expected: "", + StorageAccountKey: "bar", + CanonicalizedString: "foobarzoo", + }, + { + // Valid + Expected: "h5U0ATVX6SpbFX1H6GNuxIMeXXCILLoIvhflPtuQZ30=", + StorageAccountKey: base64.StdEncoding.EncodeToString([]byte("bar")), + CanonicalizedString: "foobarzoo", + }, + } + + for _, v := range testData { + actual := hmacValue(v.StorageAccountKey, v.CanonicalizedString) + if actual != v.Expected { + t.Fatalf("Expected %q but got %q", v.Expected, actual) + } + } +} + +func TestTestPrepareHeadersForRequest(t *testing.T) { + request, err := http.NewRequest("GET", "http://example.com", nil) + if err != nil { + t.Fatal(err) + } + + headers := []string{ + "Date", + "X-Ms-Date", + } + + for _, header := range headers { + existingVal := request.Header.Get(header) + if existingVal != "" { + t.Fatalf("%q had a value prior to being set: %q", header, existingVal) + } + } + + prepareHeadersForRequest(request) + + for _, header := range headers { + updatedVal := request.Header.Get(header) + if updatedVal == "" { + t.Fatalf("%q didn't have a value after being set: %q", header, updatedVal) + } + } +} + +func TestPrepareHeadersForRequestWithNoneConfigured(t *testing.T) { + request, err := http.NewRequest("GET", "http://example.com", nil) + if err != nil { + t.Fatal(err) + } + + request.Header = nil + prepareHeadersForRequest(request) + + if request.Header == nil { + t.Fatalf("Expected `request.Header` to not be nil, but it was!") + } +} + +func TestPrimaryStorageAccountName(t *testing.T) { + testData := []struct { + Expected string + Input string + }{ + { + // Empty + Expected: "", + Input: "", + }, + { + // Primary + Expected: "bar", + Input: "bar", + }, + { + // Secondary + Expected: "bar", + Input: "bar-secondary", + }, + } + + for _, v := range testData { + actual := primaryStorageAccountName(v.Input) + if actual != v.Expected { + t.Fatalf("Expected %q but got %q", v.Expected, actual) + } + } +} diff --git a/azurerm/internal/clients/client.go b/azurerm/internal/clients/client.go new file mode 100644 index 000000000000..c32884c58a7d --- /dev/null +++ b/azurerm/internal/clients/client.go @@ -0,0 +1,12 @@ +package clients + +import ( + "context" +) + +type Client struct { + // StopContext is used for propagating control from Terraform Core (e.g. Ctrl/Cmd+C) + StopContext context.Context + + Compute ComputeClient +} diff --git a/azurerm/internal/clients/compute.go b/azurerm/internal/clients/compute.go new file mode 100644 index 000000000000..7b2d461b850c --- /dev/null +++ b/azurerm/internal/clients/compute.go @@ -0,0 +1,90 @@ +package clients + +import ( + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/Azure/azure-sdk-for-go/services/marketplaceordering/mgmt/2015-06-01/marketplaceordering" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type ComputeClient struct { + AvailabilitySetsClient *compute.AvailabilitySetsClient + DisksClient *compute.DisksClient + GalleriesClient *compute.GalleriesClient + GalleryImagesClient *compute.GalleryImagesClient + GalleryImageVersionsClient *compute.GalleryImageVersionsClient + ProximityPlacementGroupsClient *compute.ProximityPlacementGroupsClient + MarketplaceAgreementsClient *marketplaceordering.MarketplaceAgreementsClient + ImagesClient *compute.ImagesClient + SnapshotsClient *compute.SnapshotsClient + UsageClient *compute.UsageClient + VMExtensionImageClient *compute.VirtualMachineExtensionImagesClient + VMExtensionClient *compute.VirtualMachineExtensionsClient + VMScaleSetClient *compute.VirtualMachineScaleSetsClient + VMClient *compute.VirtualMachinesClient + VMImageClient *compute.VirtualMachineImagesClient +} + +func NewComputeClient(o *common.ClientOptions) *ComputeClient { + availabilitySetsClient := compute.NewAvailabilitySetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&availabilitySetsClient.Client, o.ResourceManagerAuthorizer) + + disksClient := compute.NewDisksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&disksClient.Client, o.ResourceManagerAuthorizer) + + galleriesClient := compute.NewGalleriesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&galleriesClient.Client, o.ResourceManagerAuthorizer) + + galleryImagesClient := compute.NewGalleryImagesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&galleryImagesClient.Client, o.ResourceManagerAuthorizer) + + galleryImageVersionsClient := compute.NewGalleryImageVersionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&galleryImageVersionsClient.Client, o.ResourceManagerAuthorizer) + + imagesClient := compute.NewImagesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&imagesClient.Client, o.ResourceManagerAuthorizer) + + marketplaceAgreementsClient := marketplaceordering.NewMarketplaceAgreementsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&marketplaceAgreementsClient.Client, o.ResourceManagerAuthorizer) + + proximityPlacementGroupsClient := compute.NewProximityPlacementGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&proximityPlacementGroupsClient.Client, o.ResourceManagerAuthorizer) + + snapshotsClient := compute.NewSnapshotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&snapshotsClient.Client, o.ResourceManagerAuthorizer) + + usageClient := compute.NewUsageClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&usageClient.Client, o.ResourceManagerAuthorizer) + + vmExtensionImageClient := compute.NewVirtualMachineExtensionImagesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&vmExtensionImageClient.Client, o.ResourceManagerAuthorizer) + + vmExtensionClient := compute.NewVirtualMachineExtensionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&vmExtensionClient.Client, o.ResourceManagerAuthorizer) + + vmImageClient := compute.NewVirtualMachineImagesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&vmImageClient.Client, o.ResourceManagerAuthorizer) + + vmScaleSetClient := compute.NewVirtualMachineScaleSetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&vmScaleSetClient.Client, o.ResourceManagerAuthorizer) + + vmClient := compute.NewVirtualMachinesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&vmClient.Client, o.ResourceManagerAuthorizer) + + return &ComputeClient{ + AvailabilitySetsClient: &availabilitySetsClient, + DisksClient: &disksClient, + GalleriesClient: &galleriesClient, + GalleryImagesClient: &galleryImagesClient, + GalleryImageVersionsClient: &galleryImageVersionsClient, + ImagesClient: &imagesClient, + MarketplaceAgreementsClient: &marketplaceAgreementsClient, + ProximityPlacementGroupsClient: &proximityPlacementGroupsClient, + SnapshotsClient: &snapshotsClient, + UsageClient: &usageClient, + VMExtensionImageClient: &vmExtensionImageClient, + VMExtensionClient: &vmExtensionClient, + VMScaleSetClient: &vmScaleSetClient, + VMClient: &vmClient, + VMImageClient: &vmImageClient, + } +} diff --git a/azurerm/internal/common/client_options.go b/azurerm/internal/common/client_options.go new file mode 100644 index 000000000000..abe91cd852fd --- /dev/null +++ b/azurerm/internal/common/client_options.go @@ -0,0 +1,64 @@ +package common + +import ( + "fmt" + "log" + "os" + "strings" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-helpers/sender" + "github.com/hashicorp/terraform/httpclient" + "github.com/terraform-providers/terraform-provider-azurerm/version" +) + +type ClientOptions struct { + SubscriptionId string + TenantID string + PartnerId string + TerraformVersion string + + GraphAuthorizer autorest.Authorizer + GraphEndpoint string + KeyVaultAuthorizer autorest.Authorizer + ResourceManagerAuthorizer autorest.Authorizer + ResourceManagerEndpoint string + StorageAuthorizer autorest.Authorizer + + PollingDuration time.Duration + SkipProviderReg bool + DisableCorrelationRequestID bool + Environment azure.Environment +} + +func (o ClientOptions) ConfigureClient(c *autorest.Client, authorizer autorest.Authorizer) { + setUserAgent(c, o.TerraformVersion, o.PartnerId) + + c.Authorizer = authorizer + c.Sender = sender.BuildSender("AzureRM") + c.PollingDuration = o.PollingDuration + c.SkipResourceProviderRegistration = o.SkipProviderReg + if !o.DisableCorrelationRequestID { + c.RequestInspector = WithCorrelationRequestID(CorrelationRequestID()) + } +} + +func setUserAgent(client *autorest.Client, tfVersion, partnerID string) { + tfUserAgent := httpclient.TerraformUserAgent(tfVersion) + + providerUserAgent := fmt.Sprintf("%s terraform-provider-azurerm/%s", tfUserAgent, version.ProviderVersion) + client.UserAgent = strings.TrimSpace(fmt.Sprintf("%s %s", client.UserAgent, providerUserAgent)) + + // append the CloudShell version to the user agent if it exists + if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" { + client.UserAgent = fmt.Sprintf("%s %s", client.UserAgent, azureAgent) + } + + if partnerID != "" { + client.UserAgent = fmt.Sprintf("%s pid-%s", client.UserAgent, partnerID) + } + + log.Printf("[DEBUG] AzureRM Client User Agent: %s\n", client.UserAgent) +} diff --git a/azurerm/helpers/azure/request.go b/azurerm/internal/common/correlation_id.go similarity index 73% rename from azurerm/helpers/azure/request.go rename to azurerm/internal/common/correlation_id.go index 878047dc2eb3..45dadada2236 100644 --- a/azurerm/helpers/azure/request.go +++ b/azurerm/internal/common/correlation_id.go @@ -1,4 +1,4 @@ -package azure +package common import ( "log" @@ -19,23 +19,25 @@ var ( ) // WithCorrelationRequestID returns a PrepareDecorator that adds an HTTP extension header of -// `x-ms-correlation-request-id` whose value is passed, undecorated UUID (e.g., -// `7F5A6223-F475-4A9C-B9D5-12575AA6B11B`). +// `x-ms-correlation-request-id` whose value is passed, undecorated UUID (e.g.,7F5A6223-F475-4A9C-B9D5-12575AA6B11B`). +// todo 2.0 change to withCorrelationRequestID func WithCorrelationRequestID(uuid string) autorest.PrepareDecorator { return autorest.WithHeader(HeaderCorrelationRequestID, uuid) } // CorrelationRequestID generates an UUID to pass through `x-ms-correlation-request-id` header. +// todo 2.0 change to correlationRequestID func CorrelationRequestID() string { msCorrelationRequestIDOnce.Do(func() { var err error msCorrelationRequestID, err = uuid.GenerateUUID() if err != nil { - log.Printf("[WARN] Fail to generate uuid for msCorrelationRequestID: %+v", err) + log.Printf("[WARN] Failed to generate uuid for msCorrelationRequestID: %+v", err) } + + log.Printf("[DEBUG] Genereated Provider Correlation Request Id: %s", msCorrelationRequestID) }) - log.Printf("[DEBUG] AzureRM Correlation Request Id: %s", msCorrelationRequestID) return msCorrelationRequestID } diff --git a/azurerm/helpers/azure/request_test.go b/azurerm/internal/common/correlation_id_test.go similarity index 98% rename from azurerm/helpers/azure/request_test.go rename to azurerm/internal/common/correlation_id_test.go index 410bf5167eeb..43119a0a9869 100644 --- a/azurerm/helpers/azure/request_test.go +++ b/azurerm/internal/common/correlation_id_test.go @@ -1,4 +1,4 @@ -package azure +package common import ( "net/http" diff --git a/azurerm/internal/features/requires_import.go b/azurerm/internal/features/requires_import.go new file mode 100644 index 000000000000..7e4565351099 --- /dev/null +++ b/azurerm/internal/features/requires_import.go @@ -0,0 +1,29 @@ +package features + +import ( + "os" + "strings" +) + +// ShouldResourcesBeImported returns whether the feature Requiring Resources to be Imported +// should be enabled. +// +// This feature prevents Terraform from 'adopting' resources which already exist, which is the +// behaviour used by ARM Templates which will update these resources rather than overwriting them +// Instead existing resources will need to be imported using `terraform import`, as is the case +// with other Terraform Providers. +// +// This feature-toggle defaults to off in 1.x versions of the Azure Provider, however this will +// become the default behaviour in version 2.0 of the Azure Provider. As outlined in the announcement +// for v2.0 of the Azure Provider: https://github.com/terraform-providers/terraform-provider-azurerm/issues/2807 +// +// Operators wishing to adopt this behaviour can opt-into this behaviour in 1.x versions of the +// Azure Provider by setting the Environment Variable 'ARM_PROVIDER_STRICT' to 'true' +func ShouldResourcesBeImported() bool { + // NOTE: we'll need to add an infobox to the following resources when this goes live: + // * App Service Source Control Token + // * MySQL|PostgreSQL Configuration + // since these resources can't support import + // in addition the virtual resources will need adjusting + return strings.EqualFold(os.Getenv("ARM_PROVIDER_STRICT"), "true") +} diff --git a/azurerm/internal/features/requires_import_test.go b/azurerm/internal/features/requires_import_test.go new file mode 100644 index 000000000000..dd72255dc4b2 --- /dev/null +++ b/azurerm/internal/features/requires_import_test.go @@ -0,0 +1,55 @@ +package features + +import ( + "os" + "testing" +) + +func TestRequiresImport(t *testing.T) { + testData := []struct { + name string + value string + expected bool + }{ + { + name: "unset", + value: "", + expected: false, + }, + { + name: "disabled lower-case", + value: "false", + expected: false, + }, + { + name: "disabled upper-case", + value: "FALSE", + expected: false, + }, + { + name: "enabled lower-case", + value: "true", + expected: true, + }, + { + name: "enabled upper-case", + value: "TRUE", + expected: true, + }, + { + name: "invalid", + value: "pandas", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Test %q..", v.name) + + os.Setenv("ARM_PROVIDER_STRICT", v.value) + actual := ShouldResourcesBeImported() + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/azurerm/internal/flags/features.go b/azurerm/internal/flags/features.go deleted file mode 100644 index de0d4e324407..000000000000 --- a/azurerm/internal/flags/features.go +++ /dev/null @@ -1,13 +0,0 @@ -package flags - -import ( - "os" - "strings" -) - -// NOTE: we'll need to add an infobox to MySQL|PostgreSQL Configuration when this goes live -// since these resources can't support import -// in addition the virtual resources will need adjusting - -// This file contains feature flags for functionality which will prove more challenging to implement en-mass -var RequireResourcesToBeImported = strings.EqualFold(os.Getenv("ARM_PROVIDER_STRICT"), "true") diff --git a/azurerm/internal/locks/lock.go b/azurerm/internal/locks/lock.go new file mode 100644 index 000000000000..d6e3c49bd2d9 --- /dev/null +++ b/azurerm/internal/locks/lock.go @@ -0,0 +1,37 @@ +package locks + +import "github.com/hashicorp/terraform/helper/mutexkv" + +// armMutexKV is the instance of MutexKV for ARM resources +var armMutexKV = mutexkv.NewMutexKV() + +func ByID(id string) { + armMutexKV.Lock(id) +} + +// handle the case of using the same name for different kinds of resources +func ByName(name string, resourceType string) { + updatedName := resourceType + "." + name + armMutexKV.Lock(updatedName) +} + +func MultipleByName(names *[]string, resourceType string) { + for _, name := range *names { + ByName(name, resourceType) + } +} + +func UnlockByID(id string) { + armMutexKV.Unlock(id) +} + +func UnlockByName(name string, resourceType string) { + updatedName := resourceType + "." + name + armMutexKV.Unlock(updatedName) +} + +func UnlockMultipleByName(names *[]string, resourceType string) { + for _, name := range *names { + UnlockByName(name, resourceType) + } +} diff --git a/azurerm/internal/services/analysisservices/client.go b/azurerm/internal/services/analysisservices/client.go new file mode 100644 index 000000000000..14a8767f3c2d --- /dev/null +++ b/azurerm/internal/services/analysisservices/client.go @@ -0,0 +1,19 @@ +package analysisservices + +import ( + "github.com/Azure/azure-sdk-for-go/services/analysisservices/mgmt/2017-08-01/analysisservices" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ServerClient *analysisservices.ServersClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ServerClient := analysisservices.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServerClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ServerClient: &ServerClient, + } +} diff --git a/azurerm/internal/services/apimanagement/client.go b/azurerm/internal/services/apimanagement/client.go new file mode 100644 index 000000000000..eddaa30e405a --- /dev/null +++ b/azurerm/internal/services/apimanagement/client.go @@ -0,0 +1,134 @@ +package apimanagement + +import ( + "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ApiClient *apimanagement.APIClient + ApiPoliciesClient *apimanagement.APIPolicyClient + ApiOperationsClient *apimanagement.APIOperationClient + ApiOperationPoliciesClient *apimanagement.APIOperationPolicyClient + ApiSchemasClient *apimanagement.APISchemaClient + ApiVersionSetClient *apimanagement.APIVersionSetClient + AuthorizationServersClient *apimanagement.AuthorizationServerClient + BackendClient *apimanagement.BackendClient + CertificatesClient *apimanagement.CertificateClient + GroupClient *apimanagement.GroupClient + GroupUsersClient *apimanagement.GroupUserClient + LoggerClient *apimanagement.LoggerClient + OpenIdConnectClient *apimanagement.OpenIDConnectProviderClient + PolicyClient *apimanagement.PolicyClient + ProductsClient *apimanagement.ProductClient + ProductApisClient *apimanagement.ProductAPIClient + ProductGroupsClient *apimanagement.ProductGroupClient + ProductPoliciesClient *apimanagement.ProductPolicyClient + PropertyClient *apimanagement.PropertyClient + ServiceClient *apimanagement.ServiceClient + SignInClient *apimanagement.SignInSettingsClient + SignUpClient *apimanagement.SignUpSettingsClient + SubscriptionsClient *apimanagement.SubscriptionClient + UsersClient *apimanagement.UserClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ApiClient := apimanagement.NewAPIClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiClient.Client, o.ResourceManagerAuthorizer) + + ApiPoliciesClient := apimanagement.NewAPIPolicyClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiPoliciesClient.Client, o.ResourceManagerAuthorizer) + + ApiOperationsClient := apimanagement.NewAPIOperationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiOperationsClient.Client, o.ResourceManagerAuthorizer) + + ApiOperationPoliciesClient := apimanagement.NewAPIOperationPolicyClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiOperationPoliciesClient.Client, o.ResourceManagerAuthorizer) + + ApiSchemasClient := apimanagement.NewAPISchemaClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiSchemasClient.Client, o.ResourceManagerAuthorizer) + + ApiVersionSetClient := apimanagement.NewAPIVersionSetClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApiVersionSetClient.Client, o.ResourceManagerAuthorizer) + + AuthorizationServersClient := apimanagement.NewAuthorizationServerClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AuthorizationServersClient.Client, o.ResourceManagerAuthorizer) + + BackendClient := apimanagement.NewBackendClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&BackendClient.Client, o.ResourceManagerAuthorizer) + + CertificatesClient := apimanagement.NewCertificateClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&CertificatesClient.Client, o.ResourceManagerAuthorizer) + + GroupClient := apimanagement.NewGroupClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&GroupClient.Client, o.ResourceManagerAuthorizer) + + GroupUsersClient := apimanagement.NewGroupUserClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&GroupUsersClient.Client, o.ResourceManagerAuthorizer) + + LoggerClient := apimanagement.NewLoggerClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LoggerClient.Client, o.ResourceManagerAuthorizer) + + PolicyClient := apimanagement.NewPolicyClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PolicyClient.Client, o.ResourceManagerAuthorizer) + + ServiceClient := apimanagement.NewServiceClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServiceClient.Client, o.ResourceManagerAuthorizer) + + SignInClient := apimanagement.NewSignInSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SignInClient.Client, o.ResourceManagerAuthorizer) + + SignUpClient := apimanagement.NewSignUpSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SignUpClient.Client, o.ResourceManagerAuthorizer) + + OpenIdConnectClient := apimanagement.NewOpenIDConnectProviderClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&OpenIdConnectClient.Client, o.ResourceManagerAuthorizer) + + ProductsClient := apimanagement.NewProductClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProductsClient.Client, o.ResourceManagerAuthorizer) + + ProductApisClient := apimanagement.NewProductAPIClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProductApisClient.Client, o.ResourceManagerAuthorizer) + + ProductGroupsClient := apimanagement.NewProductGroupClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProductGroupsClient.Client, o.ResourceManagerAuthorizer) + + ProductPoliciesClient := apimanagement.NewProductPolicyClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProductPoliciesClient.Client, o.ResourceManagerAuthorizer) + + PropertyClient := apimanagement.NewPropertyClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PropertyClient.Client, o.ResourceManagerAuthorizer) + + SubscriptionsClient := apimanagement.NewSubscriptionClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SubscriptionsClient.Client, o.ResourceManagerAuthorizer) + + UsersClient := apimanagement.NewUserClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&UsersClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ApiClient: &ApiClient, + ApiPoliciesClient: &ApiPoliciesClient, + ApiOperationsClient: &ApiOperationsClient, + ApiOperationPoliciesClient: &ApiOperationPoliciesClient, + ApiSchemasClient: &ApiSchemasClient, + ApiVersionSetClient: &ApiVersionSetClient, + AuthorizationServersClient: &AuthorizationServersClient, + BackendClient: &BackendClient, + CertificatesClient: &CertificatesClient, + GroupClient: &GroupClient, + GroupUsersClient: &GroupUsersClient, + LoggerClient: &LoggerClient, + OpenIdConnectClient: &OpenIdConnectClient, + PolicyClient: &PolicyClient, + ProductsClient: &ProductsClient, + ProductApisClient: &ProductApisClient, + ProductGroupsClient: &ProductGroupsClient, + ProductPoliciesClient: &ProductPoliciesClient, + PropertyClient: &PropertyClient, + ServiceClient: &ServiceClient, + SignInClient: &SignInClient, + SignUpClient: &SignUpClient, + SubscriptionsClient: &SubscriptionsClient, + UsersClient: &UsersClient, + } +} diff --git a/azurerm/internal/services/apimgmt/client.go b/azurerm/internal/services/apimgmt/client.go deleted file mode 100644 index 5bd333996042..000000000000 --- a/azurerm/internal/services/apimgmt/client.go +++ /dev/null @@ -1,29 +0,0 @@ -package apimgmt - -import "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement" - -type Client struct { - ApiClient apimanagement.APIClient - ApiPoliciesClient apimanagement.APIPolicyClient - ApiOperationsClient apimanagement.APIOperationClient - ApiOperationPoliciesClient apimanagement.APIOperationPolicyClient - ApiSchemasClient apimanagement.APISchemaClient - ApiVersionSetClient apimanagement.APIVersionSetClient - AuthorizationServersClient apimanagement.AuthorizationServerClient - CertificatesClient apimanagement.CertificateClient - GroupClient apimanagement.GroupClient - GroupUsersClient apimanagement.GroupUserClient - LoggerClient apimanagement.LoggerClient - OpenIdConnectClient apimanagement.OpenIDConnectProviderClient - PolicyClient apimanagement.PolicyClient - ProductsClient apimanagement.ProductClient - ProductApisClient apimanagement.ProductAPIClient - ProductGroupsClient apimanagement.ProductGroupClient - ProductPoliciesClient apimanagement.ProductPolicyClient - PropertyClient apimanagement.PropertyClient - ServiceClient apimanagement.ServiceClient - SignInClient apimanagement.SignInSettingsClient - SignUpClient apimanagement.SignUpSettingsClient - SubscriptionsClient apimanagement.SubscriptionClient - UsersClient apimanagement.UserClient -} diff --git a/azurerm/internal/services/applicationinsights/client.go b/azurerm/internal/services/applicationinsights/client.go new file mode 100644 index 000000000000..07ba4310bbe0 --- /dev/null +++ b/azurerm/internal/services/applicationinsights/client.go @@ -0,0 +1,34 @@ +package applicationinsights + +import ( + "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + AnalyticsItemsClient *insights.AnalyticsItemsClient + APIKeyClient *insights.APIKeysClient + ComponentsClient *insights.ComponentsClient + WebTestsClient *insights.WebTestsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AnalyticsItemsClient := insights.NewAnalyticsItemsClient(o.SubscriptionId) + o.ConfigureClient(&AnalyticsItemsClient.Client, o.ResourceManagerAuthorizer) + + APIKeyClient := insights.NewAPIKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&APIKeyClient.Client, o.ResourceManagerAuthorizer) + + ComponentsClient := insights.NewComponentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ComponentsClient.Client, o.ResourceManagerAuthorizer) + + WebTestsClient := insights.NewWebTestsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WebTestsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AnalyticsItemsClient: &AnalyticsItemsClient, + APIKeyClient: &APIKeyClient, + ComponentsClient: &ComponentsClient, + WebTestsClient: &WebTestsClient, + } +} diff --git a/azurerm/internal/services/authorization/client.go b/azurerm/internal/services/authorization/client.go new file mode 100644 index 000000000000..68535d01e911 --- /dev/null +++ b/azurerm/internal/services/authorization/client.go @@ -0,0 +1,24 @@ +package authorization + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + RoleAssignmentsClient *authorization.RoleAssignmentsClient + RoleDefinitionsClient *authorization.RoleDefinitionsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + RoleAssignmentsClient := authorization.NewRoleAssignmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RoleAssignmentsClient.Client, o.ResourceManagerAuthorizer) + + RoleDefinitionsClient := authorization.NewRoleDefinitionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RoleDefinitionsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + RoleAssignmentsClient: &RoleAssignmentsClient, + RoleDefinitionsClient: &RoleDefinitionsClient, + } +} diff --git a/azurerm/internal/services/automation/client.go b/azurerm/internal/services/automation/client.go index 052556241b3b..b8038fcb76f2 100644 --- a/azurerm/internal/services/automation/client.go +++ b/azurerm/internal/services/automation/client.go @@ -1,16 +1,64 @@ package automation -import "github.com/Azure/azure-sdk-for-go/services/automation/mgmt/2015-10-31/automation" +import ( + "github.com/Azure/azure-sdk-for-go/services/automation/mgmt/2015-10-31/automation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - AccountClient automation.AccountClient - AgentRegistrationInfoClient automation.AgentRegistrationInformationClient - CredentialClient automation.CredentialClient - DscConfigurationClient automation.DscConfigurationClient - DscNodeConfigurationClient automation.DscNodeConfigurationClient - ModuleClient automation.ModuleClient - RunbookClient automation.RunbookClient - RunbookDraftClient automation.RunbookDraftClient - ScheduleClient automation.ScheduleClient - VariableClient automation.VariableClient + AccountClient *automation.AccountClient + AgentRegistrationInfoClient *automation.AgentRegistrationInformationClient + CredentialClient *automation.CredentialClient + DscConfigurationClient *automation.DscConfigurationClient + DscNodeConfigurationClient *automation.DscNodeConfigurationClient + ModuleClient *automation.ModuleClient + RunbookClient *automation.RunbookClient + RunbookDraftClient *automation.RunbookDraftClient + ScheduleClient *automation.ScheduleClient + VariableClient *automation.VariableClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AccountClient := automation.NewAccountClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AccountClient.Client, o.ResourceManagerAuthorizer) + + AgentRegistrationInfoClient := automation.NewAgentRegistrationInformationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AgentRegistrationInfoClient.Client, o.ResourceManagerAuthorizer) + + CredentialClient := automation.NewCredentialClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&CredentialClient.Client, o.ResourceManagerAuthorizer) + + DscConfigurationClient := automation.NewDscConfigurationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DscConfigurationClient.Client, o.ResourceManagerAuthorizer) + + DscNodeConfigurationClient := automation.NewDscNodeConfigurationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DscNodeConfigurationClient.Client, o.ResourceManagerAuthorizer) + + ModuleClient := automation.NewModuleClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ModuleClient.Client, o.ResourceManagerAuthorizer) + + RunbookClient := automation.NewRunbookClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RunbookClient.Client, o.ResourceManagerAuthorizer) + + ScheduleClient := automation.NewScheduleClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ScheduleClient.Client, o.ResourceManagerAuthorizer) + + RunbookDraftClient := automation.NewRunbookDraftClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RunbookDraftClient.Client, o.ResourceManagerAuthorizer) + + VariableClient := automation.NewVariableClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VariableClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AccountClient: &AccountClient, + AgentRegistrationInfoClient: &AgentRegistrationInfoClient, + CredentialClient: &CredentialClient, + DscConfigurationClient: &DscConfigurationClient, + DscNodeConfigurationClient: &DscNodeConfigurationClient, + ModuleClient: &ModuleClient, + RunbookClient: &RunbookClient, + RunbookDraftClient: &RunbookDraftClient, + ScheduleClient: &ScheduleClient, + VariableClient: &VariableClient, + } } diff --git a/azurerm/internal/services/batch/client.go b/azurerm/internal/services/batch/client.go new file mode 100644 index 000000000000..07606a256dbe --- /dev/null +++ b/azurerm/internal/services/batch/client.go @@ -0,0 +1,34 @@ +package batch + +import ( + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + AccountClient *batch.AccountClient + ApplicationClient *batch.ApplicationClient + CertificateClient *batch.CertificateClient + PoolClient *batch.PoolClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AccountClient := batch.NewAccountClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AccountClient.Client, o.ResourceManagerAuthorizer) + + ApplicationClient := batch.NewApplicationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApplicationClient.Client, o.ResourceManagerAuthorizer) + + CertificateClient := batch.NewCertificateClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&CertificateClient.Client, o.ResourceManagerAuthorizer) + + PoolClient := batch.NewPoolClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PoolClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AccountClient: &AccountClient, + ApplicationClient: &ApplicationClient, + CertificateClient: &CertificateClient, + PoolClient: &PoolClient, + } +} diff --git a/azurerm/internal/services/bot/client.go b/azurerm/internal/services/bot/client.go new file mode 100644 index 000000000000..08af3db6d752 --- /dev/null +++ b/azurerm/internal/services/bot/client.go @@ -0,0 +1,29 @@ +package bot + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/botservice/mgmt/2018-07-12/botservice" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + BotClient *botservice.BotsClient + ConnectionClient *botservice.BotConnectionClient + ChannelClient *botservice.ChannelsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + botClient := botservice.NewBotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&botClient.Client, o.ResourceManagerAuthorizer) + + connectionClient := botservice.NewBotConnectionClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&connectionClient.Client, o.ResourceManagerAuthorizer) + + channelClient := botservice.NewChannelsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&channelClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + BotClient: &botClient, + ChannelClient: &channelClient, + ConnectionClient: &connectionClient, + } +} diff --git a/azurerm/internal/services/cdn/client.go b/azurerm/internal/services/cdn/client.go index c091aaac1b38..c35993f115c8 100644 --- a/azurerm/internal/services/cdn/client.go +++ b/azurerm/internal/services/cdn/client.go @@ -2,10 +2,28 @@ package cdn import ( "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2017-10-12/cdn" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - CustomDomainsClient cdn.CustomDomainsClient - EndpointsClient cdn.EndpointsClient - ProfilesClient cdn.ProfilesClient + CustomDomainsClient *cdn.CustomDomainsClient + EndpointsClient *cdn.EndpointsClient + ProfilesClient *cdn.ProfilesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + CustomDomainsClient := cdn.NewCustomDomainsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&CustomDomainsClient.Client, o.ResourceManagerAuthorizer) + + EndpointsClient := cdn.NewEndpointsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&EndpointsClient.Client, o.ResourceManagerAuthorizer) + + ProfilesClient := cdn.NewProfilesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProfilesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + CustomDomainsClient: &CustomDomainsClient, + EndpointsClient: &EndpointsClient, + ProfilesClient: &ProfilesClient, + } } diff --git a/azurerm/internal/services/cognitive/client.go b/azurerm/internal/services/cognitive/client.go index ab84051f8ec3..9b17d0123e50 100644 --- a/azurerm/internal/services/cognitive/client.go +++ b/azurerm/internal/services/cognitive/client.go @@ -1,7 +1,19 @@ package cognitive -import "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2017-04-18/cognitiveservices" +import ( + "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2017-04-18/cognitiveservices" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - AccountsClient cognitiveservices.AccountsClient + AccountsClient *cognitiveservices.AccountsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AccountsClient := cognitiveservices.NewAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AccountsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AccountsClient: &AccountsClient, + } } diff --git a/azurerm/internal/services/common/service_registration.go b/azurerm/internal/services/common/service_registration.go new file mode 100644 index 000000000000..12c24aeb699d --- /dev/null +++ b/azurerm/internal/services/common/service_registration.go @@ -0,0 +1,17 @@ +package common + +import "github.com/hashicorp/terraform/helper/schema" + +// NOTE: unfortunately this has to live in it's own package to avoid a circular reference +// since the Services will need to access ArmClient which is moving to `internal/common` + +type ServiceRegistration interface { + // Name is the name of this Service + Name() string + + // SupportedDataSources returns the supported Data Sources supported by this Service + SupportedDataSources() map[string]*schema.Resource + + // SupportedResources returns the supported Resources supported by this Service + SupportedResources() map[string]*schema.Resource +} diff --git a/azurerm/internal/services/compute/registration.go b/azurerm/internal/services/compute/registration.go new file mode 100644 index 000000000000..ea6db057ff33 --- /dev/null +++ b/azurerm/internal/services/compute/registration.go @@ -0,0 +1,22 @@ +package compute + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +type Registration struct{} + +// Name is the name of this Service +func (r Registration) Name() string { + return "Compute" +} + +// SupportedDataSources returns the supported Data Sources supported by this Service +func (r Registration) SupportedDataSources() map[string]*schema.Resource { + return map[string]*schema.Resource{} +} + +// SupportedResources returns the supported Resources supported by this Service +func (r Registration) SupportedResources() map[string]*schema.Resource { + return map[string]*schema.Resource{} +} diff --git a/azurerm/internal/services/containers/client.go b/azurerm/internal/services/containers/client.go index 0b240dadb516..8a5057e762a4 100644 --- a/azurerm/internal/services/containers/client.go +++ b/azurerm/internal/services/containers/client.go @@ -2,14 +2,47 @@ package containers import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance" - "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2017-10-01/containerregistry" - "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-02-01/containerservice" + "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-06-01/containerservice" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - KubernetesClustersClient containerservice.ManagedClustersClient - GroupsClient containerinstance.ContainerGroupsClient - RegistryClient containerregistry.RegistriesClient - RegistryReplicationsClient containerregistry.ReplicationsClient - ServicesClient containerservice.ContainerServicesClient + KubernetesClustersClient *containerservice.ManagedClustersClient + GroupsClient *containerinstance.ContainerGroupsClient + RegistriesClient *containerregistry.RegistriesClient + WebhooksClient *containerregistry.WebhooksClient + ReplicationsClient *containerregistry.ReplicationsClient + ServicesClient *containerservice.ContainerServicesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + RegistriesClient := containerregistry.NewRegistriesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RegistriesClient.Client, o.ResourceManagerAuthorizer) + + WebhooksClient := containerregistry.NewWebhooksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WebhooksClient.Client, o.ResourceManagerAuthorizer) + + ReplicationsClient := containerregistry.NewReplicationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ReplicationsClient.Client, o.ResourceManagerAuthorizer) + + GroupsClient := containerinstance.NewContainerGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&GroupsClient.Client, o.ResourceManagerAuthorizer) + + // ACS + ServicesClient := containerservice.NewContainerServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServicesClient.Client, o.ResourceManagerAuthorizer) + + // AKS + KubernetesClustersClient := containerservice.NewManagedClustersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&KubernetesClustersClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + KubernetesClustersClient: &KubernetesClustersClient, + GroupsClient: &GroupsClient, + RegistriesClient: &RegistriesClient, + WebhooksClient: &WebhooksClient, + ReplicationsClient: &ReplicationsClient, + ServicesClient: &ServicesClient, + } } diff --git a/azurerm/internal/services/cosmos/client.go b/azurerm/internal/services/cosmos/client.go new file mode 100644 index 000000000000..f2a303bfe259 --- /dev/null +++ b/azurerm/internal/services/cosmos/client.go @@ -0,0 +1,19 @@ +package cosmos + +import ( + "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + DatabaseClient *documentdb.DatabaseAccountsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + DatabaseClient := documentdb.NewDatabaseAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabaseClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + DatabaseClient: &DatabaseClient, + } +} diff --git a/azurerm/internal/services/databricks/client.go b/azurerm/internal/services/databricks/client.go index d82e141dd511..bd4f50578db2 100644 --- a/azurerm/internal/services/databricks/client.go +++ b/azurerm/internal/services/databricks/client.go @@ -1,7 +1,19 @@ package databricks -import "github.com/Azure/azure-sdk-for-go/services/databricks/mgmt/2018-04-01/databricks" +import ( + "github.com/Azure/azure-sdk-for-go/services/databricks/mgmt/2018-04-01/databricks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - WorkspacesClient databricks.WorkspacesClient + WorkspacesClient *databricks.WorkspacesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + WorkspacesClient := databricks.NewWorkspacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WorkspacesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + WorkspacesClient: &WorkspacesClient, + } } diff --git a/azurerm/internal/services/datafactory/client.go b/azurerm/internal/services/datafactory/client.go index 533c94964d4e..d741e279f32c 100644 --- a/azurerm/internal/services/datafactory/client.go +++ b/azurerm/internal/services/datafactory/client.go @@ -1,10 +1,34 @@ package datafactory -import "github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory" +import ( + "github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - DatasetClient datafactory.DatasetsClient - FactoriesClient datafactory.FactoriesClient - LinkedServiceClient datafactory.LinkedServicesClient - PipelinesClient datafactory.PipelinesClient + DatasetClient *datafactory.DatasetsClient + FactoriesClient *datafactory.FactoriesClient + LinkedServiceClient *datafactory.LinkedServicesClient + PipelinesClient *datafactory.PipelinesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + DatasetClient := datafactory.NewDatasetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatasetClient.Client, o.ResourceManagerAuthorizer) + + FactoriesClient := datafactory.NewFactoriesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FactoriesClient.Client, o.ResourceManagerAuthorizer) + + LinkedServiceClient := datafactory.NewLinkedServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LinkedServiceClient.Client, o.ResourceManagerAuthorizer) + + PipelinesClient := datafactory.NewPipelinesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PipelinesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + DatasetClient: &DatasetClient, + FactoriesClient: &FactoriesClient, + LinkedServiceClient: &LinkedServiceClient, + PipelinesClient: &PipelinesClient, + } } diff --git a/azurerm/internal/services/datalake/client.go b/azurerm/internal/services/datalake/client.go new file mode 100644 index 000000000000..73e8d5b55cdb --- /dev/null +++ b/azurerm/internal/services/datalake/client.go @@ -0,0 +1,44 @@ +package datalake + +import ( + analytics "github.com/Azure/azure-sdk-for-go/services/datalake/analytics/mgmt/2016-11-01/account" + "github.com/Azure/azure-sdk-for-go/services/datalake/store/2016-11-01/filesystem" + store "github.com/Azure/azure-sdk-for-go/services/datalake/store/mgmt/2016-11-01/account" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + // Data Lake Store + StoreAccountsClient *store.AccountsClient + StoreFirewallRulesClient *store.FirewallRulesClient + StoreFilesClient *filesystem.Client + + // Data Lake Analytics + AnalyticsAccountsClient *analytics.AccountsClient + AnalyticsFirewallRulesClient *analytics.FirewallRulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + StoreAccountsClient := store.NewAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&StoreAccountsClient.Client, o.ResourceManagerAuthorizer) + + StoreFirewallRulesClient := store.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&StoreFirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + StoreFilesClient := filesystem.NewClient() + o.ConfigureClient(&StoreFilesClient.Client, o.ResourceManagerAuthorizer) + + AnalyticsAccountsClient := analytics.NewAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AnalyticsAccountsClient.Client, o.ResourceManagerAuthorizer) + + AnalyticsFirewallRulesClient := analytics.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AnalyticsFirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + StoreAccountsClient: &StoreAccountsClient, + StoreFirewallRulesClient: &StoreFirewallRulesClient, + StoreFilesClient: &StoreFilesClient, + AnalyticsAccountsClient: &AnalyticsAccountsClient, + AnalyticsFirewallRulesClient: &AnalyticsFirewallRulesClient, + } +} diff --git a/azurerm/internal/services/devspace/client.go b/azurerm/internal/services/devspace/client.go index d5e84c6acc25..91d6ddf368e2 100644 --- a/azurerm/internal/services/devspace/client.go +++ b/azurerm/internal/services/devspace/client.go @@ -1,7 +1,19 @@ package devspace -import "github.com/Azure/azure-sdk-for-go/services/preview/devspaces/mgmt/2018-06-01-preview/devspaces" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/devspaces/mgmt/2018-06-01-preview/devspaces" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - ControllersClient devspaces.ControllersClient + ControllersClient *devspaces.ControllersClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ControllersClient := devspaces.NewControllersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ControllersClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ControllersClient: &ControllersClient, + } } diff --git a/azurerm/internal/services/devtestlabs/client.go b/azurerm/internal/services/devtestlabs/client.go new file mode 100644 index 000000000000..dc9aef3e76b9 --- /dev/null +++ b/azurerm/internal/services/devtestlabs/client.go @@ -0,0 +1,39 @@ +package devtestlabs + +import ( + "github.com/Azure/azure-sdk-for-go/services/devtestlabs/mgmt/2016-05-15/dtl" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + LabsClient *dtl.LabsClient + LabSchedulesClient *dtl.SchedulesClient + PoliciesClient *dtl.PoliciesClient + VirtualMachinesClient *dtl.VirtualMachinesClient + VirtualNetworksClient *dtl.VirtualNetworksClient +} + +func BuildClient(o *common.ClientOptions) *Client { + LabsClient := dtl.NewLabsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LabsClient.Client, o.ResourceManagerAuthorizer) + + PoliciesClient := dtl.NewPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PoliciesClient.Client, o.ResourceManagerAuthorizer) + + VirtualMachinesClient := dtl.NewVirtualMachinesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualMachinesClient.Client, o.ResourceManagerAuthorizer) + + VirtualNetworksClient := dtl.NewVirtualNetworksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualNetworksClient.Client, o.ResourceManagerAuthorizer) + + LabSchedulesClient := dtl.NewSchedulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LabSchedulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + LabsClient: &LabsClient, + LabSchedulesClient: &LabSchedulesClient, + PoliciesClient: &PoliciesClient, + VirtualMachinesClient: &VirtualMachinesClient, + VirtualNetworksClient: &VirtualNetworksClient, + } +} diff --git a/azurerm/internal/services/dns/client.go b/azurerm/internal/services/dns/client.go index 0e3441c013c3..0111069133a3 100644 --- a/azurerm/internal/services/dns/client.go +++ b/azurerm/internal/services/dns/client.go @@ -1,8 +1,24 @@ package dns -import "github.com/Azure/azure-sdk-for-go/services/preview/dns/mgmt/2018-03-01-preview/dns" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/dns/mgmt/2018-03-01-preview/dns" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - RecordSetsClient dns.RecordSetsClient - ZonesClient dns.ZonesClient + RecordSetsClient *dns.RecordSetsClient + ZonesClient *dns.ZonesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + RecordSetsClient := dns.NewRecordSetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RecordSetsClient.Client, o.ResourceManagerAuthorizer) + + ZonesClient := dns.NewZonesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ZonesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + RecordSetsClient: &RecordSetsClient, + ZonesClient: &ZonesClient, + } } diff --git a/azurerm/internal/services/eventgrid/client.go b/azurerm/internal/services/eventgrid/client.go index e7c3f3a69d90..c7b270241b4d 100644 --- a/azurerm/internal/services/eventgrid/client.go +++ b/azurerm/internal/services/eventgrid/client.go @@ -1,9 +1,29 @@ package eventgrid -import "github.com/Azure/azure-sdk-for-go/services/preview/eventgrid/mgmt/2018-09-15-preview/eventgrid" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/eventgrid/mgmt/2018-09-15-preview/eventgrid" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - DomainsClient eventgrid.DomainsClient - EventSubscriptionsClient eventgrid.EventSubscriptionsClient - TopicsClient eventgrid.TopicsClient + DomainsClient *eventgrid.DomainsClient + EventSubscriptionsClient *eventgrid.EventSubscriptionsClient + TopicsClient *eventgrid.TopicsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + DomainsClient := eventgrid.NewDomainsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DomainsClient.Client, o.ResourceManagerAuthorizer) + + EventSubscriptionsClient := eventgrid.NewEventSubscriptionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&EventSubscriptionsClient.Client, o.ResourceManagerAuthorizer) + + TopicsClient := eventgrid.NewTopicsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&TopicsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + DomainsClient: &DomainsClient, + EventSubscriptionsClient: &EventSubscriptionsClient, + TopicsClient: &TopicsClient, + } } diff --git a/azurerm/internal/services/eventhub/client.go b/azurerm/internal/services/eventhub/client.go index d92ef2a6057e..54ae33fdbc30 100644 --- a/azurerm/internal/services/eventhub/client.go +++ b/azurerm/internal/services/eventhub/client.go @@ -2,10 +2,33 @@ package eventhub import ( "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - ConsumerGroupClient eventhub.ConsumerGroupsClient - EventHubsClient eventhub.EventHubsClient - NamespacesClient eventhub.NamespacesClient + ConsumerGroupClient *eventhub.ConsumerGroupsClient + DisasterRecoveryConfigsClient *eventhub.DisasterRecoveryConfigsClient + EventHubsClient *eventhub.EventHubsClient + NamespacesClient *eventhub.NamespacesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + EventHubsClient := eventhub.NewEventHubsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&EventHubsClient.Client, o.ResourceManagerAuthorizer) + + DisasterRecoveryConfigsClient := eventhub.NewDisasterRecoveryConfigsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DisasterRecoveryConfigsClient.Client, o.ResourceManagerAuthorizer) + + ConsumerGroupClient := eventhub.NewConsumerGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ConsumerGroupClient.Client, o.ResourceManagerAuthorizer) + + NamespacesClient := eventhub.NewNamespacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&NamespacesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ConsumerGroupClient: &ConsumerGroupClient, + DisasterRecoveryConfigsClient: &DisasterRecoveryConfigsClient, + EventHubsClient: &EventHubsClient, + NamespacesClient: &NamespacesClient, + } } diff --git a/azurerm/internal/services/frontdoor/client.go b/azurerm/internal/services/frontdoor/client.go new file mode 100644 index 000000000000..60fee3730207 --- /dev/null +++ b/azurerm/internal/services/frontdoor/client.go @@ -0,0 +1,29 @@ +package frontdoor + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + FrontDoorsClient *frontdoor.FrontDoorsClient + FrontDoorsFrontendClient *frontdoor.FrontendEndpointsClient + FrontDoorsPolicyClient *frontdoor.PoliciesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + frontDoorsClient := frontdoor.NewFrontDoorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&frontDoorsClient.Client, o.ResourceManagerAuthorizer) + + frontDoorsFrontendClient := frontdoor.NewFrontendEndpointsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&frontDoorsFrontendClient.Client, o.ResourceManagerAuthorizer) + + frontDoorsPolicyClient := frontdoor.NewPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&frontDoorsPolicyClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + FrontDoorsClient: &frontDoorsClient, + FrontDoorsFrontendClient: &frontDoorsFrontendClient, + FrontDoorsPolicyClient: &frontDoorsPolicyClient, + } +} diff --git a/azurerm/internal/services/frontdoor/helper.go b/azurerm/internal/services/frontdoor/helper.go new file mode 100644 index 000000000000..006e03a08228 --- /dev/null +++ b/azurerm/internal/services/frontdoor/helper.go @@ -0,0 +1,203 @@ +package frontdoor + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" +) + +func VerifyBackendPoolExists(backendPoolName string, backendPools []interface{}) error { + if backendPoolName == "" { + return fmt.Errorf(`"backend_pool_name" cannot be empty`) + } + + for _, bps := range backendPools { + backendPool := bps.(map[string]interface{}) + if backendPool["name"].(string) == backendPoolName { + return nil + } + } + + return fmt.Errorf(`unable to locate "backend_pool_name":%q in configuration file`, backendPoolName) +} + +func AzureKeyVaultCertificateHasValues(customHttpsConfiguration map[string]interface{}, MatchAllKeys bool) bool { + certificateSecretName := customHttpsConfiguration["azure_key_vault_certificate_secret_name"] + certificateSecretVersion := customHttpsConfiguration["azure_key_vault_certificate_secret_version"] + certificateVaultId := customHttpsConfiguration["azure_key_vault_certificate_vault_id"] + + if MatchAllKeys { + if strings.TrimSpace(certificateSecretName.(string)) != "" && strings.TrimSpace(certificateSecretVersion.(string)) != "" && strings.TrimSpace(certificateVaultId.(string)) != "" { + return true + } + } else { + if strings.TrimSpace(certificateSecretName.(string)) != "" || strings.TrimSpace(certificateSecretVersion.(string)) != "" || strings.TrimSpace(certificateVaultId.(string)) != "" { + return true + } + } + + return false +} + +func IsFrontDoorFrontendEndpointConfigurable(currentState frontdoor.CustomHTTPSProvisioningState, customHttpsProvisioningEnabled bool, frontendEndpointName string, resourceGroup string) error { + action := "disable" + if customHttpsProvisioningEnabled { + action = "enable" + } + + switch currentState { + case frontdoor.CustomHTTPSProvisioningStateDisabling, frontdoor.CustomHTTPSProvisioningStateEnabling, frontdoor.CustomHTTPSProvisioningStateFailed: + return fmt.Errorf("Unable to %s the Front Door Frontend Endpoint %q (Resource Group %q) Custom Domain HTTPS state because the Frontend Endpoint is currently in the %q state", action, frontendEndpointName, resourceGroup, currentState) + default: + return nil + } +} + +func NormalizeCustomHTTPSProvisioningStateToBool(provisioningState frontdoor.CustomHTTPSProvisioningState) bool { + isEnabled := false + if provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled || provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabling { + isEnabled = true + } + + return isEnabled +} + +func GetFrontDoorBasicRouteConfigurationType(i interface{}) string { + _, ok := i.(frontdoor.ForwardingConfiguration) + if !ok { + _, ok := i.(frontdoor.RedirectConfiguration) + if !ok { + return "" + } + return "RedirectConfiguration" + } else { + return "ForwardingConfiguration" + } +} + +func VerifyRoutingRuleFrontendEndpoints(routingRuleFrontends []interface{}, configFrontendEndpoints []interface{}) error { + for _, routingRuleFrontend := range routingRuleFrontends { + // Get the name of the frontend defined in the routing rule + routingRulefrontendName := routingRuleFrontend.(string) + found := false + + // Loop over all of the defined frontend endpoints in the config + // seeing if we find the routing rule frontend in the list + for _, configFrontendEndpoint := range configFrontendEndpoints { + configFrontend := configFrontendEndpoint.(map[string]interface{}) + configFrontendName := configFrontend["name"] + if routingRulefrontendName == configFrontendName { + found = true + break + } + } + + if !found { + return fmt.Errorf(`"frontend_endpoints":%q was not found in the configuration file. verify you have the "frontend_endpoint":%q defined in the configuration file`, routingRulefrontendName, routingRulefrontendName) + } + } + + return nil +} + +func VerifyLoadBalancingAndHealthProbeSettings(backendPools []interface{}, loadBalancingSettings []interface{}, healthProbeSettings []interface{}) error { + for _, bps := range backendPools { + backendPool := bps.(map[string]interface{}) + backendPoolName := backendPool["name"] + backendPoolLoadBalancingName := backendPool["load_balancing_name"] + backendPoolHealthProbeName := backendPool["health_probe_name"] + found := false + + // Verify backend pool load balancing settings name exists + if len(loadBalancingSettings) > 0 { + for _, lbs := range loadBalancingSettings { + loadBalancing := lbs.(map[string]interface{}) + loadBalancingName := loadBalancing["name"] + + if loadBalancingName == backendPoolLoadBalancingName { + found = true + break + } + } + + if !found { + return fmt.Errorf(`"backend_pool":%q "load_balancing_name":%q was not found in the configuration file. verify you have the "backend_pool_load_balancing":%q defined in the configuration file`, backendPoolName, backendPoolLoadBalancingName, backendPoolLoadBalancingName) + } + } + + found = false + + // Verify health probe settings name exists + if len(healthProbeSettings) > 0 { + for _, hps := range healthProbeSettings { + healthProbe := hps.(map[string]interface{}) + healthProbeName := healthProbe["name"] + + if healthProbeName == backendPoolHealthProbeName { + found = true + break + } + } + + if !found { + return fmt.Errorf(`"backend_pool":%q "health_probe_name":%q was not found in the configuration file. verify you have the "backend_pool_health_probe":%q defined in the configuration file`, backendPoolName, backendPoolHealthProbeName, backendPoolHealthProbeName) + } + } + } + + return nil +} + +func VerifyCustomHttpsConfiguration(configFrontendEndpoints []interface{}) error { + for _, configFrontendEndpoint := range configFrontendEndpoints { + if configFrontend := configFrontendEndpoint.(map[string]interface{}); len(configFrontend) > 0 { + FrontendName := configFrontend["name"] + customHttpsEnabled := configFrontend["custom_https_provisioning_enabled"].(bool) + + if chc := configFrontend["custom_https_configuration"].([]interface{}); len(chc) > 0 { + if !customHttpsEnabled { + return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid because "custom_https_provisioning_enabled" is set to "false". please remove the "custom_https_configuration" block from the configuration file`, FrontendName) + } + + customHttpsConfiguration := chc[0].(map[string]interface{}) + certificateSource := customHttpsConfiguration["certificate_source"] + if certificateSource == string(frontdoor.CertificateSourceAzureKeyVault) { + if !AzureKeyVaultCertificateHasValues(customHttpsConfiguration, true) { + return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid, all of the following keys must have values in the "custom_https_configuration" block: "azure_key_vault_certificate_secret_name", "azure_key_vault_certificate_secret_version", and "azure_key_vault_certificate_vault_id"`, FrontendName) + } + } else { + if AzureKeyVaultCertificateHasValues(customHttpsConfiguration, false) { + return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid, all of the following keys must be removed from the "custom_https_configuration" block: "azure_key_vault_certificate_secret_name", "azure_key_vault_certificate_secret_version", and "azure_key_vault_certificate_vault_id"`, FrontendName) + } + } + } else if customHttpsEnabled { + return fmt.Errorf(`"frontend_endpoint":%q configuration is invalid because "custom_https_provisioning_enabled" is set to "true" and the "custom_https_configuration" block is undefined. please add the "custom_https_configuration" block to the configuration file`, FrontendName) + } + } + } + + return nil +} + +func FlattenTransformSlice(input *[]frontdoor.TransformType) []interface{} { + result := make([]interface{}, 0) + + if input != nil { + for _, item := range *input { + result = append(result, string(item)) + } + } + return result +} + +func FlattenFrontendEndpointLinkSlice(input *[]frontdoor.FrontendEndpointLink) []interface{} { + result := make([]interface{}, 0) + + if input != nil { + for _, item := range *input { + result = append(result, *item.ID) + } + } + return result +} diff --git a/azurerm/internal/services/frontdoor/validate.go b/azurerm/internal/services/frontdoor/validate.go new file mode 100644 index 000000000000..67e2e5a57880 --- /dev/null +++ b/azurerm/internal/services/frontdoor/validate.go @@ -0,0 +1,91 @@ +package frontdoor + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" +) + +func ValidateFrontDoorName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `(^[\da-zA-Z])([-\da-zA-Z]{3,61})([\da-zA-Z]$)`); !m { + errors = append(regexErrs, fmt.Errorf(`%q must be between 5 and 63 characters in length and begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens.`, k)) + } + + return nil, errors +} + +func ValidateBackendPoolRoutingRuleName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `(^[\da-zA-Z])([-\da-zA-Z]{1,88})([\da-zA-Z]$)`); !m { + errors = append(regexErrs, fmt.Errorf(`%q must be between 1 and 90 characters in length and begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens.`, k)) + } + + return nil, errors +} + +func ValidateCustomBlockResponseBody(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$`); !m { + errors = append(regexErrs, fmt.Errorf(`%q contains invalid characters, %q must contain only alphanumeric and equals sign characters.`, k, k)) + } + + return nil, errors +} + +func ValidateFrontdoorSettings(d *schema.ResourceDiff) error { + routingRules := d.Get("routing_rule").([]interface{}) + configFrontendEndpoints := d.Get("frontend_endpoint").([]interface{}) + backendPools := d.Get("backend_pool").([]interface{}) + loadBalancingSettings := d.Get("backend_pool_load_balancing").([]interface{}) + healthProbeSettings := d.Get("backend_pool_health_probe").([]interface{}) + + if len(configFrontendEndpoints) == 0 { + return fmt.Errorf(`"frontend_endpoint": must have at least one "frontend_endpoint" defined, found 0`) + } + + // Loop over all of the Routing Rules and validate that only one type of configuration is defined per Routing Rule + for _, rr := range routingRules { + routingRule := rr.(map[string]interface{}) + routingRuleName := routingRule["name"] + redirectConfig := routingRule["redirect_configuration"].([]interface{}) + forwardConfig := routingRule["forwarding_configuration"].([]interface{}) + + // Check 0. validate that at least one routing configuration exists per routing rule + if len(redirectConfig) == 0 && len(forwardConfig) == 0 { + return fmt.Errorf(`"routing_rule":%q is invalid. you must have either a "redirect_configuration" or a "forwarding_configuration" defined for the "routing_rule":%q `, routingRuleName, routingRuleName) + } + + // Check 1. validate that only one configuration type is defined per routing rule + if len(redirectConfig) == 1 && len(forwardConfig) == 1 { + return fmt.Errorf(`"routing_rule":%q is invalid. "redirect_configuration" conflicts with "forwarding_configuration". You can only have one configuration type per each routing rule`, routingRuleName) + } + + // Check 2. routing rule is a forwarding_configuration type make sure the backend_pool_name exists in the configuration file + if len(forwardConfig) > 0 { + fc := forwardConfig[0].(map[string]interface{}) + if err := VerifyBackendPoolExists(fc["backend_pool_name"].(string), backendPools); err != nil { + return fmt.Errorf(`"routing_rule":%q is invalid. %+v`, routingRuleName, err) + } + } + + // Check 3. validate that each routing rule frontend_endpoints are actually defined in the resource schema + if routingRuleFrontends := routingRule["frontend_endpoints"].([]interface{}); len(routingRuleFrontends) > 0 { + if err := VerifyRoutingRuleFrontendEndpoints(routingRuleFrontends, configFrontendEndpoints); err != nil { + return fmt.Errorf(`"routing_rule":%q %+v`, routingRuleName, err) + } + } else { + return fmt.Errorf(`"routing_rule": %q must have at least one "frontend_endpoints" defined`, routingRuleName) + } + } + + // Verify backend pool load balancing settings and health probe settings are defined in the resource schema + if err := VerifyLoadBalancingAndHealthProbeSettings(backendPools, loadBalancingSettings, healthProbeSettings); err != nil { + return fmt.Errorf(`%+v`, err) + } + + // Verify frontend endpoints custom https configuration is valid if defined + if err := VerifyCustomHttpsConfiguration(configFrontendEndpoints); err != nil { + return fmt.Errorf(`%+v`, err) + } + + return nil +} diff --git a/azurerm/internal/services/graph/client.go b/azurerm/internal/services/graph/client.go new file mode 100644 index 000000000000..8e6f73b0f780 --- /dev/null +++ b/azurerm/internal/services/graph/client.go @@ -0,0 +1,24 @@ +package graph + +import ( + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ApplicationsClient *graphrbac.ApplicationsClient + ServicePrincipalsClient *graphrbac.ServicePrincipalsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ApplicationsClient := graphrbac.NewApplicationsClientWithBaseURI(o.GraphEndpoint, o.TenantID) + o.ConfigureClient(&ApplicationsClient.Client, o.GraphAuthorizer) + + ServicePrincipalsClient := graphrbac.NewServicePrincipalsClientWithBaseURI(o.GraphEndpoint, o.TenantID) + o.ConfigureClient(&ServicePrincipalsClient.Client, o.GraphAuthorizer) + + return &Client{ + ApplicationsClient: &ApplicationsClient, + ServicePrincipalsClient: &ServicePrincipalsClient, + } +} diff --git a/azurerm/internal/services/hdinsight/client.go b/azurerm/internal/services/hdinsight/client.go index 92da76c6286d..6802d1eb49d5 100644 --- a/azurerm/internal/services/hdinsight/client.go +++ b/azurerm/internal/services/hdinsight/client.go @@ -1,9 +1,29 @@ package hdinsight -import "github.com/Azure/azure-sdk-for-go/services/preview/hdinsight/mgmt/2018-06-01-preview/hdinsight" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/hdinsight/mgmt/2018-06-01-preview/hdinsight" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - ApplicationsClient hdinsight.ApplicationsClient - ClustersClient hdinsight.ClustersClient - ConfigurationsClient hdinsight.ConfigurationsClient + ApplicationsClient *hdinsight.ApplicationsClient + ClustersClient *hdinsight.ClustersClient + ConfigurationsClient *hdinsight.ConfigurationsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ApplicationsClient := hdinsight.NewApplicationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApplicationsClient.Client, o.ResourceManagerAuthorizer) + + ClustersClient := hdinsight.NewClustersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ClustersClient.Client, o.ResourceManagerAuthorizer) + + ConfigurationsClient := hdinsight.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ConfigurationsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ApplicationsClient: &ApplicationsClient, + ClustersClient: &ClustersClient, + ConfigurationsClient: &ConfigurationsClient, + } } diff --git a/azurerm/internal/services/iothub/client.go b/azurerm/internal/services/iothub/client.go index db883f03b475..69a6af073831 100644 --- a/azurerm/internal/services/iothub/client.go +++ b/azurerm/internal/services/iothub/client.go @@ -3,9 +3,28 @@ package iothub import ( "github.com/Azure/azure-sdk-for-go/services/preview/iothub/mgmt/2018-12-01-preview/devices" "github.com/Azure/azure-sdk-for-go/services/provisioningservices/mgmt/2018-01-22/iothub" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - ResourceClient devices.IotHubResourceClient - DPSResourceClient iothub.IotDpsResourceClient + ResourceClient *devices.IotHubResourceClient + DPSResourceClient *iothub.IotDpsResourceClient + DPSCertificateClient *iothub.DpsCertificateClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ResourceClient := devices.NewIotHubResourceClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ResourceClient.Client, o.ResourceManagerAuthorizer) + + DPSResourceClient := iothub.NewIotDpsResourceClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DPSResourceClient.Client, o.ResourceManagerAuthorizer) + + DPSCertificateClient := iothub.NewDpsCertificateClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DPSCertificateClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ResourceClient: &ResourceClient, + DPSResourceClient: &DPSResourceClient, + DPSCertificateClient: &DPSCertificateClient, + } } diff --git a/azurerm/internal/services/keyvault/client.go b/azurerm/internal/services/keyvault/client.go new file mode 100644 index 000000000000..5b82ec0c87f1 --- /dev/null +++ b/azurerm/internal/services/keyvault/client.go @@ -0,0 +1,25 @@ +package keyvault + +import ( + keyvaultmgmt "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault" + "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + VaultsClient *keyvault.VaultsClient + ManagementClient *keyvaultmgmt.BaseClient +} + +func BuildClient(o *common.ClientOptions) *Client { + VaultsClient := keyvault.NewVaultsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VaultsClient.Client, o.ResourceManagerAuthorizer) + + ManagementClient := keyvaultmgmt.New() + o.ConfigureClient(&ManagementClient.Client, o.KeyVaultAuthorizer) + + return &Client{ + VaultsClient: &VaultsClient, + ManagementClient: &ManagementClient, + } +} diff --git a/azurerm/internal/services/kusto/client.go b/azurerm/internal/services/kusto/client.go new file mode 100644 index 000000000000..531fc0275b31 --- /dev/null +++ b/azurerm/internal/services/kusto/client.go @@ -0,0 +1,24 @@ +package kusto + +import ( + "github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2019-05-15/kusto" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ClustersClient *kusto.ClustersClient + DatabasesClient *kusto.DatabasesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ClustersClient := kusto.NewClustersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ClustersClient.Client, o.ResourceManagerAuthorizer) + + DatabasesClient := kusto.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ClustersClient: &ClustersClient, + DatabasesClient: &DatabasesClient, + } +} diff --git a/azurerm/internal/services/loganalytics/client.go b/azurerm/internal/services/loganalytics/client.go index 9448eadf7a13..4c81c7727b6f 100644 --- a/azurerm/internal/services/loganalytics/client.go +++ b/azurerm/internal/services/loganalytics/client.go @@ -3,10 +3,28 @@ package loganalytics import ( "github.com/Azure/azure-sdk-for-go/services/preview/operationalinsights/mgmt/2015-11-01-preview/operationalinsights" "github.com/Azure/azure-sdk-for-go/services/preview/operationsmanagement/mgmt/2015-11-01-preview/operationsmanagement" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - LinkedServicesClient operationalinsights.LinkedServicesClient - SolutionsClient operationsmanagement.SolutionsClient - WorkspacesClient operationalinsights.WorkspacesClient + LinkedServicesClient *operationalinsights.LinkedServicesClient + SolutionsClient *operationsmanagement.SolutionsClient + WorkspacesClient *operationalinsights.WorkspacesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + WorkspacesClient := operationalinsights.NewWorkspacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WorkspacesClient.Client, o.ResourceManagerAuthorizer) + + SolutionsClient := operationsmanagement.NewSolutionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, "Microsoft.OperationsManagement", "solutions", "testing") + o.ConfigureClient(&SolutionsClient.Client, o.ResourceManagerAuthorizer) + + LinkedServicesClient := operationalinsights.NewLinkedServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LinkedServicesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + LinkedServicesClient: &LinkedServicesClient, + SolutionsClient: &SolutionsClient, + WorkspacesClient: &WorkspacesClient, + } } diff --git a/azurerm/internal/services/logic/client.go b/azurerm/internal/services/logic/client.go new file mode 100644 index 000000000000..8ce9a871baca --- /dev/null +++ b/azurerm/internal/services/logic/client.go @@ -0,0 +1,19 @@ +package logic + +import ( + "github.com/Azure/azure-sdk-for-go/services/logic/mgmt/2016-06-01/logic" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + WorkflowsClient *logic.WorkflowsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + WorkflowsClient := logic.NewWorkflowsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WorkflowsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + WorkflowsClient: &WorkflowsClient, + } +} diff --git a/azurerm/internal/services/managementgroup/client.go b/azurerm/internal/services/managementgroup/client.go new file mode 100644 index 000000000000..17b6471fb816 --- /dev/null +++ b/azurerm/internal/services/managementgroup/client.go @@ -0,0 +1,24 @@ +package managementgroup + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/resources/mgmt/2018-03-01-preview/managementgroups" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + GroupsClient *managementgroups.Client + SubscriptionClient *managementgroups.SubscriptionsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + GroupsClient := managementgroups.NewClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&GroupsClient.Client, o.ResourceManagerAuthorizer) + + SubscriptionClient := managementgroups.NewSubscriptionsClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&SubscriptionClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + GroupsClient: &GroupsClient, + SubscriptionClient: &SubscriptionClient, + } +} diff --git a/azurerm/internal/services/maps/client.go b/azurerm/internal/services/maps/client.go new file mode 100644 index 000000000000..0bca868b2ce5 --- /dev/null +++ b/azurerm/internal/services/maps/client.go @@ -0,0 +1,19 @@ +package maps + +import ( + "github.com/Azure/azure-sdk-for-go/services/maps/mgmt/2018-05-01/maps" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + AccountsClient *maps.AccountsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AccountsClient := maps.NewAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AccountsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AccountsClient: &AccountsClient, + } +} diff --git a/azurerm/internal/services/maps/validation.go b/azurerm/internal/services/maps/validation.go new file mode 100644 index 000000000000..997b2506025d --- /dev/null +++ b/azurerm/internal/services/maps/validation.go @@ -0,0 +1,14 @@ +package maps + +import ( + "regexp" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func ValidateName() schema.SchemaValidateFunc { + return validation.StringMatch( + regexp.MustCompile(`^[A-Za-z0-9]{1}[A-Za-z0-9\._-]{1,}$`), + "First character must be alphanumeric. Subsequent character(s) must be any combination of alphanumeric, underscore (_), period (.), or hyphen (-).") +} diff --git a/azurerm/internal/services/maps/validation_test.go b/azurerm/internal/services/maps/validation_test.go new file mode 100644 index 000000000000..dcfdc9eb52c7 --- /dev/null +++ b/azurerm/internal/services/maps/validation_test.go @@ -0,0 +1,69 @@ +package maps + +import "testing" + +func TestValidateName(t *testing.T) { + testData := []struct { + Name string + Expected bool + }{ + { + Name: "", + Expected: false, + }, + { + Name: "hello", + Expected: true, + }, + { + Name: "Hello", + Expected: true, + }, + { + Name: "1hello", + Expected: true, + }, + { + Name: "1he-llo", + Expected: true, + }, + { + Name: "he-llo1", + Expected: true, + }, + { + Name: "he_llo1", + Expected: true, + }, + { + Name: ".hello1", + Expected: false, + }, + { + Name: "_hello1", + Expected: false, + }, + { + Name: "he.llo1", + Expected: true, + }, + { + Name: "he-llo!", + Expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + warnings, errors := ValidateName()(v.Name, "name") + if len(warnings) != 0 { + t.Fatalf("Expected no warnings but got %d", len(warnings)) + } + + actual := len(errors) == 0 + if v.Expected != actual { + t.Fatalf("Expected %t but got %t for %q: %s", v.Expected, actual, v.Name, errors) + } + } +} diff --git a/azurerm/internal/services/mariadb/client.go b/azurerm/internal/services/mariadb/client.go new file mode 100644 index 000000000000..7cf01e01132c --- /dev/null +++ b/azurerm/internal/services/mariadb/client.go @@ -0,0 +1,39 @@ +package mariadb + +import ( + "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ConfigurationsClient *mariadb.ConfigurationsClient + DatabasesClient *mariadb.DatabasesClient + FirewallRulesClient *mariadb.FirewallRulesClient + ServersClient *mariadb.ServersClient + VirtualNetworkRulesClient *mariadb.VirtualNetworkRulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + configurationsClient := mariadb.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&configurationsClient.Client, o.ResourceManagerAuthorizer) + + DatabasesClient := mariadb.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) + + FirewallRulesClient := mariadb.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + ServersClient := mariadb.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) + + VirtualNetworkRulesClient := mariadb.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ConfigurationsClient: &configurationsClient, + DatabasesClient: &DatabasesClient, + FirewallRulesClient: &FirewallRulesClient, + ServersClient: &ServersClient, + VirtualNetworkRulesClient: &VirtualNetworkRulesClient, + } +} diff --git a/azurerm/internal/services/media/client.go b/azurerm/internal/services/media/client.go index 5f18d63ef04d..a09212a2a638 100644 --- a/azurerm/internal/services/media/client.go +++ b/azurerm/internal/services/media/client.go @@ -1,7 +1,19 @@ package media -import "github.com/Azure/azure-sdk-for-go/services/mediaservices/mgmt/2018-07-01/media" +import ( + "github.com/Azure/azure-sdk-for-go/services/mediaservices/mgmt/2018-07-01/media" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - ServicesClient media.MediaservicesClient + ServicesClient *media.MediaservicesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ServicesClient := media.NewMediaservicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServicesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ServicesClient: &ServicesClient, + } } diff --git a/azurerm/internal/services/monitor/client.go b/azurerm/internal/services/monitor/client.go new file mode 100644 index 000000000000..97d99809524a --- /dev/null +++ b/azurerm/internal/services/monitor/client.go @@ -0,0 +1,57 @@ +package monitor + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + // Autoscale Settings + AutoscaleSettingsClient *insights.AutoscaleSettingsClient + + // Monitor + ActionGroupsClient *insights.ActionGroupsClient + ActivityLogAlertsClient *insights.ActivityLogAlertsClient + AlertRulesClient *insights.AlertRulesClient + DiagnosticSettingsClient *insights.DiagnosticSettingsClient + DiagnosticSettingsCategoryClient *insights.DiagnosticSettingsCategoryClient + LogProfilesClient *insights.LogProfilesClient + MetricAlertsClient *insights.MetricAlertsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AutoscaleSettingsClient := insights.NewAutoscaleSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AutoscaleSettingsClient.Client, o.ResourceManagerAuthorizer) + + ActionGroupsClient := insights.NewActionGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ActionGroupsClient.Client, o.ResourceManagerAuthorizer) + + ActivityLogAlertsClient := insights.NewActivityLogAlertsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ActivityLogAlertsClient.Client, o.ResourceManagerAuthorizer) + + AlertRulesClient := insights.NewAlertRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AlertRulesClient.Client, o.ResourceManagerAuthorizer) + + DiagnosticSettingsClient := insights.NewDiagnosticSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DiagnosticSettingsClient.Client, o.ResourceManagerAuthorizer) + + DiagnosticSettingsCategoryClient := insights.NewDiagnosticSettingsCategoryClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DiagnosticSettingsCategoryClient.Client, o.ResourceManagerAuthorizer) + + LogProfilesClient := insights.NewLogProfilesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LogProfilesClient.Client, o.ResourceManagerAuthorizer) + + MetricAlertsClient := insights.NewMetricAlertsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&MetricAlertsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AutoscaleSettingsClient: &AutoscaleSettingsClient, + ActionGroupsClient: &ActionGroupsClient, + ActivityLogAlertsClient: &ActivityLogAlertsClient, + AlertRulesClient: &AlertRulesClient, + DiagnosticSettingsClient: &DiagnosticSettingsClient, + DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient, + LogProfilesClient: &LogProfilesClient, + MetricAlertsClient: &MetricAlertsClient, + } +} diff --git a/azurerm/internal/services/msi/client.go b/azurerm/internal/services/msi/client.go index a9ddfaf4fb14..4aefc92122a4 100644 --- a/azurerm/internal/services/msi/client.go +++ b/azurerm/internal/services/msi/client.go @@ -1,7 +1,19 @@ package msi -import "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - UserAssignedIdentitiesClient msi.UserAssignedIdentitiesClient + UserAssignedIdentitiesClient *msi.UserAssignedIdentitiesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + UserAssignedIdentitiesClient := msi.NewUserAssignedIdentitiesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&UserAssignedIdentitiesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + UserAssignedIdentitiesClient: &UserAssignedIdentitiesClient, + } } diff --git a/azurerm/internal/services/mssql/client.go b/azurerm/internal/services/mssql/client.go new file mode 100644 index 000000000000..658d67ac02d1 --- /dev/null +++ b/azurerm/internal/services/mssql/client.go @@ -0,0 +1,19 @@ +package mssql + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2017-10-01-preview/sql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ElasticPoolsClient *sql.ElasticPoolsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ElasticPoolsClient := sql.NewElasticPoolsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ElasticPoolsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ElasticPoolsClient: &ElasticPoolsClient, + } +} diff --git a/azurerm/internal/services/mysql/client.go b/azurerm/internal/services/mysql/client.go new file mode 100644 index 000000000000..16a4b70fdd47 --- /dev/null +++ b/azurerm/internal/services/mysql/client.go @@ -0,0 +1,39 @@ +package mysql + +import ( + "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ConfigurationsClient *mysql.ConfigurationsClient + DatabasesClient *mysql.DatabasesClient + FirewallRulesClient *mysql.FirewallRulesClient + ServersClient *mysql.ServersClient + VirtualNetworkRulesClient *mysql.VirtualNetworkRulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ConfigurationsClient := mysql.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ConfigurationsClient.Client, o.ResourceManagerAuthorizer) + + DatabasesClient := mysql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) + + FirewallRulesClient := mysql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + ServersClient := mysql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) + + VirtualNetworkRulesClient := mysql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ConfigurationsClient: &ConfigurationsClient, + DatabasesClient: &DatabasesClient, + FirewallRulesClient: &FirewallRulesClient, + ServersClient: &ServersClient, + VirtualNetworkRulesClient: &VirtualNetworkRulesClient, + } +} diff --git a/azurerm/internal/services/network/client.go b/azurerm/internal/services/network/client.go new file mode 100644 index 000000000000..cb4a6a8d5fe0 --- /dev/null +++ b/azurerm/internal/services/network/client.go @@ -0,0 +1,149 @@ +package network + +import ( + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ApplicationGatewaysClient *network.ApplicationGatewaysClient + ApplicationSecurityGroupsClient *network.ApplicationSecurityGroupsClient + AzureFirewallsClient *network.AzureFirewallsClient + ConnectionMonitorsClient *network.ConnectionMonitorsClient + DDOSProtectionPlansClient *network.DdosProtectionPlansClient + ExpressRouteAuthsClient *network.ExpressRouteCircuitAuthorizationsClient + ExpressRouteCircuitsClient *network.ExpressRouteCircuitsClient + ExpressRoutePeeringsClient *network.ExpressRouteCircuitPeeringsClient + InterfacesClient *network.InterfacesClient + LoadBalancersClient *network.LoadBalancersClient + LocalNetworkGatewaysClient *network.LocalNetworkGatewaysClient + ProfileClient *network.ProfilesClient + PacketCapturesClient *network.PacketCapturesClient + PublicIPsClient *network.PublicIPAddressesClient + PublicIPPrefixesClient *network.PublicIPPrefixesClient + RoutesClient *network.RoutesClient + RouteTablesClient *network.RouteTablesClient + SecurityGroupClient *network.SecurityGroupsClient + SecurityRuleClient *network.SecurityRulesClient + SubnetsClient *network.SubnetsClient + VnetGatewayConnectionsClient *network.VirtualNetworkGatewayConnectionsClient + VnetGatewayClient *network.VirtualNetworkGatewaysClient + VnetClient *network.VirtualNetworksClient + VnetPeeringsClient *network.VirtualNetworkPeeringsClient + VirtualWanClient *network.VirtualWansClient + WatcherClient *network.WatchersClient + WebApplicationFirewallPoliciesClient *network.WebApplicationFirewallPoliciesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ApplicationGatewaysClient := network.NewApplicationGatewaysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApplicationGatewaysClient.Client, o.ResourceManagerAuthorizer) + + ApplicationSecurityGroupsClient := network.NewApplicationSecurityGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ApplicationSecurityGroupsClient.Client, o.ResourceManagerAuthorizer) + + AzureFirewallsClient := network.NewAzureFirewallsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AzureFirewallsClient.Client, o.ResourceManagerAuthorizer) + + ConnectionMonitorsClient := network.NewConnectionMonitorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ConnectionMonitorsClient.Client, o.ResourceManagerAuthorizer) + + DDOSProtectionPlansClient := network.NewDdosProtectionPlansClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DDOSProtectionPlansClient.Client, o.ResourceManagerAuthorizer) + + ExpressRouteAuthsClient := network.NewExpressRouteCircuitAuthorizationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ExpressRouteAuthsClient.Client, o.ResourceManagerAuthorizer) + + ExpressRouteCircuitsClient := network.NewExpressRouteCircuitsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ExpressRouteCircuitsClient.Client, o.ResourceManagerAuthorizer) + + ExpressRoutePeeringsClient := network.NewExpressRouteCircuitPeeringsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ExpressRoutePeeringsClient.Client, o.ResourceManagerAuthorizer) + + InterfacesClient := network.NewInterfacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&InterfacesClient.Client, o.ResourceManagerAuthorizer) + + LoadBalancersClient := network.NewLoadBalancersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LoadBalancersClient.Client, o.ResourceManagerAuthorizer) + + LocalNetworkGatewaysClient := network.NewLocalNetworkGatewaysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LocalNetworkGatewaysClient.Client, o.ResourceManagerAuthorizer) + + ProfileClient := network.NewProfilesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProfileClient.Client, o.ResourceManagerAuthorizer) + + VnetClient := network.NewVirtualNetworksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VnetClient.Client, o.ResourceManagerAuthorizer) + + PacketCapturesClient := network.NewPacketCapturesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PacketCapturesClient.Client, o.ResourceManagerAuthorizer) + + VnetPeeringsClient := network.NewVirtualNetworkPeeringsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VnetPeeringsClient.Client, o.ResourceManagerAuthorizer) + + PublicIPsClient := network.NewPublicIPAddressesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PublicIPsClient.Client, o.ResourceManagerAuthorizer) + + PublicIPPrefixesClient := network.NewPublicIPPrefixesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PublicIPPrefixesClient.Client, o.ResourceManagerAuthorizer) + + RoutesClient := network.NewRoutesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RoutesClient.Client, o.ResourceManagerAuthorizer) + + RouteTablesClient := network.NewRouteTablesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RouteTablesClient.Client, o.ResourceManagerAuthorizer) + + SecurityGroupClient := network.NewSecurityGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SecurityGroupClient.Client, o.ResourceManagerAuthorizer) + + SecurityRuleClient := network.NewSecurityRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SecurityRuleClient.Client, o.ResourceManagerAuthorizer) + + SubnetsClient := network.NewSubnetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SubnetsClient.Client, o.ResourceManagerAuthorizer) + + VnetGatewayClient := network.NewVirtualNetworkGatewaysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VnetGatewayClient.Client, o.ResourceManagerAuthorizer) + + VnetGatewayConnectionsClient := network.NewVirtualNetworkGatewayConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VnetGatewayConnectionsClient.Client, o.ResourceManagerAuthorizer) + + VirtualWanClient := network.NewVirtualWansClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualWanClient.Client, o.ResourceManagerAuthorizer) + + WatcherClient := network.NewWatchersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WatcherClient.Client, o.ResourceManagerAuthorizer) + + WebApplicationFirewallPoliciesClient := network.NewWebApplicationFirewallPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&WebApplicationFirewallPoliciesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ApplicationGatewaysClient: &ApplicationGatewaysClient, + ApplicationSecurityGroupsClient: &ApplicationSecurityGroupsClient, + AzureFirewallsClient: &AzureFirewallsClient, + ConnectionMonitorsClient: &ConnectionMonitorsClient, + DDOSProtectionPlansClient: &DDOSProtectionPlansClient, + ExpressRouteAuthsClient: &ExpressRouteAuthsClient, + ExpressRouteCircuitsClient: &ExpressRouteCircuitsClient, + ExpressRoutePeeringsClient: &ExpressRoutePeeringsClient, + InterfacesClient: &InterfacesClient, + LoadBalancersClient: &LoadBalancersClient, + LocalNetworkGatewaysClient: &LocalNetworkGatewaysClient, + ProfileClient: &ProfileClient, + PacketCapturesClient: &PacketCapturesClient, + PublicIPsClient: &PublicIPsClient, + PublicIPPrefixesClient: &PublicIPPrefixesClient, + RoutesClient: &RoutesClient, + RouteTablesClient: &RouteTablesClient, + SecurityGroupClient: &SecurityGroupClient, + SecurityRuleClient: &SecurityRuleClient, + SubnetsClient: &SubnetsClient, + VnetGatewayConnectionsClient: &VnetGatewayConnectionsClient, + VnetGatewayClient: &VnetGatewayClient, + VnetClient: &VnetClient, + VnetPeeringsClient: &VnetPeeringsClient, + VirtualWanClient: &VirtualWanClient, + WatcherClient: &WatcherClient, + WebApplicationFirewallPoliciesClient: &WebApplicationFirewallPoliciesClient, + } +} diff --git a/azurerm/internal/services/network/network_security_group.go b/azurerm/internal/services/network/network_security_group.go new file mode 100644 index 000000000000..9ed32f257ac1 --- /dev/null +++ b/azurerm/internal/services/network/network_security_group.go @@ -0,0 +1,31 @@ +package network + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type NetworkSecurityGroupResourceID struct { + Base azure.ResourceID + + Name string +} + +func ParseNetworkSecurityGroupResourceID(input string) (*NetworkSecurityGroupResourceID, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse Network Security Group ID %q: %+v", input, err) + } + + networkSecurityGroup := NetworkSecurityGroupResourceID{ + Base: *id, + Name: id.Path["networkSecurityGroups"], + } + + if networkSecurityGroup.Name == "" { + return nil, fmt.Errorf("ID was missing the `networkSecurityGroups` element") + } + + return &networkSecurityGroup, nil +} diff --git a/azurerm/internal/services/network/network_security_group_test.go b/azurerm/internal/services/network/network_security_group_test.go new file mode 100644 index 000000000000..bad19fa33216 --- /dev/null +++ b/azurerm/internal/services/network/network_security_group_test.go @@ -0,0 +1,62 @@ +package network + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +func TestParseNetworkSecurityGroup(t *testing.T) { + testData := []struct { + Name string + Input string + Expected *RouteTableResourceID + }{ + { + Name: "Empty", + Input: "", + Expected: nil, + }, + { + Name: "No Network Security Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo", + Expected: nil, + }, + { + Name: "No Network Security Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/networkSecurityGroups/", + Expected: nil, + }, + { + Name: "Completed", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/networkSecurityGroups/example", + Expected: &RouteTableResourceID{ + Name: "example", + Base: azure.ResourceID{ + ResourceGroup: "foo", + }, + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := ParseNetworkSecurityGroupResourceID(v.Input) + if err != nil { + if v.Expected == nil { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + + if actual.Base.ResourceGroup != v.Expected.Base.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.Base.ResourceGroup, actual.Base.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/network/route_table.go b/azurerm/internal/services/network/route_table.go new file mode 100644 index 000000000000..d9e90c9cb12d --- /dev/null +++ b/azurerm/internal/services/network/route_table.go @@ -0,0 +1,34 @@ +package network + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +// NOTE: there's some nice things we can do with this around validation +// since these top level objects exist + +type RouteTableResourceID struct { + Base azure.ResourceID + + Name string +} + +func ParseRouteTableResourceID(input string) (*RouteTableResourceID, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse Route Table ID %q: %+v", input, err) + } + + routeTable := RouteTableResourceID{ + Base: *id, + Name: id.Path["routeTables"], + } + + if routeTable.Name == "" { + return nil, fmt.Errorf("ID was missing the `routeTables` element") + } + + return &routeTable, nil +} diff --git a/azurerm/internal/services/network/route_table_test.go b/azurerm/internal/services/network/route_table_test.go new file mode 100644 index 000000000000..3eea66a51b31 --- /dev/null +++ b/azurerm/internal/services/network/route_table_test.go @@ -0,0 +1,62 @@ +package network + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +func TestParseRouteTable(t *testing.T) { + testData := []struct { + Name string + Input string + Expected *RouteTableResourceID + }{ + { + Name: "Empty", + Input: "", + Expected: nil, + }, + { + Name: "No Route Table Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo", + Expected: nil, + }, + { + Name: "No Route Table Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/routeTables/", + Expected: nil, + }, + { + Name: "Completed", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/routeTables/example", + Expected: &RouteTableResourceID{ + Name: "example", + Base: azure.ResourceID{ + ResourceGroup: "foo", + }, + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := ParseRouteTableResourceID(v.Input) + if err != nil { + if v.Expected == nil { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + + if actual.Base.ResourceGroup != v.Expected.Base.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.Base.ResourceGroup, actual.Base.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/notificationhub/client.go b/azurerm/internal/services/notificationhub/client.go index 4bd7945a0e22..0ff86a62d2d8 100644 --- a/azurerm/internal/services/notificationhub/client.go +++ b/azurerm/internal/services/notificationhub/client.go @@ -2,9 +2,23 @@ package notificationhub import ( "github.com/Azure/azure-sdk-for-go/services/notificationhubs/mgmt/2017-04-01/notificationhubs" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" ) type Client struct { - HubsClient notificationhubs.Client - NamespacesClient notificationhubs.NamespacesClient + HubsClient *notificationhubs.Client + NamespacesClient *notificationhubs.NamespacesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + NamespacesClient := notificationhubs.NewNamespacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&NamespacesClient.Client, o.ResourceManagerAuthorizer) + + HubsClient := notificationhubs.NewClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&HubsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + HubsClient: &HubsClient, + NamespacesClient: &NamespacesClient, + } } diff --git a/azurerm/internal/services/policy/client.go b/azurerm/internal/services/policy/client.go index b55590dfee81..00e3d0262fb1 100644 --- a/azurerm/internal/services/policy/client.go +++ b/azurerm/internal/services/policy/client.go @@ -1,9 +1,29 @@ package policy -import "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy" +import ( + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - AssignmentsClient policy.AssignmentsClient - DefinitionsClient policy.DefinitionsClient - SetDefinitionsClient policy.SetDefinitionsClient + AssignmentsClient *policy.AssignmentsClient + DefinitionsClient *policy.DefinitionsClient + SetDefinitionsClient *policy.SetDefinitionsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AssignmentsClient := policy.NewAssignmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AssignmentsClient.Client, o.ResourceManagerAuthorizer) + + DefinitionsClient := policy.NewDefinitionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DefinitionsClient.Client, o.ResourceManagerAuthorizer) + + SetDefinitionsClient := policy.NewSetDefinitionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SetDefinitionsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AssignmentsClient: &AssignmentsClient, + DefinitionsClient: &DefinitionsClient, + SetDefinitionsClient: &SetDefinitionsClient, + } } diff --git a/azurerm/internal/services/portal/client.go b/azurerm/internal/services/portal/client.go new file mode 100644 index 000000000000..1246a83453a9 --- /dev/null +++ b/azurerm/internal/services/portal/client.go @@ -0,0 +1,19 @@ +package portal + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/portal/mgmt/2019-01-01-preview/portal" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + DashboardsClient *portal.DashboardsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + DashboardsClient := portal.NewDashboardsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DashboardsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + DashboardsClient: &DashboardsClient, + } +} diff --git a/azurerm/internal/services/postgres/client.go b/azurerm/internal/services/postgres/client.go new file mode 100644 index 000000000000..0444d1e08734 --- /dev/null +++ b/azurerm/internal/services/postgres/client.go @@ -0,0 +1,39 @@ +package postgres + +import ( + "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ConfigurationsClient *postgresql.ConfigurationsClient + DatabasesClient *postgresql.DatabasesClient + FirewallRulesClient *postgresql.FirewallRulesClient + ServersClient *postgresql.ServersClient + VirtualNetworkRulesClient *postgresql.VirtualNetworkRulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ConfigurationsClient := postgresql.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ConfigurationsClient.Client, o.ResourceManagerAuthorizer) + + DatabasesClient := postgresql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) + + FirewallRulesClient := postgresql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + ServersClient := postgresql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) + + VirtualNetworkRulesClient := postgresql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ConfigurationsClient: &ConfigurationsClient, + DatabasesClient: &DatabasesClient, + FirewallRulesClient: &FirewallRulesClient, + ServersClient: &ServersClient, + VirtualNetworkRulesClient: &VirtualNetworkRulesClient, + } +} diff --git a/azurerm/internal/services/privatedns/client.go b/azurerm/internal/services/privatedns/client.go new file mode 100644 index 000000000000..49d2feb879eb --- /dev/null +++ b/azurerm/internal/services/privatedns/client.go @@ -0,0 +1,29 @@ +package privatedns + +import ( + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + RecordSetsClient *privatedns.RecordSetsClient + PrivateZonesClient *privatedns.PrivateZonesClient + VirtualNetworkLinksClient *privatedns.VirtualNetworkLinksClient +} + +func BuildClient(o *common.ClientOptions) *Client { + RecordSetsClient := privatedns.NewRecordSetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&RecordSetsClient.Client, o.ResourceManagerAuthorizer) + + PrivateZonesClient := privatedns.NewPrivateZonesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PrivateZonesClient.Client, o.ResourceManagerAuthorizer) + + virtualNetworkLinksClient := privatedns.NewVirtualNetworkLinksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&virtualNetworkLinksClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + RecordSetsClient: &RecordSetsClient, + PrivateZonesClient: &PrivateZonesClient, + VirtualNetworkLinksClient: &virtualNetworkLinksClient, + } +} diff --git a/azurerm/internal/services/recoveryservices/client.go b/azurerm/internal/services/recoveryservices/client.go new file mode 100644 index 000000000000..7d3abc4f550d --- /dev/null +++ b/azurerm/internal/services/recoveryservices/client.go @@ -0,0 +1,79 @@ +package recoveryservices + +import ( + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2016-06-01/recoveryservices" + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2017-07-01/backup" + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ProtectedItemsClient *backup.ProtectedItemsGroupClient + ProtectionPoliciesClient *backup.ProtectionPoliciesClient + VaultsClient *recoveryservices.VaultsClient + FabricClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationFabricsClient + ProtectionContainerClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectionContainersClient + ReplicationPoliciesClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationPoliciesClient + ContainerMappingClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectionContainerMappingsClient + NetworkMappingClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationNetworkMappingsClient + ReplicationMigrationItemsClient func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectedItemsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + VaultsClient := recoveryservices.NewVaultsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VaultsClient.Client, o.ResourceManagerAuthorizer) + + ProtectedItemsClient := backup.NewProtectedItemsGroupClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProtectedItemsClient.Client, o.ResourceManagerAuthorizer) + + ProtectionPoliciesClient := backup.NewProtectionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProtectionPoliciesClient.Client, o.ResourceManagerAuthorizer) + + FabricClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationFabricsClient { + client := siterecovery.NewReplicationFabricsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + ProtectionContainerClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectionContainersClient { + client := siterecovery.NewReplicationProtectionContainersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + ReplicationPoliciesClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationPoliciesClient { + client := siterecovery.NewReplicationPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + ContainerMappingClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectionContainerMappingsClient { + client := siterecovery.NewReplicationProtectionContainerMappingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + NetworkMappingClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationNetworkMappingsClient { + client := siterecovery.NewReplicationNetworkMappingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + ReplicationMigrationItemsClient := func(resourceGroupName string, vaultName string) siterecovery.ReplicationProtectedItemsClient { + client := siterecovery.NewReplicationProtectedItemsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, resourceGroupName, vaultName) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + return client + } + + return &Client{ + ProtectedItemsClient: &ProtectedItemsClient, + ProtectionPoliciesClient: &ProtectionPoliciesClient, + VaultsClient: &VaultsClient, + FabricClient: FabricClient, + ProtectionContainerClient: ProtectionContainerClient, + ReplicationPoliciesClient: ReplicationPoliciesClient, + ContainerMappingClient: ContainerMappingClient, + NetworkMappingClient: NetworkMappingClient, + ReplicationMigrationItemsClient: ReplicationMigrationItemsClient, + } +} diff --git a/azurerm/internal/services/redis/client.go b/azurerm/internal/services/redis/client.go index ee82bd5a78f1..55ed2bbf81f9 100644 --- a/azurerm/internal/services/redis/client.go +++ b/azurerm/internal/services/redis/client.go @@ -1,9 +1,29 @@ package redis -import "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" +import ( + "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - Client redis.Client - FirewallRulesClient redis.FirewallRulesClient - PatchSchedulesClient redis.PatchSchedulesClient + Client *redis.Client + FirewallRulesClient *redis.FirewallRulesClient + PatchSchedulesClient *redis.PatchSchedulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + client := redis.NewClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + + FirewallRulesClient := redis.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + PatchSchedulesClient := redis.NewPatchSchedulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PatchSchedulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + Client: &client, + FirewallRulesClient: &FirewallRulesClient, + PatchSchedulesClient: &PatchSchedulesClient, + } } diff --git a/azurerm/internal/services/relay/client.go b/azurerm/internal/services/relay/client.go index 793f8f2aefd0..8b10bb708dbc 100644 --- a/azurerm/internal/services/relay/client.go +++ b/azurerm/internal/services/relay/client.go @@ -1,7 +1,19 @@ package relay -import "github.com/Azure/azure-sdk-for-go/services/relay/mgmt/2017-04-01/relay" +import ( + "github.com/Azure/azure-sdk-for-go/services/relay/mgmt/2017-04-01/relay" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - NamespacesClient relay.NamespacesClient + NamespacesClient *relay.NamespacesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + NamespacesClient := relay.NewNamespacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&NamespacesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + NamespacesClient: &NamespacesClient, + } } diff --git a/azurerm/internal/services/resource/client.go b/azurerm/internal/services/resource/client.go new file mode 100644 index 000000000000..afbf78542116 --- /dev/null +++ b/azurerm/internal/services/resource/client.go @@ -0,0 +1,37 @@ +package resource + +import ( + providers "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-09-01/locks" + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + GroupsClient *resources.GroupsClient + DeploymentsClient *resources.DeploymentsClient + LocksClient *locks.ManagementLocksClient + ProvidersClient *providers.ProvidersClient +} + +func BuildClient(o *common.ClientOptions) *Client { + LocksClient := locks.NewManagementLocksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&LocksClient.Client, o.ResourceManagerAuthorizer) + + DeploymentsClient := resources.NewDeploymentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DeploymentsClient.Client, o.ResourceManagerAuthorizer) + + GroupsClient := resources.NewGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&GroupsClient.Client, o.ResourceManagerAuthorizer) + + // this has to come from the Profile since this is shared with Stack + ProvidersClient := providers.NewProvidersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProvidersClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + GroupsClient: &GroupsClient, + DeploymentsClient: &DeploymentsClient, + LocksClient: &LocksClient, + ProvidersClient: &ProvidersClient, + } +} diff --git a/azurerm/internal/services/scheduler/client.go b/azurerm/internal/services/scheduler/client.go index 8c77e7eae484..61ae260b7d6e 100644 --- a/azurerm/internal/services/scheduler/client.go +++ b/azurerm/internal/services/scheduler/client.go @@ -1,9 +1,25 @@ package scheduler -import "github.com/Azure/azure-sdk-for-go/services/scheduler/mgmt/2016-03-01/scheduler" +import ( + "github.com/Azure/azure-sdk-for-go/services/scheduler/mgmt/2016-03-01/scheduler" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) // TODO: remove in 2.0 type Client struct { - JobCollectionsClient scheduler.JobCollectionsClient //nolint: megacheck - JobsClient scheduler.JobsClient //nolint: megacheck + JobCollectionsClient *scheduler.JobCollectionsClient //nolint: megacheck + JobsClient *scheduler.JobsClient //nolint: megacheck +} + +func BuildClient(o *common.ClientOptions) *Client { + JobCollectionsClient := scheduler.NewJobCollectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) //nolint: megacheck + o.ConfigureClient(&JobCollectionsClient.Client, o.ResourceManagerAuthorizer) + + JobsClient := scheduler.NewJobsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) //nolint: megacheck + o.ConfigureClient(&JobsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + JobCollectionsClient: &JobCollectionsClient, + JobsClient: &JobsClient, + } } diff --git a/azurerm/internal/services/search/client.go b/azurerm/internal/services/search/client.go index 07b384684798..51456406d798 100644 --- a/azurerm/internal/services/search/client.go +++ b/azurerm/internal/services/search/client.go @@ -1,8 +1,24 @@ package search -import "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2015-08-19/search" +import ( + "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2015-08-19/search" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - AdminKeysClient search.AdminKeysClient - ServicesClient search.ServicesClient + AdminKeysClient *search.AdminKeysClient + ServicesClient *search.ServicesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AdminKeysClient := search.NewAdminKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AdminKeysClient.Client, o.ResourceManagerAuthorizer) + + ServicesClient := search.NewServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServicesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AdminKeysClient: &AdminKeysClient, + ServicesClient: &ServicesClient, + } } diff --git a/azurerm/internal/services/securitycenter/client.go b/azurerm/internal/services/securitycenter/client.go new file mode 100644 index 000000000000..109230cb6e4c --- /dev/null +++ b/azurerm/internal/services/securitycenter/client.go @@ -0,0 +1,36 @@ +package securitycenter + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v1.0/security" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ContactsClient *security.ContactsClient + PricingClient *security.PricingsClient + WorkspaceClient *security.WorkspaceSettingsClient + AdvancedThreatProtectionClient *security.AdvancedThreatProtectionClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ascLocation := "Global" + + ContactsClient := security.NewContactsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) + o.ConfigureClient(&ContactsClient.Client, o.ResourceManagerAuthorizer) + + PricingClient := security.NewPricingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) + o.ConfigureClient(&PricingClient.Client, o.ResourceManagerAuthorizer) + + WorkspaceClient := security.NewWorkspaceSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) + o.ConfigureClient(&WorkspaceClient.Client, o.ResourceManagerAuthorizer) + + AdvancedThreatProtectionClient := security.NewAdvancedThreatProtectionClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) + o.ConfigureClient(&AdvancedThreatProtectionClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ContactsClient: &ContactsClient, + PricingClient: &PricingClient, + WorkspaceClient: &WorkspaceClient, + AdvancedThreatProtectionClient: &AdvancedThreatProtectionClient, + } +} diff --git a/azurerm/internal/services/servicebus/client.go b/azurerm/internal/services/servicebus/client.go index a05b7e43875e..b5c18a1d1bbc 100644 --- a/azurerm/internal/services/servicebus/client.go +++ b/azurerm/internal/services/servicebus/client.go @@ -1,11 +1,45 @@ package servicebus -import "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" +import ( + servicebusPreview "github.com/Azure/azure-sdk-for-go/services/preview/servicebus/mgmt/2018-01-01-preview/servicebus" + "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - QueuesClient servicebus.QueuesClient - NamespacesClient servicebus.NamespacesClient - TopicsClient servicebus.TopicsClient - SubscriptionsClient servicebus.SubscriptionsClient - SubscriptionRulesClient servicebus.RulesClient + QueuesClient *servicebus.QueuesClient + NamespacesClient *servicebus.NamespacesClient + NamespacesClientPreview *servicebusPreview.NamespacesClient + TopicsClient *servicebus.TopicsClient + SubscriptionsClient *servicebus.SubscriptionsClient + SubscriptionRulesClient *servicebus.RulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + QueuesClient := servicebus.NewQueuesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&QueuesClient.Client, o.ResourceManagerAuthorizer) + + NamespacesClient := servicebus.NewNamespacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&NamespacesClient.Client, o.ResourceManagerAuthorizer) + + NamespacesClientPreview := servicebusPreview.NewNamespacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&NamespacesClientPreview.Client, o.ResourceManagerAuthorizer) + + TopicsClient := servicebus.NewTopicsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&TopicsClient.Client, o.ResourceManagerAuthorizer) + + SubscriptionsClient := servicebus.NewSubscriptionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SubscriptionsClient.Client, o.ResourceManagerAuthorizer) + + SubscriptionRulesClient := servicebus.NewRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&SubscriptionRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + QueuesClient: &QueuesClient, + NamespacesClient: &NamespacesClient, + NamespacesClientPreview: &NamespacesClientPreview, + TopicsClient: &TopicsClient, + SubscriptionsClient: &SubscriptionsClient, + SubscriptionRulesClient: &SubscriptionRulesClient, + } } diff --git a/azurerm/internal/services/servicefabric/client.go b/azurerm/internal/services/servicefabric/client.go new file mode 100644 index 000000000000..b887a88e9c0b --- /dev/null +++ b/azurerm/internal/services/servicefabric/client.go @@ -0,0 +1,19 @@ +package servicefabric + +import ( + "github.com/Azure/azure-sdk-for-go/services/servicefabric/mgmt/2018-02-01/servicefabric" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + ClustersClient *servicefabric.ClustersClient +} + +func BuildClient(o *common.ClientOptions) *Client { + ClustersClient := servicefabric.NewClustersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ClustersClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + ClustersClient: &ClustersClient, + } +} diff --git a/azurerm/internal/services/signalr/client.go b/azurerm/internal/services/signalr/client.go index 8aea295374b2..8786b4382c71 100644 --- a/azurerm/internal/services/signalr/client.go +++ b/azurerm/internal/services/signalr/client.go @@ -1,7 +1,19 @@ package signalr -import "github.com/Azure/azure-sdk-for-go/services/preview/signalr/mgmt/2018-03-01-preview/signalr" +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/signalr/mgmt/2018-03-01-preview/signalr" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) type Client struct { - Client signalr.Client + Client *signalr.Client +} + +func BuildClient(o *common.ClientOptions) *Client { + client := signalr.NewClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + + return &Client{ + Client: &client, + } } diff --git a/azurerm/internal/services/sql/client.go b/azurerm/internal/services/sql/client.go new file mode 100644 index 000000000000..95458dca13fa --- /dev/null +++ b/azurerm/internal/services/sql/client.go @@ -0,0 +1,55 @@ +package sql + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + DatabasesClient *sql.DatabasesClient + DatabaseThreatDetectionPoliciesClient *sql.DatabaseThreatDetectionPoliciesClient + ElasticPoolsClient *sql.ElasticPoolsClient + FirewallRulesClient *sql.FirewallRulesClient + FailoverGroupsClient *sql.FailoverGroupsClient + ServersClient *sql.ServersClient + ServerAzureADAdministratorsClient *sql.ServerAzureADAdministratorsClient + VirtualNetworkRulesClient *sql.VirtualNetworkRulesClient +} + +func BuildClient(o *common.ClientOptions) *Client { + // SQL Azure + DatabasesClient := sql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) + + DatabaseThreatDetectionPoliciesClient := sql.NewDatabaseThreatDetectionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&DatabaseThreatDetectionPoliciesClient.Client, o.ResourceManagerAuthorizer) + + ElasticPoolsClient := sql.NewElasticPoolsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ElasticPoolsClient.Client, o.ResourceManagerAuthorizer) + + FailoverGroupsClient := sql.NewFailoverGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FailoverGroupsClient.Client, o.ResourceManagerAuthorizer) + + FirewallRulesClient := sql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + + ServersClient := sql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) + + ServerAzureADAdministratorsClient := sql.NewServerAzureADAdministratorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ServerAzureADAdministratorsClient.Client, o.ResourceManagerAuthorizer) + + VirtualNetworkRulesClient := sql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + DatabasesClient: &DatabasesClient, + DatabaseThreatDetectionPoliciesClient: &DatabaseThreatDetectionPoliciesClient, + ElasticPoolsClient: &ElasticPoolsClient, + FailoverGroupsClient: &FailoverGroupsClient, + FirewallRulesClient: &FirewallRulesClient, + ServersClient: &ServersClient, + ServerAzureADAdministratorsClient: &ServerAzureADAdministratorsClient, + VirtualNetworkRulesClient: &VirtualNetworkRulesClient, + } +} diff --git a/azurerm/internal/services/storage/blobs.go b/azurerm/internal/services/storage/blobs.go new file mode 100644 index 000000000000..ceb220822fe9 --- /dev/null +++ b/azurerm/internal/services/storage/blobs.go @@ -0,0 +1,348 @@ +package storage + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + "strings" + "sync" + "time" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" +) + +const pollingInterval = time.Second * 15 + +type BlobUpload struct { + Client *blobs.Client + + AccountName string + BlobName string + ContainerName string + + BlobType string + ContentType string + MetaData map[string]string + Parallelism int + Size int + Source string + SourceContent string + SourceUri string +} + +func (sbu BlobUpload) Create(ctx context.Context) error { + blobType := strings.ToLower(sbu.BlobType) + + if blobType == "append" { + if sbu.Source != "" || sbu.SourceContent != "" || sbu.SourceUri != "" { + return fmt.Errorf("A source cannot be specified for an Append blob") + } + + return sbu.createEmptyAppendBlob(ctx) + } + + if blobType == "block" { + if sbu.SourceUri != "" { + return sbu.copy(ctx) + } + + if sbu.SourceContent != "" { + return sbu.uploadBlockBlobFromContent(ctx) + } + if sbu.Source != "" { + return sbu.uploadBlockBlob(ctx) + } + + return sbu.createEmptyBlockBlob(ctx) + } + + if blobType == "page" { + if sbu.SourceUri != "" { + return sbu.copy(ctx) + } + if sbu.SourceContent != "" { + return fmt.Errorf("`source_content` cannot be specified for a Page blob") + } + if sbu.Source != "" { + return sbu.uploadPageBlob(ctx) + } + + return sbu.createEmptyPageBlob(ctx) + } + + return fmt.Errorf("Unsupported Blob Type: %q", blobType) +} + +func (sbu BlobUpload) copy(ctx context.Context) error { + input := blobs.CopyInput{ + CopySource: sbu.SourceUri, + MetaData: sbu.MetaData, + } + if err := sbu.Client.CopyAndWait(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input, pollingInterval); err != nil { + return fmt.Errorf("Error copy/waiting: %s", err) + } + + return nil +} + +func (sbu BlobUpload) createEmptyAppendBlob(ctx context.Context) error { + input := blobs.PutAppendBlobInput{ + ContentType: utils.String(sbu.ContentType), + MetaData: sbu.MetaData, + } + if _, err := sbu.Client.PutAppendBlob(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input); err != nil { + return fmt.Errorf("Error PutAppendBlob: %s", err) + } + + return nil +} + +func (sbu BlobUpload) createEmptyBlockBlob(ctx context.Context) error { + input := blobs.PutBlockBlobInput{ + ContentType: utils.String(sbu.ContentType), + MetaData: sbu.MetaData, + } + if _, err := sbu.Client.PutBlockBlob(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input); err != nil { + return fmt.Errorf("Error PutBlockBlob: %s", err) + } + + return nil +} + +func (sbu BlobUpload) uploadBlockBlobFromContent(ctx context.Context) error { + tmpFile, err := ioutil.TempFile(os.TempDir(), "upload-") + if err != nil { + return fmt.Errorf("Error creating temporary file: %s", err) + } + defer os.Remove(tmpFile.Name()) + + if _, err = tmpFile.Write([]byte(sbu.SourceContent)); err != nil { + return fmt.Errorf("Error writing Source Content to Temp File: %s", err) + } + defer tmpFile.Close() + + sbu.Source = tmpFile.Name() + return sbu.uploadBlockBlob(ctx) +} + +func (sbu BlobUpload) uploadBlockBlob(ctx context.Context) error { + file, err := os.Open(sbu.Source) + if err != nil { + return fmt.Errorf("Error opening: %s", err) + } + defer file.Close() + + input := blobs.PutBlockBlobInput{ + ContentType: utils.String(sbu.ContentType), + MetaData: sbu.MetaData, + } + if err := sbu.Client.PutBlockBlobFromFile(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, file, input); err != nil { + return fmt.Errorf("Error PutBlockBlobFromFile: %s", err) + } + + return nil +} + +func (sbu BlobUpload) createEmptyPageBlob(ctx context.Context) error { + if sbu.Size == 0 { + return fmt.Errorf("`size` cannot be zero for a page blob") + } + + input := blobs.PutPageBlobInput{ + BlobContentLengthBytes: int64(sbu.Size), + ContentType: utils.String(sbu.ContentType), + MetaData: sbu.MetaData, + } + if _, err := sbu.Client.PutPageBlob(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input); err != nil { + return fmt.Errorf("Error PutPageBlob: %s", err) + } + + return nil +} + +func (sbu BlobUpload) uploadPageBlob(ctx context.Context) error { + if sbu.Size != 0 { + return fmt.Errorf("`size` cannot be set for an uploaded page blob") + } + + // determine the details about the file + file, err := os.Open(sbu.Source) + if err != nil { + return fmt.Errorf("Error opening source file for upload %q: %s", sbu.Source, err) + } + defer file.Close() + + // TODO: all of this ultimately can be moved into Giovanni + + info, err := file.Stat() + if err != nil { + return fmt.Errorf("Could not stat file %q: %s", file.Name(), err) + } + + fileSize := info.Size() + + // first let's create a file of the specified file size + input := blobs.PutPageBlobInput{ + BlobContentLengthBytes: fileSize, + ContentType: utils.String(sbu.ContentType), + MetaData: sbu.MetaData, + } + if _, err := sbu.Client.PutPageBlob(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input); err != nil { + return fmt.Errorf("Error PutPageBlob: %s", err) + } + + if err := sbu.pageUploadFromSource(ctx, file, fileSize); err != nil { + return fmt.Errorf("Error creating storage blob on Azure: %s", err) + } + + return nil +} + +// TODO: move below here into Giovanni + +type storageBlobPage struct { + offset int64 + section *io.SectionReader +} + +func (sbu BlobUpload) pageUploadFromSource(ctx context.Context, file io.ReaderAt, fileSize int64) error { + workerCount := sbu.Parallelism * runtime.NumCPU() + + // first we chunk the file and assign them to 'pages' + pageList, err := sbu.storageBlobPageSplit(file, fileSize) + if err != nil { + return fmt.Errorf("Error splitting source file %q into pages: %s", sbu.Source, err) + } + + // finally we upload the contents of said file + pages := make(chan storageBlobPage, len(pageList)) + errors := make(chan error, len(pageList)) + wg := &sync.WaitGroup{} + wg.Add(len(pageList)) + + total := int64(0) + for _, page := range pageList { + total += page.section.Size() + pages <- page + } + close(pages) + + for i := 0; i < workerCount; i++ { + go sbu.blobPageUploadWorker(ctx, blobPageUploadContext{ + blobSize: fileSize, + pages: pages, + errors: errors, + wg: wg, + }) + } + + wg.Wait() + + if len(errors) > 0 { + return fmt.Errorf("Error while uploading source file %q: %s", sbu.Source, <-errors) + } + + return nil +} + +const ( + minPageSize int64 = 4 * 1024 + + // TODO: investigate whether this can be bumped to 100MB with the new API + maxPageSize int64 = 4 * 1024 * 1024 +) + +func (sbu BlobUpload) storageBlobPageSplit(file io.ReaderAt, fileSize int64) ([]storageBlobPage, error) { + // whilst the file Size can be any arbitrary Size, it must be uploaded in fixed-Size pages + blobSize := fileSize + if fileSize%minPageSize != 0 { + blobSize = fileSize + (minPageSize - (fileSize % minPageSize)) + } + + emptyPage := make([]byte, minPageSize) + + type byteRange struct { + offset int64 + length int64 + } + + var nonEmptyRanges []byteRange + var currentRange byteRange + for i := int64(0); i < blobSize; i += minPageSize { + pageBuf := make([]byte, minPageSize) + if _, err := file.ReadAt(pageBuf, i); err != nil && err != io.EOF { + return nil, fmt.Errorf("Could not read chunk at %d: %s", i, err) + } + + if bytes.Equal(pageBuf, emptyPage) { + if currentRange.length != 0 { + nonEmptyRanges = append(nonEmptyRanges, currentRange) + } + currentRange = byteRange{ + offset: i + minPageSize, + } + } else { + currentRange.length += minPageSize + if currentRange.length == maxPageSize || (currentRange.offset+currentRange.length == blobSize) { + nonEmptyRanges = append(nonEmptyRanges, currentRange) + currentRange = byteRange{ + offset: i + minPageSize, + } + } + } + } + + var pages []storageBlobPage + for _, nonEmptyRange := range nonEmptyRanges { + pages = append(pages, storageBlobPage{ + offset: nonEmptyRange.offset, + section: io.NewSectionReader(file, nonEmptyRange.offset, nonEmptyRange.length), + }) + } + + return pages, nil +} + +type blobPageUploadContext struct { + blobSize int64 + pages chan storageBlobPage + errors chan error + wg *sync.WaitGroup +} + +func (sbu BlobUpload) blobPageUploadWorker(ctx context.Context, uploadCtx blobPageUploadContext) { + for page := range uploadCtx.pages { + start := page.offset + end := page.offset + page.section.Size() - 1 + if end > uploadCtx.blobSize-1 { + end = uploadCtx.blobSize - 1 + } + size := end - start + 1 + + chunk := make([]byte, size) + if _, err := page.section.Read(chunk); err != nil && err != io.EOF { + uploadCtx.errors <- fmt.Errorf("Error reading source file %q at offset %d: %s", sbu.Source, page.offset, err) + uploadCtx.wg.Done() + continue + } + + input := blobs.PutPageUpdateInput{ + StartByte: start, + EndByte: end, + Content: chunk, + } + + if _, err := sbu.Client.PutPageUpdate(ctx, sbu.AccountName, sbu.ContainerName, sbu.BlobName, input); err != nil { + uploadCtx.errors <- fmt.Errorf("Error writing page at offset %d for file %q: %s", page.offset, sbu.Source, err) + uploadCtx.wg.Done() + continue + } + + uploadCtx.wg.Done() + } +} diff --git a/azurerm/internal/services/storage/client.go b/azurerm/internal/services/storage/client.go new file mode 100644 index 000000000000..bd1b7a00dacf --- /dev/null +++ b/azurerm/internal/services/storage/client.go @@ -0,0 +1,132 @@ +package storage + +import ( + "context" + "fmt" + + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/datalakestore/filesystems" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + az "github.com/Azure/go-autorest/autorest/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/authorizers" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/shares" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/tables" +) + +type Client struct { + AccountsClient *storage.AccountsClient + FileSystemsClient *filesystems.Client + ManagementPoliciesClient storage.ManagementPoliciesClient + + environment az.Environment +} + +func BuildClient(options *common.ClientOptions) *Client { + accountsClient := storage.NewAccountsClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId) + options.ConfigureClient(&accountsClient.Client, options.ResourceManagerAuthorizer) + + fileSystemsClient := filesystems.NewWithEnvironment(options.Environment) + fileSystemsClient.Authorizer = options.StorageAuthorizer + + managementPoliciesClient := storage.NewManagementPoliciesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId) + options.ConfigureClient(&managementPoliciesClient.Client, options.ResourceManagerAuthorizer) + + // TODO: switch Storage Containers to using the storage.BlobContainersClient + // (which should fix #2977) when the storage clients have been moved in here + return &Client{ + AccountsClient: &accountsClient, + FileSystemsClient: &fileSystemsClient, + ManagementPoliciesClient: managementPoliciesClient, + environment: options.Environment, + } +} + +func (client Client) BlobsClient(ctx context.Context, resourceGroup, accountName string) (*blobs.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyAuthorizer(accountName, *accountKey) + blobsClient := blobs.NewWithEnvironment(client.environment) + blobsClient.Client.Authorizer = storageAuth + return &blobsClient, nil +} + +func (client Client) ContainersClient(ctx context.Context, resourceGroup, accountName string) (*containers.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyAuthorizer(accountName, *accountKey) + containersClient := containers.NewWithEnvironment(client.environment) + containersClient.Client.Authorizer = storageAuth + return &containersClient, nil +} + +func (client Client) FileShareDirectoriesClient(ctx context.Context, resourceGroup, accountName string) (*directories.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteAuthorizer(accountName, *accountKey) + directoriesClient := directories.NewWithEnvironment(client.environment) + directoriesClient.Client.Authorizer = storageAuth + return &directoriesClient, nil +} + +func (client Client) FileSharesClient(ctx context.Context, resourceGroup, accountName string) (*shares.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteAuthorizer(accountName, *accountKey) + directoriesClient := shares.NewWithEnvironment(client.environment) + directoriesClient.Client.Authorizer = storageAuth + return &directoriesClient, nil +} + +func (client Client) QueuesClient(ctx context.Context, resourceGroup, accountName string) (*queues.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteAuthorizer(accountName, *accountKey) + queuesClient := queues.NewWithEnvironment(client.environment) + queuesClient.Client.Authorizer = storageAuth + return &queuesClient, nil +} + +func (client Client) TableEntityClient(ctx context.Context, resourceGroup, accountName string) (*entities.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteTableAuthorizer(accountName, *accountKey) + entitiesClient := entities.NewWithEnvironment(client.environment) + entitiesClient.Client.Authorizer = storageAuth + return &entitiesClient, nil +} + +func (client Client) TablesClient(ctx context.Context, resourceGroup, accountName string) (*tables.Client, error) { + accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error retrieving Account Key: %s", err) + } + + storageAuth := authorizers.NewSharedKeyLiteTableAuthorizer(accountName, *accountKey) + tablesClient := tables.NewWithEnvironment(client.environment) + tablesClient.Client.Authorizer = storageAuth + return &tablesClient, nil +} diff --git a/azurerm/internal/services/storage/helpers.go b/azurerm/internal/services/storage/helpers.go new file mode 100644 index 000000000000..9dcf82b5c623 --- /dev/null +++ b/azurerm/internal/services/storage/helpers.go @@ -0,0 +1,100 @@ +package storage + +import ( + "context" + "fmt" + "log" + "strings" + "sync" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +var ( + accountKeysCache = map[string]string{} + resourceGroupNamesCache = map[string]string{} + writeLock = sync.RWMutex{} +) + +func (client Client) ClearFromCache(resourceGroup, accountName string) { + writeLock.Lock() + + log.Printf("[DEBUG] Removing Account %q (Resource Group %q) from the cache", accountName, resourceGroup) + accountCacheKey := fmt.Sprintf("%s-%s", resourceGroup, accountName) + delete(accountKeysCache, accountCacheKey) + + resourceGroupsCacheKey := accountName + delete(resourceGroupNamesCache, resourceGroupsCacheKey) + + log.Printf("[DEBUG] Removed Account %q (Resource Group %q) from the cache", accountName, resourceGroup) + writeLock.Unlock() +} + +func (client Client) FindResourceGroup(ctx context.Context, accountName string) (*string, error) { + cacheKey := accountName + if v, ok := resourceGroupNamesCache[cacheKey]; ok { + return &v, nil + } + + log.Printf("[DEBUG] Cache Miss - looking up the resource group for storage account %q..", accountName) + writeLock.Lock() + accounts, err := client.AccountsClient.List(ctx) + if err != nil { + return nil, fmt.Errorf("Error listing Storage Accounts (to find Resource Group for %q): %s", accountName, err) + } + + if accounts.Value == nil { + return nil, nil + } + + var resourceGroup *string + for _, account := range *accounts.Value { + if account.Name == nil || account.ID == nil { + continue + } + + if strings.EqualFold(accountName, *account.Name) { + id, err := azure.ParseAzureResourceID(*account.ID) + if err != nil { + return nil, fmt.Errorf("Error parsing ID for Storage Account %q: %s", accountName, err) + } + + resourceGroup = &id.ResourceGroup + break + } + } + + if resourceGroup != nil { + resourceGroupNamesCache[cacheKey] = *resourceGroup + } + + writeLock.Unlock() + + return resourceGroup, nil +} + +func (client Client) findAccountKey(ctx context.Context, resourceGroup, accountName string) (*string, error) { + cacheKey := fmt.Sprintf("%s-%s", resourceGroup, accountName) + if v, ok := accountKeysCache[cacheKey]; ok { + return &v, nil + } + + writeLock.Lock() + log.Printf("[DEBUG] Cache Miss - looking up the account key for storage account %q..", accountName) + props, err := client.AccountsClient.ListKeys(ctx, resourceGroup, accountName) + if err != nil { + return nil, fmt.Errorf("Error Listing Keys for Storage Account %q (Resource Group %q): %+v", accountName, resourceGroup, err) + } + + if props.Keys == nil || len(*props.Keys) == 0 { + return nil, fmt.Errorf("Keys were nil for Storage Account %q (Resource Group %q): %+v", accountName, resourceGroup, err) + } + + keys := *props.Keys + firstKey := keys[0].Value + + accountKeysCache[cacheKey] = *firstKey + writeLock.Unlock() + + return firstKey, nil +} diff --git a/azurerm/internal/services/storage/id.go b/azurerm/internal/services/storage/id.go new file mode 100644 index 000000000000..2d86d84d6c0e --- /dev/null +++ b/azurerm/internal/services/storage/id.go @@ -0,0 +1,69 @@ +package storage + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type AccountID struct { + Name string + ResourceGroup string + + ID azure.ResourceID +} + +func AccountIDSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateAccountID, + } +} + +func ParseAccountID(id string) (*AccountID, error) { + storageID, err := azure.ParseAzureResourceID(id) + if err != nil { + return nil, err + } + + resourceGroup := storageID.ResourceGroup + if resourceGroup == "" { + return nil, fmt.Errorf("%q is missing a Resource Group", id) + } + + storageAccountName := storageID.Path["storageAccounts"] + if storageAccountName == "" { + return nil, fmt.Errorf("%q is missing the `storageAccounts` segment", id) + } + + accountId := AccountID{ + Name: storageAccountName, + ResourceGroup: resourceGroup, + ID: *storageID, + } + return &accountId, nil +} + +func ValidateAccountID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + id, err := azure.ParseAzureResourceID(v) + if err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a Resource Id: %v", v, err)) + } + + if id != nil { + if id.Path["storageAccounts"] == "" { + errors = append(errors, fmt.Errorf("The 'storageAccounts' segment is missing from Resource ID %q", v)) + } + } + + return warnings, errors +} diff --git a/azurerm/internal/services/storage/id_test.go b/azurerm/internal/services/storage/id_test.go new file mode 100644 index 000000000000..5ec93b2a0e8e --- /dev/null +++ b/azurerm/internal/services/storage/id_test.go @@ -0,0 +1,74 @@ +package storage + +import ( + "testing" +) + +func TestParseAccountID(t *testing.T) { + testData := []struct { + input string + expected *AccountID + }{ + { + input: "", + expected: nil, + }, + { + input: "/subscriptions/00000000-0000-0000-0000-000000000000", + expected: nil, + }, + { + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups", + expected: nil, + }, + { + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/hello", + expected: nil, + }, + { + // wrong case + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/hello/storageaccounts/account1", + expected: nil, + }, + { + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/hello/storageAccounts/account1", + expected: &AccountID{ + Name: "account1", + ResourceGroup: "hello", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + actual, err := ParseAccountID(v.input) + + // if we get something there shouldn't be an error + if v.expected != nil && err == nil { + continue + } + + // if nothing's expected we should get an error + if v.expected == nil && err != nil { + continue + } + + if v.expected == nil && actual == nil { + continue + } + + if v.expected == nil && actual != nil { + t.Fatalf("Expected nothing but got %+v", actual) + } + if v.expected != nil && actual == nil { + t.Fatalf("Expected %+v but got nil", actual) + } + + if v.expected.ResourceGroup != actual.ResourceGroup { + t.Fatalf("Expected ResourceGroup to be %q but got %q", v.expected.ResourceGroup, actual.ResourceGroup) + } + if v.expected.Name != actual.Name { + t.Fatalf("Expected Name to be %q but got %q", v.expected.Name, actual.Name) + } + } +} diff --git a/azurerm/internal/services/storage/metadata.go b/azurerm/internal/services/storage/metadata.go new file mode 100644 index 000000000000..5d0bcfa78513 --- /dev/null +++ b/azurerm/internal/services/storage/metadata.go @@ -0,0 +1,148 @@ +package storage + +import ( + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform/helper/schema" +) + +func MetaDataSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ValidateFunc: validateMetaDataKeys, + } +} + +func MetaDataComputedSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Computed: true, + ValidateFunc: validateMetaDataKeys, + } +} + +func ExpandMetaData(input map[string]interface{}) map[string]string { + output := make(map[string]string) + + for k, v := range input { + output[k] = v.(string) + } + + return output +} + +func FlattenMetaData(input map[string]string) map[string]interface{} { + output := make(map[string]interface{}) + + for k, v := range input { + output[k] = v + } + + return output +} + +func validateMetaDataKeys(value interface{}, _ string) (warnings []string, errors []error) { + v, ok := value.(map[string]interface{}) + if !ok { + return + } + + for k := range v { + isCSharpKeyword := cSharpKeywords[strings.ToLower(k)] != nil + if isCSharpKeyword { + errors = append(errors, fmt.Errorf("%q is not a valid key (C# keyword)", k)) + } + + // must begin with a letter, underscore + // the rest: letters, digits and underscores + r, _ := regexp.Compile(`^([a-z_]{1}[a-z0-9_]{1,})$`) + if !r.MatchString(k) { + errors = append(errors, fmt.Errorf("MetaData must start with letters or an underscores. Got %q.", k)) + } + } + + return +} + +var cSharpKeywords = map[string]*struct{}{ + "abstract": {}, + "as": {}, + "base": {}, + "bool": {}, + "break": {}, + "byte": {}, + "case": {}, + "catch": {}, + "char": {}, + "checked": {}, + "class": {}, + "const": {}, + "continue": {}, + "decimal": {}, + "default": {}, + "delegate": {}, + "do": {}, + "double": {}, + "else": {}, + "enum": {}, + "event": {}, + "explicit": {}, + "extern": {}, + "false": {}, + "finally": {}, + "fixed": {}, + "float": {}, + "for": {}, + "foreach": {}, + "goto": {}, + "if": {}, + "implicit": {}, + "in": {}, + "int": {}, + "interface": {}, + "internal": {}, + "is": {}, + "lock": {}, + "long": {}, + "namespace": {}, + "new": {}, + "null": {}, + "object": {}, + "operator": {}, + "out": {}, + "override": {}, + "params": {}, + "private": {}, + "protected": {}, + "public": {}, + "readonly": {}, + "ref": {}, + "return": {}, + "sbyte": {}, + "sealed": {}, + "short": {}, + "sizeof": {}, + "stackalloc": {}, + "static": {}, + "string": {}, + "struct": {}, + "switch": {}, + "this": {}, + "throw": {}, + "true": {}, + "try": {}, + "typeof": {}, + "uint": {}, + "ulong": {}, + "unchecked": {}, + "unsafe": {}, + "ushort": {}, + "using": {}, + "void": {}, + "volatile": {}, + "while": {}, +} diff --git a/azurerm/internal/services/storage/metadata_test.go b/azurerm/internal/services/storage/metadata_test.go new file mode 100644 index 000000000000..84d045f49830 --- /dev/null +++ b/azurerm/internal/services/storage/metadata_test.go @@ -0,0 +1,61 @@ +package storage + +import "testing" + +func TestValidateMetaDataKeys(t *testing.T) { + testData := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "Hello", + Expected: false, + }, + { + Input: "hello", + Expected: true, + }, + { + Input: "hello", + Expected: true, + }, + { + // C# keyword + Input: "using", + Expected: false, + }, + { + Input: "0hello", + Expected: false, + }, + { + Input: "heLLo", + Expected: false, + }, + { + Input: "panda_cycle", + Expected: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + value := map[string]interface{}{ + v.Input: "hello", + } + warnings, errors := validateMetaDataKeys(value, "field") + if len(warnings) != 0 { + t.Fatalf("Expected no warnings but got %d", len(warnings)) + } + + actual := len(errors) == 0 + if v.Expected != actual { + t.Fatalf("Expected %t but got %t", v.Expected, actual) + } + } +} diff --git a/azurerm/internal/services/streamanalytics/client.go b/azurerm/internal/services/streamanalytics/client.go new file mode 100644 index 000000000000..d6a79e717b78 --- /dev/null +++ b/azurerm/internal/services/streamanalytics/client.go @@ -0,0 +1,38 @@ +package streamanalytics + +import ( + "github.com/Azure/azure-sdk-for-go/services/streamanalytics/mgmt/2016-03-01/streamanalytics" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + FunctionsClient *streamanalytics.FunctionsClient + JobsClient *streamanalytics.StreamingJobsClient + InputsClient *streamanalytics.InputsClient + OutputsClient *streamanalytics.OutputsClient + TransformationsClient *streamanalytics.TransformationsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + FunctionsClient := streamanalytics.NewFunctionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&FunctionsClient.Client, o.ResourceManagerAuthorizer) + + JobsClient := streamanalytics.NewStreamingJobsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&JobsClient.Client, o.ResourceManagerAuthorizer) + + InputsClient := streamanalytics.NewInputsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&InputsClient.Client, o.ResourceManagerAuthorizer) + + OutputsClient := streamanalytics.NewOutputsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&OutputsClient.Client, o.ResourceManagerAuthorizer) + + TransformationsClient := streamanalytics.NewTransformationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&TransformationsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ + FunctionsClient: &FunctionsClient, + JobsClient: &JobsClient, + InputsClient: &InputsClient, + OutputsClient: &OutputsClient, + TransformationsClient: &TransformationsClient, + } +} diff --git a/azurerm/internal/services/subscription/client.go b/azurerm/internal/services/subscription/client.go new file mode 100644 index 000000000000..fd186bb9ef3c --- /dev/null +++ b/azurerm/internal/services/subscription/client.go @@ -0,0 +1,19 @@ +package subscription + +import ( + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-06-01/subscriptions" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + Client *subscriptions.Client +} + +func BuildClient(o *common.ClientOptions) *Client { + client := subscriptions.NewClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer) + + return &Client{ + Client: &client, + } +} diff --git a/azurerm/internal/services/trafficmanager/client.go b/azurerm/internal/services/trafficmanager/client.go new file mode 100644 index 000000000000..4c74fb338589 --- /dev/null +++ b/azurerm/internal/services/trafficmanager/client.go @@ -0,0 +1,29 @@ +package trafficmanager + +import ( + "github.com/Azure/azure-sdk-for-go/services/trafficmanager/mgmt/2018-04-01/trafficmanager" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + GeographialHierarchiesClient *trafficmanager.GeographicHierarchiesClient + ProfilesClient *trafficmanager.ProfilesClient + EndpointsClient *trafficmanager.EndpointsClient +} + +func BuildClient(o *common.ClientOptions) *Client { + EndpointsClient := trafficmanager.NewEndpointsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&EndpointsClient.Client, o.ResourceManagerAuthorizer) + + GeographialHierarchiesClient := trafficmanager.NewGeographicHierarchiesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&GeographialHierarchiesClient.Client, o.ResourceManagerAuthorizer) + + ProfilesClient := trafficmanager.NewProfilesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ProfilesClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + GeographialHierarchiesClient: &GeographialHierarchiesClient, + ProfilesClient: &ProfilesClient, + EndpointsClient: &EndpointsClient, + } +} diff --git a/azurerm/internal/services/web/client.go b/azurerm/internal/services/web/client.go new file mode 100644 index 000000000000..a9812d707872 --- /dev/null +++ b/azurerm/internal/services/web/client.go @@ -0,0 +1,34 @@ +package web + +import ( + "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" +) + +type Client struct { + AppServicePlansClient *web.AppServicePlansClient + AppServicesClient *web.AppsClient + CertificatesClient *web.CertificatesClient + BaseClient *web.BaseClient +} + +func BuildClient(o *common.ClientOptions) *Client { + AppServicePlansClient := web.NewAppServicePlansClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AppServicePlansClient.Client, o.ResourceManagerAuthorizer) + + AppServicesClient := web.NewAppsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&AppServicesClient.Client, o.ResourceManagerAuthorizer) + + CertificatesClient := web.NewCertificatesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&CertificatesClient.Client, o.ResourceManagerAuthorizer) + + BaseClient := web.NewWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&BaseClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + AppServicePlansClient: &AppServicePlansClient, + AppServicesClient: &AppServicesClient, + CertificatesClient: &CertificatesClient, + BaseClient: &BaseClient, + } +} diff --git a/azurerm/internal/tags/expand.go b/azurerm/internal/tags/expand.go new file mode 100644 index 000000000000..deb6ce7827de --- /dev/null +++ b/azurerm/internal/tags/expand.go @@ -0,0 +1,13 @@ +package tags + +func Expand(tagsMap map[string]interface{}) map[string]*string { + output := make(map[string]*string, len(tagsMap)) + + for i, v := range tagsMap { + //Validate should have ignored this error already + value, _ := TagValueToString(v) + output[i] = &value + } + + return output +} diff --git a/azurerm/internal/tags/expand_test.go b/azurerm/internal/tags/expand_test.go new file mode 100644 index 000000000000..4911947150da --- /dev/null +++ b/azurerm/internal/tags/expand_test.go @@ -0,0 +1,33 @@ +package tags + +import ( + "fmt" + "testing" +) + +func TestExpand(t *testing.T) { + testData := make(map[string]interface{}) + testData["key1"] = "value1" + testData["key2"] = 21 + testData["key3"] = "value3" + + expanded := Expand(testData) + + if len(expanded) != 3 { + t.Fatalf("Expected 3 results in expanded tag map, got %d", len(expanded)) + } + + for k, v := range testData { + var strVal string + switch v := v.(type) { + case string: + strVal = v + case int: + strVal = fmt.Sprintf("%d", v) + } + + if *expanded[k] != strVal { + t.Fatalf("Expanded value %q incorrect: expected %q, got %q", k, strVal, *expanded[k]) + } + } +} diff --git a/azurerm/internal/tags/filter.go b/azurerm/internal/tags/filter.go new file mode 100644 index 000000000000..aab95faa8eab --- /dev/null +++ b/azurerm/internal/tags/filter.go @@ -0,0 +1,27 @@ +package tags + +import "strings" + +func Filter(tagsMap map[string]*string, tagNames ...string) map[string]*string { + if len(tagNames) == 0 { + return tagsMap + } + + // Build the filter dictionary from passed tag names. + filterDict := make(map[string]bool) + for _, name := range tagNames { + if len(name) > 0 { + filterDict[strings.ToLower(name)] = true + } + } + + // Filter out tag if it exists(case insensitive) in the dictionary. + tagsRet := make(map[string]*string) + for k, v := range tagsMap { + if !filterDict[strings.ToLower(k)] { + tagsRet[k] = v + } + } + + return tagsRet +} diff --git a/azurerm/internal/tags/filter_test.go b/azurerm/internal/tags/filter_test.go new file mode 100644 index 000000000000..9872bffb4a3a --- /dev/null +++ b/azurerm/internal/tags/filter_test.go @@ -0,0 +1,24 @@ +package tags + +import ( + "testing" +) + +func TestFilter(t *testing.T) { + testData := make(map[string]*string) + valueData := [3]string{"value1", "value2", "value3"} + + testData["key1"] = &valueData[0] + testData["key2"] = &valueData[1] + testData["key3"] = &valueData[2] + + filtered := Filter(testData, "key1", "key3", "") + + if len(filtered) != 1 { + t.Fatalf("Expected 1 result in filtered tag map, got %d", len(filtered)) + } + + if filtered["key2"] != &valueData[1] { + t.Fatalf("Expected %v in filtered tag map, got %v", valueData[1], *filtered["key2"]) + } +} diff --git a/azurerm/internal/tags/flatten.go b/azurerm/internal/tags/flatten.go new file mode 100644 index 000000000000..9661ee487a65 --- /dev/null +++ b/azurerm/internal/tags/flatten.go @@ -0,0 +1,31 @@ +package tags + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" +) + +func Flatten(tagMap map[string]*string) map[string]interface{} { + // If tagsMap is nil, len(tagsMap) will be 0. + output := make(map[string]interface{}, len(tagMap)) + + for i, v := range tagMap { + if v == nil { + continue + } + + output[i] = *v + } + + return output +} + +func FlattenAndSet(d *schema.ResourceData, tagMap map[string]*string) error { + flattened := Flatten(tagMap) + if err := d.Set("tags", flattened); err != nil { + return fmt.Errorf("Error setting `tags`: %s", err) + } + + return nil +} diff --git a/azurerm/internal/tags/flatten_test.go b/azurerm/internal/tags/flatten_test.go new file mode 100644 index 000000000000..55920fbb797a --- /dev/null +++ b/azurerm/internal/tags/flatten_test.go @@ -0,0 +1,53 @@ +package tags + +import ( + "reflect" + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestFlatten(t *testing.T) { + testData := []struct { + Name string + Input map[string]*string + Expected map[string]interface{} + }{ + { + Name: "Empty", + Input: map[string]*string{}, + Expected: map[string]interface{}{}, + }, + { + Name: "One Item", + Input: map[string]*string{ + "hello": utils.String("there"), + }, + Expected: map[string]interface{}{ + "hello": "there", + }, + }, + { + Name: "Multiple Items", + Input: map[string]*string{ + "euros": utils.String("3"), + "hello": utils.String("there"), + "panda": utils.String("pops"), + }, + Expected: map[string]interface{}{ + "euros": "3", + "hello": "there", + "panda": "pops", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Test %q", v.Name) + + actual := Flatten(v.Input) + if !reflect.DeepEqual(actual, v.Expected) { + t.Fatalf("Expected %+v but got %+v", actual, v.Expected) + } + } +} diff --git a/azurerm/internal/tags/schema.go b/azurerm/internal/tags/schema.go new file mode 100644 index 000000000000..36d7420fb7ef --- /dev/null +++ b/azurerm/internal/tags/schema.go @@ -0,0 +1,38 @@ +package tags + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +// SchemaDataSource returns the Schema which should be used for Tags on a Data Source +func SchemaDataSource() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + } +} + +// ForceNewSchema returns the Schema which should be used for Tags when changes +// require recreation of the resource +func ForceNewSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: Validate, + } +} + +// Schema returns the Schema used for Tags +func Schema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Computed: true, + ValidateFunc: Validate, + } +} diff --git a/azurerm/internal/tags/validation.go b/azurerm/internal/tags/validation.go new file mode 100644 index 000000000000..1bca2c7d3e61 --- /dev/null +++ b/azurerm/internal/tags/validation.go @@ -0,0 +1,37 @@ +package tags + +import "fmt" + +func Validate(v interface{}, _ string) (warnings []string, errors []error) { + tagsMap := v.(map[string]interface{}) + + if len(tagsMap) > 50 { + errors = append(errors, fmt.Errorf("a maximum of 50 tags can be applied to each ARM resource")) + } + + for k, v := range tagsMap { + if len(k) > 512 { + errors = append(errors, fmt.Errorf("the maximum length for a tag key is 512 characters: %q is %d characters", k, len(k))) + } + + value, err := TagValueToString(v) + if err != nil { + errors = append(errors, err) + } else if len(value) > 256 { + errors = append(errors, fmt.Errorf("the maximum length for a tag value is 256 characters: the value for %q is %d characters", k, len(value))) + } + } + + return warnings, errors +} + +func TagValueToString(v interface{}) (string, error) { + switch value := v.(type) { + case string: + return value, nil + case int: + return fmt.Sprintf("%d", value), nil + default: + return "", fmt.Errorf("unknown tag type %T in tag value", value) + } +} diff --git a/azurerm/internal/tags/validation_test.go b/azurerm/internal/tags/validation_test.go new file mode 100644 index 000000000000..02f749221046 --- /dev/null +++ b/azurerm/internal/tags/validation_test.go @@ -0,0 +1,69 @@ +package tags + +import ( + "fmt" + "strings" + "testing" +) + +func TestValidateMaximumNumberOfTags(t *testing.T) { + tagsMap := make(map[string]interface{}) + for i := 0; i < 51; i++ { + tagsMap[fmt.Sprintf("key%d", i)] = fmt.Sprintf("value%d", i) + } + + _, es := Validate(tagsMap, "tags") + + if len(es) != 1 { + t.Fatal("Expected one validation error for too many tags") + } + + if !strings.Contains(es[0].Error(), "a maximum of 50 tags") { + t.Fatal("Wrong validation error message for too many tags") + } +} + +func TestValidateTagMaxKeyLength(t *testing.T) { + tooLongKey := strings.Repeat("long", 128) + "a" + tagsMap := make(map[string]interface{}) + tagsMap[tooLongKey] = "value" + + _, es := Validate(tagsMap, "tags") + if len(es) != 1 { + t.Fatal("Expected one validation error for a key which is > 512 chars") + } + + if !strings.Contains(es[0].Error(), "maximum length for a tag key") { + t.Fatal("Wrong validation error message maximum tag key length") + } + + if !strings.Contains(es[0].Error(), tooLongKey) { + t.Fatal("Expected validated error to contain the key name") + } + + if !strings.Contains(es[0].Error(), "513") { + t.Fatal("Expected the length in the validation error for tag key") + } +} + +func TestValidateTagMaxValueLength(t *testing.T) { + tagsMap := make(map[string]interface{}) + tagsMap["toolong"] = strings.Repeat("long", 64) + "a" + + _, es := Validate(tagsMap, "tags") + if len(es) != 1 { + t.Fatal("Expected one validation error for a value which is > 256 chars") + } + + if !strings.Contains(es[0].Error(), "maximum length for a tag value") { + t.Fatal("Wrong validation error message for maximum tag value length") + } + + if !strings.Contains(es[0].Error(), "toolong") { + t.Fatal("Expected validated error to contain the key name") + } + + if !strings.Contains(es[0].Error(), "257") { + t.Fatal("Expected the length in the validation error for value") + } +} diff --git a/azurerm/internal/tf/state/state.go b/azurerm/internal/tf/state/state.go new file mode 100644 index 000000000000..386805ed9ad3 --- /dev/null +++ b/azurerm/internal/tf/state/state.go @@ -0,0 +1,9 @@ +package state + +import "strings" + +// IgnoreCase is a StateFunc from helper/schema that converts the +// supplied value to lower before saving to state for consistency. +func IgnoreCase(val interface{}) string { + return strings.ToLower(val.(string)) +} diff --git a/azurerm/loadbalancer.go b/azurerm/loadbalancer.go index a157b570217d..a3bc18b915cf 100644 --- a/azurerm/loadbalancer.go +++ b/azurerm/loadbalancer.go @@ -6,12 +6,13 @@ import ( "regexp" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" ) func resourceGroupAndLBNameFromId(loadBalancerId string) (string, string, error) { - id, err := parseAzureResourceID(loadBalancerId) + id, err := azure.ParseAzureResourceID(loadBalancerId) if err != nil { return "", "", err } @@ -22,7 +23,7 @@ func resourceGroupAndLBNameFromId(loadBalancerId string) (string, string, error) } func retrieveLoadBalancerById(loadBalancerId string, meta interface{}) (*network.LoadBalancer, bool, error) { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext resGroup, name, err := resourceGroupAndLBNameFromId(loadBalancerId) @@ -155,7 +156,7 @@ func loadBalancerSubResourceStateImporter(d *schema.ResourceData, _ interface{}) } lbID := strings.TrimSuffix(r.FindString(d.Id()), "/") - parsed, err := parseAzureResourceID(lbID) + parsed, err := azure.ParseAzureResourceID(lbID) if err != nil { return nil, fmt.Errorf("unable to parse loadbalancer id from %s", d.Id()) } diff --git a/azurerm/locks.go b/azurerm/locks.go deleted file mode 100644 index c07bd083aa52..000000000000 --- a/azurerm/locks.go +++ /dev/null @@ -1,24 +0,0 @@ -package azurerm - -// handle the case of using the same name for different kinds of resources -func azureRMLockByName(name string, resourceType string) { - updatedName := resourceType + "." + name - armMutexKV.Lock(updatedName) -} - -func azureRMLockMultipleByName(names *[]string, resourceType string) { - for _, name := range *names { - azureRMLockByName(name, resourceType) - } -} - -func azureRMUnlockByName(name string, resourceType string) { - updatedName := resourceType + "." + name - armMutexKV.Unlock(updatedName) -} - -func azureRMUnlockMultipleByName(names *[]string, resourceType string) { - for _, name := range *names { - azureRMUnlockByName(name, resourceType) - } -} diff --git a/azurerm/logic_apps.go b/azurerm/logic_apps.go index 948c5a85772d..10fc4d626832 100644 --- a/azurerm/logic_apps.go +++ b/azurerm/logic_apps.go @@ -6,7 +6,10 @@ import ( "github.com/Azure/azure-sdk-for-go/services/logic/mgmt/2016-06-01/logic" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -21,10 +24,10 @@ func resourceLogicAppTriggerUpdate(d *schema.ResourceData, meta interface{}, log } func resourceLogicAppComponentUpdate(d *schema.ResourceData, meta interface{}, kind string, propertyName string, logicAppId string, name string, vals map[string]interface{}, resourceName string) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(logicAppId) + id, err := azure.ParseAzureResourceID(logicAppId) if err != nil { return err } @@ -35,8 +38,8 @@ func resourceLogicAppComponentUpdate(d *schema.ResourceData, meta interface{}, k log.Printf("[DEBUG] Preparing arguments for Logic App Workspace %q (Resource Group %q) %s %q", logicAppName, resourceGroup, kind, name) // lock to prevent against Actions or Triggers conflicting - azureRMLockByName(logicAppName, logicAppResourceName) - defer azureRMUnlockByName(logicAppName, logicAppResourceName) + locks.ByName(logicAppName, logicAppResourceName) + defer locks.UnlockByName(logicAppName, logicAppResourceName) read, err := client.Get(ctx, resourceGroup, logicAppName) if err != nil { @@ -60,7 +63,7 @@ func resourceLogicAppComponentUpdate(d *schema.ResourceData, meta interface{}, k definition := read.WorkflowProperties.Definition.(map[string]interface{}) vs := definition[propertyName].(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { if _, hasExisting := vs[name]; hasExisting { return tf.ImportAsExistsError(resourceName, resourceId) } @@ -98,14 +101,14 @@ func resourceLogicAppTriggerRemove(d *schema.ResourceData, meta interface{}, res } func resourceLogicAppComponentRemove(d *schema.ResourceData, meta interface{}, kind, propertyName, resourceGroup, logicAppName, name string) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext log.Printf("[DEBUG] Preparing arguments for Logic App Workspace %q (Resource Group %q) %s %q Deletion", logicAppName, resourceGroup, kind, name) // lock to prevent against Actions, Parameters or Actions conflicting - azureRMLockByName(logicAppName, logicAppResourceName) - defer azureRMUnlockByName(logicAppName, logicAppResourceName) + locks.ByName(logicAppName, logicAppResourceName) + defer locks.UnlockByName(logicAppName, logicAppResourceName) read, err := client.Get(ctx, resourceGroup, logicAppName) if err != nil { @@ -155,14 +158,14 @@ func retrieveLogicAppTrigger(meta interface{}, resourceGroup, logicAppName, name } func retrieveLogicAppComponent(meta interface{}, resourceGroup, kind, propertyName, logicAppName, name string) (*map[string]interface{}, *logic.Workflow, error) { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext log.Printf("[DEBUG] Preparing arguments for Logic App Workspace %q (Resource Group %q) %s %q", logicAppName, resourceGroup, kind, name) // lock to prevent against Actions, Parameters or Actions conflicting - azureRMLockByName(logicAppName, logicAppResourceName) - defer azureRMUnlockByName(logicAppName, logicAppResourceName) + locks.ByName(logicAppName, logicAppResourceName) + defer locks.UnlockByName(logicAppName, logicAppResourceName) read, err := client.Get(ctx, resourceGroup, logicAppName) if err != nil { diff --git a/azurerm/logic_apps_test.go b/azurerm/logic_apps_test.go index 296a585acaeb..cb0452643db3 100644 --- a/azurerm/logic_apps_test.go +++ b/azurerm/logic_apps_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" ) func testCheckAzureRMLogicAppActionExists(resourceName string) resource.TestCheckFunc { @@ -17,7 +18,7 @@ func testCheckAzureRMLogicAppActionExists(resourceName string) resource.TestChec } logicAppId := rs.Primary.Attributes["logic_app_id"] - id, err := parseAzureResourceID(logicAppId) + id, err := azure.ParseAzureResourceID(logicAppId) if err != nil { return err } @@ -26,7 +27,7 @@ func testCheckAzureRMLogicAppActionExists(resourceName string) resource.TestChec workflowName := id.Path["workflows"] resourceGroup := id.ResourceGroup - client := testAccProvider.Meta().(*ArmClient).logicWorkflowsClient + client := testAccProvider.Meta().(*ArmClient).logic.WorkflowsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, workflowName) @@ -65,7 +66,7 @@ func testCheckAzureRMLogicAppTriggerExists(resourceName string) resource.TestChe } logicAppId := rs.Primary.Attributes["logic_app_id"] - id, err := parseAzureResourceID(logicAppId) + id, err := azure.ParseAzureResourceID(logicAppId) if err != nil { return err } @@ -74,7 +75,7 @@ func testCheckAzureRMLogicAppTriggerExists(resourceName string) resource.TestChe workflowName := id.Path["workflows"] resourceGroup := id.ResourceGroup - client := testAccProvider.Meta().(*ArmClient).logicWorkflowsClient + client := testAccProvider.Meta().(*ArmClient).logic.WorkflowsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, workflowName) diff --git a/azurerm/provider.go b/azurerm/provider.go index 184485b013f8..4da93102924f 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -1,59 +1,537 @@ package azurerm import ( - "crypto/sha1" - "encoding/base64" - "encoding/hex" "fmt" + "log" + "os" "strings" "github.com/hashicorp/go-azure-helpers/authentication" - "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/common" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) // Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { + // NOTE: as part of migrating Data Sources/Resources into Packages - we should be able to use + // the Service Registration interface to gradually migrate Data Sources/Resources over to the + // new pattern. + // However this requires that the following be done first: + // 1. (DONE) Migrating the top level functions into the internal package + // 2. (DONE) Finish migrating the SDK Clients into Packages + // 3. (DONE) Switch the remaining resources over to the new Storage SDK + // (so we can remove `getBlobStorageClientForStorageAccount` from `config.go`) + // 4. (DONE) Introducing a parent struct which becomes a nested field in `config.go` + // for those properties, to ease migration (probably internal/common/clients.go) + // + // 5. Making the SDK Clients public in the ArmClient + // 6. Migrating the Fields from the `ArmClient` to the new base `Client` + // But leaving the referencing accessing the top-level field e.g. + // type Client struct { // ./azurerm/internal/common/client.go + // Example example.Client + // } + // type ArmClient struct { // ./azurerm/config.go + // common.Client + // } + // Then access the fields using: `meta.(*ArmClient).Example.Inner` + // Rather than `meta.(*ArmClient).Client.Example.Inner` + // This allows us to have less code changes in Step 7 + // 7. This should allow us to Find+Replace `(*ArmClient)` to `*common.Client` + // Unfortunately this'll need to be in a big-bang, due to the fact this is cast + // All over the place + // + // For the moment/until that's done, we'll have to continue defining these inline + supportedServices := []common.ServiceRegistration{ + compute.Registration{}, + } + + dataSources := map[string]*schema.Resource{ + "azurerm_api_management": dataSourceApiManagementService(), + "azurerm_api_management_api": dataSourceApiManagementApi(), + "azurerm_api_management_group": dataSourceApiManagementGroup(), + "azurerm_api_management_product": dataSourceApiManagementProduct(), + "azurerm_api_management_user": dataSourceArmApiManagementUser(), + "azurerm_app_service_plan": dataSourceAppServicePlan(), + "azurerm_app_service_certificate": dataSourceAppServiceCertificate(), + "azurerm_app_service": dataSourceArmAppService(), + "azurerm_application_insights": dataSourceArmApplicationInsights(), + "azurerm_application_security_group": dataSourceArmApplicationSecurityGroup(), + "azurerm_automation_variable_bool": dataSourceArmAutomationVariableBool(), + "azurerm_automation_variable_datetime": dataSourceArmAutomationVariableDateTime(), + "azurerm_automation_variable_int": dataSourceArmAutomationVariableInt(), + "azurerm_automation_variable_string": dataSourceArmAutomationVariableString(), + "azurerm_availability_set": dataSourceArmAvailabilitySet(), + "azurerm_azuread_application": dataSourceArmAzureADApplication(), + "azurerm_azuread_service_principal": dataSourceArmActiveDirectoryServicePrincipal(), + "azurerm_batch_account": dataSourceArmBatchAccount(), + "azurerm_batch_certificate": dataSourceArmBatchCertificate(), + "azurerm_batch_pool": dataSourceArmBatchPool(), + "azurerm_builtin_role_definition": dataSourceArmBuiltInRoleDefinition(), + "azurerm_cdn_profile": dataSourceArmCdnProfile(), + "azurerm_client_config": dataSourceArmClientConfig(), + "azurerm_kubernetes_service_versions": dataSourceArmKubernetesServiceVersions(), + "azurerm_container_registry": dataSourceArmContainerRegistry(), + "azurerm_cosmosdb_account": dataSourceArmCosmosDbAccount(), + "azurerm_data_lake_store": dataSourceArmDataLakeStoreAccount(), + "azurerm_dev_test_lab": dataSourceArmDevTestLab(), + "azurerm_dev_test_virtual_network": dataSourceArmDevTestVirtualNetwork(), + "azurerm_dns_zone": dataSourceArmDnsZone(), + "azurerm_eventhub_namespace": dataSourceEventHubNamespace(), + "azurerm_express_route_circuit": dataSourceArmExpressRouteCircuit(), + "azurerm_firewall": dataSourceArmFirewall(), + "azurerm_image": dataSourceArmImage(), + "azurerm_hdinsight_cluster": dataSourceArmHDInsightSparkCluster(), + "azurerm_maps_account": dataSourceArmMapsAccount(), + "azurerm_key_vault_access_policy": dataSourceArmKeyVaultAccessPolicy(), + "azurerm_key_vault_key": dataSourceArmKeyVaultKey(), + "azurerm_key_vault_secret": dataSourceArmKeyVaultSecret(), + "azurerm_key_vault": dataSourceArmKeyVault(), + "azurerm_kubernetes_cluster": dataSourceArmKubernetesCluster(), + "azurerm_lb": dataSourceArmLoadBalancer(), + "azurerm_lb_backend_address_pool": dataSourceArmLoadBalancerBackendAddressPool(), + "azurerm_log_analytics_workspace": dataSourceLogAnalyticsWorkspace(), + "azurerm_logic_app_workflow": dataSourceArmLogicAppWorkflow(), + "azurerm_managed_disk": dataSourceArmManagedDisk(), + "azurerm_management_group": dataSourceArmManagementGroup(), + "azurerm_monitor_action_group": dataSourceArmMonitorActionGroup(), + "azurerm_monitor_diagnostic_categories": dataSourceArmMonitorDiagnosticCategories(), + "azurerm_monitor_log_profile": dataSourceArmMonitorLogProfile(), + "azurerm_mssql_elasticpool": dataSourceArmMsSqlElasticpool(), + "azurerm_network_ddos_protection_plan": dataSourceNetworkDDoSProtectionPlan(), + "azurerm_network_interface": dataSourceArmNetworkInterface(), + "azurerm_network_security_group": dataSourceArmNetworkSecurityGroup(), + "azurerm_network_watcher": dataSourceArmNetworkWatcher(), + "azurerm_notification_hub_namespace": dataSourceNotificationHubNamespace(), + "azurerm_notification_hub": dataSourceNotificationHub(), + "azurerm_platform_image": dataSourceArmPlatformImage(), + "azurerm_policy_definition": dataSourceArmPolicyDefinition(), + "azurerm_proximity_placement_group": dataSourceArmProximityPlacementGroup(), + "azurerm_public_ip": dataSourceArmPublicIP(), + "azurerm_public_ips": dataSourceArmPublicIPs(), + "azurerm_public_ip_prefix": dataSourceArmPublicIpPrefix(), + "azurerm_recovery_services_vault": dataSourceArmRecoveryServicesVault(), + "azurerm_recovery_services_protection_policy_vm": dataSourceArmRecoveryServicesProtectionPolicyVm(), + "azurerm_redis_cache": dataSourceArmRedisCache(), + "azurerm_resource_group": dataSourceArmResourceGroup(), + "azurerm_role_definition": dataSourceArmRoleDefinition(), + "azurerm_route_table": dataSourceArmRouteTable(), + "azurerm_scheduler_job_collection": dataSourceArmSchedulerJobCollection(), + "azurerm_servicebus_namespace": dataSourceArmServiceBusNamespace(), + "azurerm_servicebus_namespace_authorization_rule": dataSourceArmServiceBusNamespaceAuthorizationRule(), + "azurerm_shared_image_gallery": dataSourceArmSharedImageGallery(), + "azurerm_shared_image_version": dataSourceArmSharedImageVersion(), + "azurerm_shared_image": dataSourceArmSharedImage(), + "azurerm_snapshot": dataSourceArmSnapshot(), + "azurerm_sql_server": dataSourceSqlServer(), + "azurerm_sql_database": dataSourceSqlDatabase(), + "azurerm_stream_analytics_job": dataSourceArmStreamAnalyticsJob(), + "azurerm_storage_account_blob_container_sas": dataSourceArmStorageAccountBlobContainerSharedAccessSignature(), + "azurerm_storage_account_sas": dataSourceArmStorageAccountSharedAccessSignature(), + "azurerm_storage_account": dataSourceArmStorageAccount(), + "azurerm_storage_management_policy": dataSourceArmStorageManagementPolicy(), + "azurerm_subnet": dataSourceArmSubnet(), + "azurerm_subscription": dataSourceArmSubscription(), + "azurerm_subscriptions": dataSourceArmSubscriptions(), + "azurerm_traffic_manager_geographical_location": dataSourceArmTrafficManagerGeographicalLocation(), + "azurerm_user_assigned_identity": dataSourceArmUserAssignedIdentity(), + "azurerm_virtual_machine": dataSourceArmVirtualMachine(), + "azurerm_virtual_network_gateway": dataSourceArmVirtualNetworkGateway(), + "azurerm_virtual_network_gateway_connection": dataSourceArmVirtualNetworkGatewayConnection(), + "azurerm_virtual_network": dataSourceArmVirtualNetwork(), + } + + resources := map[string]*schema.Resource{ + "azurerm_analysis_services_server": resourceArmAnalysisServicesServer(), + "azurerm_api_management": resourceArmApiManagementService(), + "azurerm_api_management_api": resourceArmApiManagementApi(), + "azurerm_api_management_api_operation": resourceArmApiManagementApiOperation(), + "azurerm_api_management_api_operation_policy": resourceArmApiManagementApiOperationPolicy(), + "azurerm_api_management_api_policy": resourceArmApiManagementApiPolicy(), + "azurerm_api_management_api_schema": resourceArmApiManagementApiSchema(), + "azurerm_api_management_api_version_set": resourceArmApiManagementApiVersionSet(), + "azurerm_api_management_authorization_server": resourceArmApiManagementAuthorizationServer(), + "azurerm_api_management_backend": resourceArmApiManagementBackend(), + "azurerm_api_management_certificate": resourceArmApiManagementCertificate(), + "azurerm_api_management_group": resourceArmApiManagementGroup(), + "azurerm_api_management_group_user": resourceArmApiManagementGroupUser(), + "azurerm_api_management_logger": resourceArmApiManagementLogger(), + "azurerm_api_management_openid_connect_provider": resourceArmApiManagementOpenIDConnectProvider(), + "azurerm_api_management_product": resourceArmApiManagementProduct(), + "azurerm_api_management_product_api": resourceArmApiManagementProductApi(), + "azurerm_api_management_product_group": resourceArmApiManagementProductGroup(), + "azurerm_api_management_product_policy": resourceArmApiManagementProductPolicy(), + "azurerm_api_management_property": resourceArmApiManagementProperty(), + "azurerm_api_management_subscription": resourceArmApiManagementSubscription(), + "azurerm_api_management_user": resourceArmApiManagementUser(), + "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), + "azurerm_app_service_certificate": resourceArmAppServiceCertificate(), + "azurerm_app_service_custom_hostname_binding": resourceArmAppServiceCustomHostnameBinding(), + "azurerm_app_service_plan": resourceArmAppServicePlan(), + "azurerm_app_service_slot": resourceArmAppServiceSlot(), + "azurerm_app_service_source_control_token": resourceArmAppServiceSourceControlToken(), + "azurerm_app_service": resourceArmAppService(), + "azurerm_application_gateway": resourceArmApplicationGateway(), + "azurerm_application_insights_api_key": resourceArmApplicationInsightsAPIKey(), + "azurerm_application_insights": resourceArmApplicationInsights(), + "azurerm_application_insights_analytics_item": resourceArmApplicationInsightsAnalyticsItem(), + "azurerm_application_insights_web_test": resourceArmApplicationInsightsWebTests(), + "azurerm_application_security_group": resourceArmApplicationSecurityGroup(), + "azurerm_automation_account": resourceArmAutomationAccount(), + "azurerm_automation_credential": resourceArmAutomationCredential(), + "azurerm_automation_dsc_configuration": resourceArmAutomationDscConfiguration(), + "azurerm_automation_dsc_nodeconfiguration": resourceArmAutomationDscNodeConfiguration(), + "azurerm_automation_module": resourceArmAutomationModule(), + "azurerm_automation_runbook": resourceArmAutomationRunbook(), + "azurerm_automation_schedule": resourceArmAutomationSchedule(), + "azurerm_automation_variable_bool": resourceArmAutomationVariableBool(), + "azurerm_automation_variable_datetime": resourceArmAutomationVariableDateTime(), + "azurerm_automation_variable_int": resourceArmAutomationVariableInt(), + "azurerm_automation_variable_string": resourceArmAutomationVariableString(), + "azurerm_autoscale_setting": resourceArmAutoScaleSetting(), + "azurerm_availability_set": resourceArmAvailabilitySet(), + "azurerm_azuread_application": resourceArmActiveDirectoryApplication(), + "azurerm_azuread_service_principal_password": resourceArmActiveDirectoryServicePrincipalPassword(), + "azurerm_azuread_service_principal": resourceArmActiveDirectoryServicePrincipal(), + "azurerm_batch_account": resourceArmBatchAccount(), + "azurerm_batch_application": resourceArmBatchApplication(), + "azurerm_batch_certificate": resourceArmBatchCertificate(), + "azurerm_bot_channel_email": resourceArmBotChannelEmail(), + "azurerm_bot_channel_slack": resourceArmBotChannelSlack(), + "azurerm_bot_channels_registration": resourceArmBotChannelsRegistration(), + "azurerm_bot_connection": resourceArmBotConnection(), + "azurerm_bot_web_app": resourceArmBotWebApp(), + "azurerm_batch_pool": resourceArmBatchPool(), + "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), + "azurerm_cdn_profile": resourceArmCdnProfile(), + "azurerm_cognitive_account": resourceArmCognitiveAccount(), + "azurerm_connection_monitor": resourceArmConnectionMonitor(), + "azurerm_container_group": resourceArmContainerGroup(), + "azurerm_container_registry_webhook": resourceArmContainerRegistryWebhook(), + "azurerm_container_registry": resourceArmContainerRegistry(), + "azurerm_container_service": resourceArmContainerService(), + "azurerm_cosmosdb_account": resourceArmCosmosDbAccount(), + "azurerm_cosmosdb_cassandra_keyspace": resourceArmCosmosDbCassandraKeyspace(), + "azurerm_cosmosdb_mongo_collection": resourceArmCosmosDbMongoCollection(), + "azurerm_cosmosdb_mongo_database": resourceArmCosmosDbMongoDatabase(), + "azurerm_cosmosdb_sql_container": resourceArmCosmosDbSQLContainer(), + "azurerm_cosmosdb_sql_database": resourceArmCosmosDbSQLDatabase(), + "azurerm_cosmosdb_table": resourceArmCosmosDbTable(), + "azurerm_dashboard": resourceArmDashboard(), + "azurerm_data_factory": resourceArmDataFactory(), + "azurerm_data_factory_dataset_mysql": resourceArmDataFactoryDatasetMySQL(), + "azurerm_data_factory_dataset_postgresql": resourceArmDataFactoryDatasetPostgreSQL(), + "azurerm_data_factory_dataset_sql_server_table": resourceArmDataFactoryDatasetSQLServerTable(), + "azurerm_data_factory_linked_service_data_lake_storage_gen2": resourceArmDataFactoryLinkedServiceDataLakeStorageGen2(), + "azurerm_data_factory_linked_service_mysql": resourceArmDataFactoryLinkedServiceMySQL(), + "azurerm_data_factory_linked_service_postgresql": resourceArmDataFactoryLinkedServicePostgreSQL(), + "azurerm_data_factory_linked_service_sql_server": resourceArmDataFactoryLinkedServiceSQLServer(), + "azurerm_data_factory_pipeline": resourceArmDataFactoryPipeline(), + "azurerm_data_lake_analytics_account": resourceArmDataLakeAnalyticsAccount(), + "azurerm_data_lake_analytics_firewall_rule": resourceArmDataLakeAnalyticsFirewallRule(), + "azurerm_data_lake_store_file": resourceArmDataLakeStoreFile(), + "azurerm_data_lake_store_firewall_rule": resourceArmDataLakeStoreFirewallRule(), + "azurerm_data_lake_store": resourceArmDataLakeStore(), + "azurerm_databricks_workspace": resourceArmDatabricksWorkspace(), + "azurerm_ddos_protection_plan": resourceArmDDoSProtectionPlan(), + "azurerm_dev_test_lab": resourceArmDevTestLab(), + "azurerm_dev_test_schedule": resourceArmDevTestLabSchedules(), + "azurerm_dev_test_linux_virtual_machine": resourceArmDevTestLinuxVirtualMachine(), + "azurerm_dev_test_policy": resourceArmDevTestPolicy(), + "azurerm_dev_test_virtual_network": resourceArmDevTestVirtualNetwork(), + "azurerm_dev_test_windows_virtual_machine": resourceArmDevTestWindowsVirtualMachine(), + "azurerm_devspace_controller": resourceArmDevSpaceController(), + "azurerm_dns_a_record": resourceArmDnsARecord(), + "azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(), + "azurerm_dns_caa_record": resourceArmDnsCaaRecord(), + "azurerm_dns_cname_record": resourceArmDnsCNameRecord(), + "azurerm_dns_mx_record": resourceArmDnsMxRecord(), + "azurerm_dns_ns_record": resourceArmDnsNsRecord(), + "azurerm_dns_ptr_record": resourceArmDnsPtrRecord(), + "azurerm_dns_srv_record": resourceArmDnsSrvRecord(), + "azurerm_dns_txt_record": resourceArmDnsTxtRecord(), + "azurerm_dns_zone": resourceArmDnsZone(), + "azurerm_eventgrid_domain": resourceArmEventGridDomain(), + "azurerm_eventgrid_event_subscription": resourceArmEventGridEventSubscription(), + "azurerm_eventgrid_topic": resourceArmEventGridTopic(), + "azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(), + "azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(), + "azurerm_eventhub_namespace_authorization_rule": resourceArmEventHubNamespaceAuthorizationRule(), + "azurerm_eventhub_namespace_disaster_recovery_config": resourceArmEventHubNamespaceDisasterRecoveryConfig(), + "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), + "azurerm_eventhub": resourceArmEventHub(), + "azurerm_express_route_circuit_authorization": resourceArmExpressRouteCircuitAuthorization(), + "azurerm_express_route_circuit_peering": resourceArmExpressRouteCircuitPeering(), + "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), + "azurerm_firewall_application_rule_collection": resourceArmFirewallApplicationRuleCollection(), + "azurerm_firewall_nat_rule_collection": resourceArmFirewallNatRuleCollection(), + "azurerm_firewall_network_rule_collection": resourceArmFirewallNetworkRuleCollection(), + "azurerm_firewall": resourceArmFirewall(), + "azurerm_frontdoor": resourceArmFrontDoor(), + "azurerm_frontdoor_firewall_policy": resourceArmFrontDoorFirewallPolicy(), + "azurerm_function_app": resourceArmFunctionApp(), + "azurerm_hdinsight_hadoop_cluster": resourceArmHDInsightHadoopCluster(), + "azurerm_hdinsight_hbase_cluster": resourceArmHDInsightHBaseCluster(), + "azurerm_hdinsight_interactive_query_cluster": resourceArmHDInsightInteractiveQueryCluster(), + "azurerm_hdinsight_kafka_cluster": resourceArmHDInsightKafkaCluster(), + "azurerm_hdinsight_ml_services_cluster": resourceArmHDInsightMLServicesCluster(), + "azurerm_hdinsight_rserver_cluster": resourceArmHDInsightRServerCluster(), + "azurerm_hdinsight_spark_cluster": resourceArmHDInsightSparkCluster(), + "azurerm_hdinsight_storm_cluster": resourceArmHDInsightStormCluster(), + "azurerm_image": resourceArmImage(), + "azurerm_iot_dps": resourceArmIotDPS(), + "azurerm_iot_dps_certificate": resourceArmIotDPSCertificate(), + "azurerm_iothub_consumer_group": resourceArmIotHubConsumerGroup(), + "azurerm_iothub": resourceArmIotHub(), + "azurerm_iothub_shared_access_policy": resourceArmIotHubSharedAccessPolicy(), + "azurerm_key_vault_access_policy": resourceArmKeyVaultAccessPolicy(), + "azurerm_key_vault_certificate": resourceArmKeyVaultCertificate(), + "azurerm_key_vault_key": resourceArmKeyVaultKey(), + "azurerm_key_vault_secret": resourceArmKeyVaultSecret(), + "azurerm_key_vault": resourceArmKeyVault(), + "azurerm_kubernetes_cluster": resourceArmKubernetesCluster(), + "azurerm_kusto_cluster": resourceArmKustoCluster(), + "azurerm_kusto_database": resourceArmKustoDatabase(), + "azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(), + "azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(), + "azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(), + "azurerm_lb_probe": resourceArmLoadBalancerProbe(), + "azurerm_lb_outbound_rule": resourceArmLoadBalancerOutboundRule(), + "azurerm_lb_rule": resourceArmLoadBalancerRule(), + "azurerm_lb": resourceArmLoadBalancer(), + "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), + "azurerm_log_analytics_solution": resourceArmLogAnalyticsSolution(), + "azurerm_log_analytics_linked_service": resourceArmLogAnalyticsLinkedService(), + "azurerm_log_analytics_workspace_linked_service": resourceArmLogAnalyticsWorkspaceLinkedService(), + "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), + "azurerm_logic_app_action_custom": resourceArmLogicAppActionCustom(), + "azurerm_logic_app_action_http": resourceArmLogicAppActionHTTP(), + "azurerm_logic_app_trigger_custom": resourceArmLogicAppTriggerCustom(), + "azurerm_logic_app_trigger_http_request": resourceArmLogicAppTriggerHttpRequest(), + "azurerm_logic_app_trigger_recurrence": resourceArmLogicAppTriggerRecurrence(), + "azurerm_logic_app_workflow": resourceArmLogicAppWorkflow(), + "azurerm_managed_disk": resourceArmManagedDisk(), + "azurerm_management_group": resourceArmManagementGroup(), + "azurerm_management_lock": resourceArmManagementLock(), + "azurerm_maps_account": resourceArmMapsAccount(), + "azurerm_mariadb_configuration": resourceArmMariaDbConfiguration(), + "azurerm_mariadb_database": resourceArmMariaDbDatabase(), + "azurerm_mariadb_firewall_rule": resourceArmMariaDBFirewallRule(), + "azurerm_mariadb_server": resourceArmMariaDbServer(), + "azurerm_mariadb_virtual_network_rule": resourceArmMariaDbVirtualNetworkRule(), + "azurerm_marketplace_agreement": resourceArmMarketplaceAgreement(), + "azurerm_media_services_account": resourceArmMediaServicesAccount(), + "azurerm_metric_alertrule": resourceArmMetricAlertRule(), + "azurerm_monitor_autoscale_setting": resourceArmMonitorAutoScaleSetting(), + "azurerm_monitor_action_group": resourceArmMonitorActionGroup(), + "azurerm_monitor_activity_log_alert": resourceArmMonitorActivityLogAlert(), + "azurerm_monitor_diagnostic_setting": resourceArmMonitorDiagnosticSetting(), + "azurerm_monitor_log_profile": resourceArmMonitorLogProfile(), + "azurerm_monitor_metric_alert": resourceArmMonitorMetricAlert(), + "azurerm_monitor_metric_alertrule": resourceArmMonitorMetricAlertRule(), + "azurerm_mssql_elasticpool": resourceArmMsSqlElasticPool(), + "azurerm_mysql_configuration": resourceArmMySQLConfiguration(), + "azurerm_mysql_database": resourceArmMySqlDatabase(), + "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), + "azurerm_mysql_server": resourceArmMySqlServer(), + "azurerm_mysql_virtual_network_rule": resourceArmMySqlVirtualNetworkRule(), + "azurerm_network_connection_monitor": resourceArmNetworkConnectionMonitor(), + "azurerm_network_ddos_protection_plan": resourceArmNetworkDDoSProtectionPlan(), + "azurerm_network_interface": resourceArmNetworkInterface(), + "azurerm_network_interface_application_gateway_backend_address_pool_association": resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociation(), + "azurerm_network_interface_application_security_group_association": resourceArmNetworkInterfaceApplicationSecurityGroupAssociation(), + "azurerm_network_interface_backend_address_pool_association": resourceArmNetworkInterfaceBackendAddressPoolAssociation(), + "azurerm_network_interface_nat_rule_association": resourceArmNetworkInterfaceNatRuleAssociation(), + "azurerm_network_packet_capture": resourceArmNetworkPacketCapture(), + "azurerm_network_profile": resourceArmNetworkProfile(), + "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), + "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), + "azurerm_network_watcher": resourceArmNetworkWatcher(), + "azurerm_notification_hub_authorization_rule": resourceArmNotificationHubAuthorizationRule(), + "azurerm_notification_hub_namespace": resourceArmNotificationHubNamespace(), + "azurerm_notification_hub": resourceArmNotificationHub(), + "azurerm_packet_capture": resourceArmPacketCapture(), + "azurerm_policy_assignment": resourceArmPolicyAssignment(), + "azurerm_policy_definition": resourceArmPolicyDefinition(), + "azurerm_policy_set_definition": resourceArmPolicySetDefinition(), + "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), + "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), + "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), + "azurerm_postgresql_server": resourceArmPostgreSQLServer(), + "azurerm_postgresql_virtual_network_rule": resourceArmPostgreSQLVirtualNetworkRule(), + "azurerm_private_dns_zone": resourceArmPrivateDnsZone(), + "azurerm_private_dns_a_record": resourceArmPrivateDnsARecord(), + "azurerm_private_dns_cname_record": resourceArmPrivateDnsCNameRecord(), + "azurerm_private_dns_zone_virtual_network_link": resourceArmPrivateDnsZoneVirtualNetworkLink(), + "azurerm_proximity_placement_group": resourceArmProximityPlacementGroup(), + "azurerm_public_ip": resourceArmPublicIp(), + "azurerm_public_ip_prefix": resourceArmPublicIpPrefix(), + "azurerm_recovery_network_mapping": resourceArmRecoveryServicesNetworkMapping(), + "azurerm_recovery_replicated_vm": resourceArmRecoveryServicesReplicatedVm(), + "azurerm_recovery_services_fabric": resourceArmRecoveryServicesFabric(), + "azurerm_recovery_services_protected_vm": resourceArmRecoveryServicesProtectedVm(), + "azurerm_recovery_services_protection_container": resourceArmRecoveryServicesProtectionContainer(), + "azurerm_recovery_services_protection_container_mapping": resourceArmRecoveryServicesProtectionContainerMapping(), + "azurerm_recovery_services_protection_policy_vm": resourceArmRecoveryServicesProtectionPolicyVm(), + "azurerm_recovery_services_replication_policy": resourceArmRecoveryServicesReplicationPolicy(), + "azurerm_recovery_services_vault": resourceArmRecoveryServicesVault(), + "azurerm_redis_cache": resourceArmRedisCache(), + "azurerm_redis_firewall_rule": resourceArmRedisFirewallRule(), + "azurerm_relay_namespace": resourceArmRelayNamespace(), + "azurerm_resource_group": resourceArmResourceGroup(), + "azurerm_role_assignment": resourceArmRoleAssignment(), + "azurerm_role_definition": resourceArmRoleDefinition(), + "azurerm_route_table": resourceArmRouteTable(), + "azurerm_route": resourceArmRoute(), + "azurerm_scheduler_job_collection": resourceArmSchedulerJobCollection(), + "azurerm_scheduler_job": resourceArmSchedulerJob(), + "azurerm_search_service": resourceArmSearchService(), + "azurerm_security_center_contact": resourceArmSecurityCenterContact(), + "azurerm_security_center_subscription_pricing": resourceArmSecurityCenterSubscriptionPricing(), + "azurerm_security_center_workspace": resourceArmSecurityCenterWorkspace(), + "azurerm_service_fabric_cluster": resourceArmServiceFabricCluster(), + "azurerm_servicebus_namespace_authorization_rule": resourceArmServiceBusNamespaceAuthorizationRule(), + "azurerm_servicebus_namespace": resourceArmServiceBusNamespace(), + "azurerm_servicebus_queue_authorization_rule": resourceArmServiceBusQueueAuthorizationRule(), + "azurerm_servicebus_queue": resourceArmServiceBusQueue(), + "azurerm_servicebus_subscription_rule": resourceArmServiceBusSubscriptionRule(), + "azurerm_servicebus_subscription": resourceArmServiceBusSubscription(), + "azurerm_servicebus_topic_authorization_rule": resourceArmServiceBusTopicAuthorizationRule(), + "azurerm_servicebus_topic": resourceArmServiceBusTopic(), + "azurerm_shared_image_gallery": resourceArmSharedImageGallery(), + "azurerm_shared_image_version": resourceArmSharedImageVersion(), + "azurerm_shared_image": resourceArmSharedImage(), + "azurerm_signalr_service": resourceArmSignalRService(), + "azurerm_snapshot": resourceArmSnapshot(), + "azurerm_sql_active_directory_administrator": resourceArmSqlAdministrator(), + "azurerm_sql_database": resourceArmSqlDatabase(), + "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), + "azurerm_sql_failover_group": resourceArmSqlFailoverGroup(), + "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), + "azurerm_sql_server": resourceArmSqlServer(), + "azurerm_sql_virtual_network_rule": resourceArmSqlVirtualNetworkRule(), + "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_blob": resourceArmStorageBlob(), + "azurerm_storage_container": resourceArmStorageContainer(), + "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), + "azurerm_storage_management_policy": resourceArmStorageManagementPolicy(), + "azurerm_storage_queue": resourceArmStorageQueue(), + "azurerm_storage_share": resourceArmStorageShare(), + "azurerm_storage_share_directory": resourceArmStorageShareDirectory(), + "azurerm_storage_table": resourceArmStorageTable(), + "azurerm_storage_table_entity": resourceArmStorageTableEntity(), + "azurerm_stream_analytics_job": resourceArmStreamAnalyticsJob(), + "azurerm_stream_analytics_function_javascript_udf": resourceArmStreamAnalyticsFunctionUDF(), + "azurerm_stream_analytics_output_blob": resourceArmStreamAnalyticsOutputBlob(), + "azurerm_stream_analytics_output_mssql": resourceArmStreamAnalyticsOutputSql(), + "azurerm_stream_analytics_output_eventhub": resourceArmStreamAnalyticsOutputEventHub(), + "azurerm_stream_analytics_output_servicebus_queue": resourceArmStreamAnalyticsOutputServiceBusQueue(), + "azurerm_stream_analytics_output_servicebus_topic": resourceArmStreamAnalyticsOutputServiceBusTopic(), + "azurerm_stream_analytics_stream_input_blob": resourceArmStreamAnalyticsStreamInputBlob(), + "azurerm_stream_analytics_stream_input_eventhub": resourceArmStreamAnalyticsStreamInputEventHub(), + "azurerm_stream_analytics_stream_input_iothub": resourceArmStreamAnalyticsStreamInputIoTHub(), + "azurerm_subnet_network_security_group_association": resourceArmSubnetNetworkSecurityGroupAssociation(), + "azurerm_subnet_route_table_association": resourceArmSubnetRouteTableAssociation(), + "azurerm_subnet": resourceArmSubnet(), + "azurerm_template_deployment": resourceArmTemplateDeployment(), + "azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(), + "azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(), + "azurerm_user_assigned_identity": resourceArmUserAssignedIdentity(), + "azurerm_virtual_machine_data_disk_attachment": resourceArmVirtualMachineDataDiskAttachment(), + "azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(), + "azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(), + "azurerm_virtual_machine": resourceArmVirtualMachine(), + "azurerm_virtual_network_gateway_connection": resourceArmVirtualNetworkGatewayConnection(), + "azurerm_virtual_network_gateway": resourceArmVirtualNetworkGateway(), + "azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(), + "azurerm_virtual_network": resourceArmVirtualNetwork(), + "azurerm_virtual_wan": resourceArmVirtualWan(), + "azurerm_web_application_firewall_policy": resourceArmWebApplicationFirewallPolicy(), + } + + // avoids this showing up in test output + var debugLog = func(f string, v ...interface{}) { + if os.Getenv("TF_LOG") == "" { + return + } + + log.Printf(f, v...) + } + for _, service := range supportedServices { + debugLog("[DEBUG] Registering Data Sources for %q..", service.Name()) + for k, v := range service.SupportedDataSources() { + if existing := dataSources[k]; existing != nil { + panic(fmt.Sprintf("An existing Data Source exists for %q", k)) + } + + dataSources[k] = v + } + + debugLog("[DEBUG] Registering Resources for %q..", service.Name()) + for k, v := range service.SupportedResources() { + if existing := resources[k]; existing != nil { + panic(fmt.Sprintf("An existing Resource exists for %q", k)) + } + + resources[k] = v + } + } + p := &schema.Provider{ Schema: map[string]*schema.Schema{ "subscription_id": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""), + Description: "The Subscription ID which should be used.", }, "client_id": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""), + Description: "The Client ID which should be used.", }, "tenant_id": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""), + Description: "The Tenant ID which should be used.", + }, + + "auxiliary_tenant_ids": { + Type: schema.TypeList, + Optional: true, + MaxItems: 3, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "environment": { Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("ARM_ENVIRONMENT", "public"), + Description: "The Cloud Environment which should be used. Possible values are public, usgovernment, german, and china. Defaults to public.", }, // Client Certificate specific fields - "client_certificate_password": { + "client_certificate_path": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PASSWORD", ""), + DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PATH", ""), + Description: "The path to the Client Certificate associated with the Service Principal for use when authenticating as a Service Principal using a Client Certificate.", }, - "client_certificate_path": { + "client_certificate_password": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PATH", ""), + DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PASSWORD", ""), + Description: "The password associated with the Client Certificate. For use when authenticating as a Service Principal using a Client Certificate", }, // Client Secret specific fields @@ -61,6 +539,7 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""), + Description: "The Client Secret which should be used. For use When authenticating as a Service Principal using a Client Secret.", }, // Managed Service Identity specific fields @@ -68,19 +547,29 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeBool, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSI", false), + Description: "Allowed Managed Service Identity be used for Authentication.", }, "msi_endpoint": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""), + Description: "The path to a custom endpoint for Managed Service Identity - in most circumstances this should be detected automatically. ", }, // Managed Tracking GUID for User-agent "partner_id": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc("ARM_PARTNER_ID", ""), ValidateFunc: validate.UUIDOrEmpty, + DefaultFunc: schema.EnvDefaultFunc("ARM_PARTNER_ID", ""), + Description: "A GUID/UUID that is registered with Microsoft to facilitate partner resource usage attribution.", + }, + + "disable_correlation_request_id": { + Type: schema.TypeBool, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("DISABLE_CORRELATION_REQUEST_ID", false), + Description: "This will disable the x-ms-correlation-request-id header.", }, // Advanced feature flags @@ -88,360 +577,19 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeBool, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_SKIP_CREDENTIALS_VALIDATION", false), + Description: "This will cause the AzureRM Provider to skip verifying the credentials being used are valid.", }, "skip_provider_registration": { Type: schema.TypeBool, Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_SKIP_PROVIDER_REGISTRATION", false), + Description: "Should the AzureRM Provider skip registering all of the Resource Providers that it supports, if they're not already registered?", }, }, - DataSourcesMap: map[string]*schema.Resource{ - "azurerm_api_management": dataSourceApiManagementService(), - "azurerm_api_management_api": dataSourceApiManagementApi(), - "azurerm_api_management_group": dataSourceApiManagementGroup(), - "azurerm_api_management_product": dataSourceApiManagementProduct(), - "azurerm_api_management_user": dataSourceArmApiManagementUser(), - "azurerm_app_service_plan": dataSourceAppServicePlan(), - "azurerm_app_service": dataSourceArmAppService(), - "azurerm_application_insights": dataSourceArmApplicationInsights(), - "azurerm_application_security_group": dataSourceArmApplicationSecurityGroup(), - "azurerm_automation_variable_bool": dataSourceArmAutomationVariableBool(), - "azurerm_automation_variable_datetime": dataSourceArmAutomationVariableDateTime(), - "azurerm_automation_variable_int": dataSourceArmAutomationVariableInt(), - "azurerm_automation_variable_string": dataSourceArmAutomationVariableString(), - "azurerm_availability_set": dataSourceArmAvailabilitySet(), - "azurerm_azuread_application": dataSourceArmAzureADApplication(), - "azurerm_azuread_service_principal": dataSourceArmActiveDirectoryServicePrincipal(), - "azurerm_batch_account": dataSourceArmBatchAccount(), - "azurerm_batch_certificate": dataSourceArmBatchCertificate(), - "azurerm_batch_pool": dataSourceArmBatchPool(), - "azurerm_builtin_role_definition": dataSourceArmBuiltInRoleDefinition(), - "azurerm_cdn_profile": dataSourceArmCdnProfile(), - "azurerm_client_config": dataSourceArmClientConfig(), - "azurerm_kubernetes_service_versions": dataSourceArmKubernetesServiceVersions(), - "azurerm_container_registry": dataSourceArmContainerRegistry(), - "azurerm_cosmosdb_account": dataSourceArmCosmosDbAccount(), - "azurerm_data_lake_store": dataSourceArmDataLakeStoreAccount(), - "azurerm_dev_test_lab": dataSourceArmDevTestLab(), - "azurerm_dns_zone": dataSourceArmDnsZone(), - "azurerm_eventhub_namespace": dataSourceEventHubNamespace(), - "azurerm_express_route_circuit": dataSourceArmExpressRouteCircuit(), - "azurerm_firewall": dataSourceArmFirewall(), - "azurerm_image": dataSourceArmImage(), - "azurerm_hdinsight_cluster": dataSourceArmHDInsightSparkCluster(), - "azurerm_key_vault_access_policy": dataSourceArmKeyVaultAccessPolicy(), - "azurerm_key_vault_key": dataSourceArmKeyVaultKey(), - "azurerm_key_vault_secret": dataSourceArmKeyVaultSecret(), - "azurerm_key_vault": dataSourceArmKeyVault(), - "azurerm_kubernetes_cluster": dataSourceArmKubernetesCluster(), - "azurerm_lb": dataSourceArmLoadBalancer(), - "azurerm_lb_backend_address_pool": dataSourceArmLoadBalancerBackendAddressPool(), - "azurerm_log_analytics_workspace": dataSourceLogAnalyticsWorkspace(), - "azurerm_logic_app_workflow": dataSourceArmLogicAppWorkflow(), - "azurerm_managed_disk": dataSourceArmManagedDisk(), - "azurerm_management_group": dataSourceArmManagementGroup(), - "azurerm_monitor_action_group": dataSourceArmMonitorActionGroup(), - "azurerm_monitor_diagnostic_categories": dataSourceArmMonitorDiagnosticCategories(), - "azurerm_monitor_log_profile": dataSourceArmMonitorLogProfile(), - "azurerm_network_interface": dataSourceArmNetworkInterface(), - "azurerm_network_security_group": dataSourceArmNetworkSecurityGroup(), - "azurerm_network_watcher": dataSourceArmNetworkWatcher(), - "azurerm_notification_hub_namespace": dataSourceNotificationHubNamespace(), - "azurerm_notification_hub": dataSourceNotificationHub(), - "azurerm_platform_image": dataSourceArmPlatformImage(), - "azurerm_policy_definition": dataSourceArmPolicyDefinition(), - "azurerm_public_ip": dataSourceArmPublicIP(), - "azurerm_public_ips": dataSourceArmPublicIPs(), - "azurerm_recovery_services_vault": dataSourceArmRecoveryServicesVault(), - "azurerm_recovery_services_protection_policy_vm": dataSourceArmRecoveryServicesProtectionPolicyVm(), - "azurerm_redis_cache": dataSourceArmRedisCache(), - "azurerm_resource_group": dataSourceArmResourceGroup(), - "azurerm_role_definition": dataSourceArmRoleDefinition(), - "azurerm_route_table": dataSourceArmRouteTable(), - "azurerm_scheduler_job_collection": dataSourceArmSchedulerJobCollection(), - "azurerm_servicebus_namespace": dataSourceArmServiceBusNamespace(), - "azurerm_shared_image_gallery": dataSourceArmSharedImageGallery(), - "azurerm_shared_image_version": dataSourceArmSharedImageVersion(), - "azurerm_shared_image": dataSourceArmSharedImage(), - "azurerm_snapshot": dataSourceArmSnapshot(), - "azurerm_sql_server": dataSourceSqlServer(), - "azurerm_stream_analytics_job": dataSourceArmStreamAnalyticsJob(), - "azurerm_storage_account_sas": dataSourceArmStorageAccountSharedAccessSignature(), - "azurerm_storage_account": dataSourceArmStorageAccount(), - "azurerm_subnet": dataSourceArmSubnet(), - "azurerm_subscription": dataSourceArmSubscription(), - "azurerm_subscriptions": dataSourceArmSubscriptions(), - "azurerm_traffic_manager_geographical_location": dataSourceArmTrafficManagerGeographicalLocation(), - "azurerm_user_assigned_identity": dataSourceArmUserAssignedIdentity(), - "azurerm_virtual_machine": dataSourceArmVirtualMachine(), - "azurerm_virtual_network_gateway": dataSourceArmVirtualNetworkGateway(), - "azurerm_virtual_network_gateway_connection": dataSourceArmVirtualNetworkGatewayConnection(), - "azurerm_virtual_network": dataSourceArmVirtualNetwork(), - }, - - ResourcesMap: map[string]*schema.Resource{ - "azurerm_api_management": resourceArmApiManagementService(), - "azurerm_api_management_api": resourceArmApiManagementApi(), - "azurerm_api_management_api_operation": resourceArmApiManagementApiOperation(), - "azurerm_api_management_api_operation_policy": resourceArmApiManagementApiOperationPolicy(), - "azurerm_api_management_api_policy": resourceArmApiManagementApiPolicy(), - "azurerm_api_management_api_schema": resourceArmApiManagementApiSchema(), - "azurerm_api_management_api_version_set": resourceArmApiManagementApiVersionSet(), - "azurerm_api_management_authorization_server": resourceArmApiManagementAuthorizationServer(), - "azurerm_api_management_certificate": resourceArmApiManagementCertificate(), - "azurerm_api_management_group": resourceArmApiManagementGroup(), - "azurerm_api_management_group_user": resourceArmApiManagementGroupUser(), - "azurerm_api_management_logger": resourceArmApiManagementLogger(), - "azurerm_api_management_openid_connect_provider": resourceArmApiManagementOpenIDConnectProvider(), - "azurerm_api_management_product": resourceArmApiManagementProduct(), - "azurerm_api_management_product_api": resourceArmApiManagementProductApi(), - "azurerm_api_management_product_group": resourceArmApiManagementProductGroup(), - "azurerm_api_management_product_policy": resourceArmApiManagementProductPolicy(), - "azurerm_api_management_property": resourceArmApiManagementProperty(), - "azurerm_api_management_subscription": resourceArmApiManagementSubscription(), - "azurerm_api_management_user": resourceArmApiManagementUser(), - "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), - "azurerm_app_service_custom_hostname_binding": resourceArmAppServiceCustomHostnameBinding(), - "azurerm_app_service_plan": resourceArmAppServicePlan(), - "azurerm_app_service_slot": resourceArmAppServiceSlot(), - "azurerm_app_service": resourceArmAppService(), - "azurerm_application_gateway": resourceArmApplicationGateway(), - "azurerm_application_insights_api_key": resourceArmApplicationInsightsAPIKey(), - "azurerm_application_insights": resourceArmApplicationInsights(), - "azurerm_application_insights_web_test": resourceArmApplicationInsightsWebTests(), - "azurerm_application_security_group": resourceArmApplicationSecurityGroup(), - "azurerm_automation_account": resourceArmAutomationAccount(), - "azurerm_automation_credential": resourceArmAutomationCredential(), - "azurerm_automation_dsc_configuration": resourceArmAutomationDscConfiguration(), - "azurerm_automation_dsc_nodeconfiguration": resourceArmAutomationDscNodeConfiguration(), - "azurerm_automation_module": resourceArmAutomationModule(), - "azurerm_automation_runbook": resourceArmAutomationRunbook(), - "azurerm_automation_schedule": resourceArmAutomationSchedule(), - "azurerm_automation_variable_bool": resourceArmAutomationVariableBool(), - "azurerm_automation_variable_datetime": resourceArmAutomationVariableDateTime(), - "azurerm_automation_variable_int": resourceArmAutomationVariableInt(), - "azurerm_automation_variable_string": resourceArmAutomationVariableString(), - "azurerm_autoscale_setting": resourceArmAutoScaleSetting(), - "azurerm_availability_set": resourceArmAvailabilitySet(), - "azurerm_azuread_application": resourceArmActiveDirectoryApplication(), - "azurerm_azuread_service_principal_password": resourceArmActiveDirectoryServicePrincipalPassword(), - "azurerm_azuread_service_principal": resourceArmActiveDirectoryServicePrincipal(), - "azurerm_batch_account": resourceArmBatchAccount(), - "azurerm_batch_certificate": resourceArmBatchCertificate(), - "azurerm_batch_pool": resourceArmBatchPool(), - "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), - "azurerm_cdn_profile": resourceArmCdnProfile(), - "azurerm_cognitive_account": resourceArmCognitiveAccount(), - "azurerm_connection_monitor": resourceArmConnectionMonitor(), - "azurerm_container_group": resourceArmContainerGroup(), - "azurerm_container_registry": resourceArmContainerRegistry(), - "azurerm_container_service": resourceArmContainerService(), - "azurerm_cosmosdb_account": resourceArmCosmosDbAccount(), - "azurerm_cosmosdb_cassandra_keyspace": resourceArmCosmosDbCassandraKeyspace(), - "azurerm_cosmosdb_mongo_collection": resourceArmCosmosDbMongoCollection(), - "azurerm_cosmosdb_mongo_database": resourceArmCosmosDbMongoDatabase(), - "azurerm_cosmosdb_sql_database": resourceArmCosmosDbSQLDatabase(), - "azurerm_cosmosdb_table": resourceArmCosmosDbTable(), - "azurerm_data_factory": resourceArmDataFactory(), - "azurerm_data_factory_dataset_mysql": resourceArmDataFactoryDatasetMySQL(), - "azurerm_data_factory_dataset_postgresql": resourceArmDataFactoryDatasetPostgreSQL(), - "azurerm_data_factory_dataset_sql_server_table": resourceArmDataFactoryDatasetSQLServerTable(), - "azurerm_data_factory_linked_service_data_lake_storage_gen2": resourceArmDataFactoryLinkedServiceDataLakeStorageGen2(), - "azurerm_data_factory_linked_service_mysql": resourceArmDataFactoryLinkedServiceMySQL(), - "azurerm_data_factory_linked_service_postgresql": resourceArmDataFactoryLinkedServicePostgreSQL(), - "azurerm_data_factory_linked_service_sql_server": resourceArmDataFactoryLinkedServiceSQLServer(), - "azurerm_data_factory_pipeline": resourceArmDataFactoryPipeline(), - "azurerm_data_lake_analytics_account": resourceArmDataLakeAnalyticsAccount(), - "azurerm_data_lake_analytics_firewall_rule": resourceArmDataLakeAnalyticsFirewallRule(), - "azurerm_data_lake_store_file": resourceArmDataLakeStoreFile(), - "azurerm_data_lake_store_firewall_rule": resourceArmDataLakeStoreFirewallRule(), - "azurerm_data_lake_store": resourceArmDataLakeStore(), - "azurerm_databricks_workspace": resourceArmDatabricksWorkspace(), - "azurerm_ddos_protection_plan": resourceArmDDoSProtectionPlan(), - "azurerm_dev_test_lab": resourceArmDevTestLab(), - "azurerm_dev_test_linux_virtual_machine": resourceArmDevTestLinuxVirtualMachine(), - "azurerm_dev_test_policy": resourceArmDevTestPolicy(), - "azurerm_dev_test_virtual_network": resourceArmDevTestVirtualNetwork(), - "azurerm_dev_test_windows_virtual_machine": resourceArmDevTestWindowsVirtualMachine(), - "azurerm_devspace_controller": resourceArmDevSpaceController(), - "azurerm_dns_a_record": resourceArmDnsARecord(), - "azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(), - "azurerm_dns_caa_record": resourceArmDnsCaaRecord(), - "azurerm_dns_cname_record": resourceArmDnsCNameRecord(), - "azurerm_dns_mx_record": resourceArmDnsMxRecord(), - "azurerm_dns_ns_record": resourceArmDnsNsRecord(), - "azurerm_dns_ptr_record": resourceArmDnsPtrRecord(), - "azurerm_dns_srv_record": resourceArmDnsSrvRecord(), - "azurerm_dns_txt_record": resourceArmDnsTxtRecord(), - "azurerm_dns_zone": resourceArmDnsZone(), - "azurerm_eventgrid_domain": resourceArmEventGridDomain(), - "azurerm_eventgrid_event_subscription": resourceArmEventGridEventSubscription(), - "azurerm_eventgrid_topic": resourceArmEventGridTopic(), - "azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(), - "azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(), - "azurerm_eventhub_namespace_authorization_rule": resourceArmEventHubNamespaceAuthorizationRule(), - "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), - "azurerm_eventhub": resourceArmEventHub(), - "azurerm_express_route_circuit_authorization": resourceArmExpressRouteCircuitAuthorization(), - "azurerm_express_route_circuit_peering": resourceArmExpressRouteCircuitPeering(), - "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), - "azurerm_firewall_application_rule_collection": resourceArmFirewallApplicationRuleCollection(), - "azurerm_firewall_nat_rule_collection": resourceArmFirewallNatRuleCollection(), - "azurerm_firewall_network_rule_collection": resourceArmFirewallNetworkRuleCollection(), - "azurerm_firewall": resourceArmFirewall(), - "azurerm_function_app": resourceArmFunctionApp(), - "azurerm_hdinsight_hadoop_cluster": resourceArmHDInsightHadoopCluster(), - "azurerm_hdinsight_hbase_cluster": resourceArmHDInsightHBaseCluster(), - "azurerm_hdinsight_interactive_query_cluster": resourceArmHDInsightInteractiveQueryCluster(), - "azurerm_hdinsight_kafka_cluster": resourceArmHDInsightKafkaCluster(), - "azurerm_hdinsight_ml_services_cluster": resourceArmHDInsightMLServicesCluster(), - "azurerm_hdinsight_rserver_cluster": resourceArmHDInsightRServerCluster(), - "azurerm_hdinsight_spark_cluster": resourceArmHDInsightSparkCluster(), - "azurerm_hdinsight_storm_cluster": resourceArmHDInsightStormCluster(), - "azurerm_image": resourceArmImage(), - "azurerm_iot_dps": resourceArmIotDPS(), - "azurerm_iothub_consumer_group": resourceArmIotHubConsumerGroup(), - "azurerm_iothub": resourceArmIotHub(), - "azurerm_iothub_shared_access_policy": resourceArmIotHubSharedAccessPolicy(), - "azurerm_key_vault_access_policy": resourceArmKeyVaultAccessPolicy(), - "azurerm_key_vault_certificate": resourceArmKeyVaultCertificate(), - "azurerm_key_vault_key": resourceArmKeyVaultKey(), - "azurerm_key_vault_secret": resourceArmKeyVaultSecret(), - "azurerm_key_vault": resourceArmKeyVault(), - "azurerm_kubernetes_cluster": resourceArmKubernetesCluster(), - "azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(), - "azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(), - "azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(), - "azurerm_lb_probe": resourceArmLoadBalancerProbe(), - "azurerm_lb_outbound_rule": resourceArmLoadBalancerOutboundRule(), - "azurerm_lb_rule": resourceArmLoadBalancerRule(), - "azurerm_lb": resourceArmLoadBalancer(), - "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), - "azurerm_log_analytics_solution": resourceArmLogAnalyticsSolution(), - "azurerm_log_analytics_linked_service": resourceArmLogAnalyticsLinkedService(), - "azurerm_log_analytics_workspace_linked_service": resourceArmLogAnalyticsWorkspaceLinkedService(), - "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), - "azurerm_logic_app_action_custom": resourceArmLogicAppActionCustom(), - "azurerm_logic_app_action_http": resourceArmLogicAppActionHTTP(), - "azurerm_logic_app_trigger_custom": resourceArmLogicAppTriggerCustom(), - "azurerm_logic_app_trigger_http_request": resourceArmLogicAppTriggerHttpRequest(), - "azurerm_logic_app_trigger_recurrence": resourceArmLogicAppTriggerRecurrence(), - "azurerm_logic_app_workflow": resourceArmLogicAppWorkflow(), - "azurerm_managed_disk": resourceArmManagedDisk(), - "azurerm_management_group": resourceArmManagementGroup(), - "azurerm_management_lock": resourceArmManagementLock(), - "azurerm_mariadb_database": resourceArmMariaDbDatabase(), - "azurerm_mariadb_server": resourceArmMariaDbServer(), - "azurerm_media_services_account": resourceArmMediaServicesAccount(), - "azurerm_metric_alertrule": resourceArmMetricAlertRule(), - "azurerm_monitor_autoscale_setting": resourceArmMonitorAutoScaleSetting(), - "azurerm_monitor_action_group": resourceArmMonitorActionGroup(), - "azurerm_monitor_activity_log_alert": resourceArmMonitorActivityLogAlert(), - "azurerm_monitor_diagnostic_setting": resourceArmMonitorDiagnosticSetting(), - "azurerm_monitor_log_profile": resourceArmMonitorLogProfile(), - "azurerm_monitor_metric_alert": resourceArmMonitorMetricAlert(), - "azurerm_monitor_metric_alertrule": resourceArmMonitorMetricAlertRule(), - "azurerm_mssql_elasticpool": resourceArmMsSqlElasticPool(), - "azurerm_mysql_configuration": resourceArmMySQLConfiguration(), - "azurerm_mysql_database": resourceArmMySqlDatabase(), - "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), - "azurerm_mysql_server": resourceArmMySqlServer(), - "azurerm_mysql_virtual_network_rule": resourceArmMySqlVirtualNetworkRule(), - "azurerm_network_connection_monitor": resourceArmNetworkConnectionMonitor(), - "azurerm_network_ddos_protection_plan": resourceArmNetworkDDoSProtectionPlan(), - "azurerm_network_interface": resourceArmNetworkInterface(), - "azurerm_network_interface_application_gateway_backend_address_pool_association": resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociation(), - "azurerm_network_interface_application_security_group_association": resourceArmNetworkInterfaceApplicationSecurityGroupAssociation(), - "azurerm_network_interface_backend_address_pool_association": resourceArmNetworkInterfaceBackendAddressPoolAssociation(), - "azurerm_network_interface_nat_rule_association": resourceArmNetworkInterfaceNatRuleAssociation(), - "azurerm_network_packet_capture": resourceArmNetworkPacketCapture(), - "azurerm_network_profile": resourceArmNetworkProfile(), - "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), - "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), - "azurerm_network_watcher": resourceArmNetworkWatcher(), - "azurerm_notification_hub_authorization_rule": resourceArmNotificationHubAuthorizationRule(), - "azurerm_notification_hub_namespace": resourceArmNotificationHubNamespace(), - "azurerm_notification_hub": resourceArmNotificationHub(), - "azurerm_packet_capture": resourceArmPacketCapture(), - "azurerm_policy_assignment": resourceArmPolicyAssignment(), - "azurerm_policy_definition": resourceArmPolicyDefinition(), - "azurerm_policy_set_definition": resourceArmPolicySetDefinition(), - "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), - "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), - "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), - "azurerm_postgresql_server": resourceArmPostgreSQLServer(), - "azurerm_postgresql_virtual_network_rule": resourceArmPostgreSQLVirtualNetworkRule(), - "azurerm_public_ip": resourceArmPublicIp(), - "azurerm_public_ip_prefix": resourceArmPublicIpPrefix(), - "azurerm_recovery_services_protected_vm": resourceArmRecoveryServicesProtectedVm(), - "azurerm_recovery_services_protection_policy_vm": resourceArmRecoveryServicesProtectionPolicyVm(), - "azurerm_recovery_services_vault": resourceArmRecoveryServicesVault(), - "azurerm_redis_cache": resourceArmRedisCache(), - "azurerm_redis_firewall_rule": resourceArmRedisFirewallRule(), - "azurerm_relay_namespace": resourceArmRelayNamespace(), - "azurerm_resource_group": resourceArmResourceGroup(), - "azurerm_role_assignment": resourceArmRoleAssignment(), - "azurerm_role_definition": resourceArmRoleDefinition(), - "azurerm_route_table": resourceArmRouteTable(), - "azurerm_route": resourceArmRoute(), - "azurerm_scheduler_job_collection": resourceArmSchedulerJobCollection(), - "azurerm_scheduler_job": resourceArmSchedulerJob(), - "azurerm_search_service": resourceArmSearchService(), - "azurerm_security_center_contact": resourceArmSecurityCenterContact(), - "azurerm_security_center_subscription_pricing": resourceArmSecurityCenterSubscriptionPricing(), - "azurerm_security_center_workspace": resourceArmSecurityCenterWorkspace(), - "azurerm_service_fabric_cluster": resourceArmServiceFabricCluster(), - "azurerm_servicebus_namespace_authorization_rule": resourceArmServiceBusNamespaceAuthorizationRule(), - "azurerm_servicebus_namespace": resourceArmServiceBusNamespace(), - "azurerm_servicebus_queue_authorization_rule": resourceArmServiceBusQueueAuthorizationRule(), - "azurerm_servicebus_queue": resourceArmServiceBusQueue(), - "azurerm_servicebus_subscription_rule": resourceArmServiceBusSubscriptionRule(), - "azurerm_servicebus_subscription": resourceArmServiceBusSubscription(), - "azurerm_servicebus_topic_authorization_rule": resourceArmServiceBusTopicAuthorizationRule(), - "azurerm_servicebus_topic": resourceArmServiceBusTopic(), - "azurerm_shared_image_gallery": resourceArmSharedImageGallery(), - "azurerm_shared_image_version": resourceArmSharedImageVersion(), - "azurerm_shared_image": resourceArmSharedImage(), - "azurerm_signalr_service": resourceArmSignalRService(), - "azurerm_snapshot": resourceArmSnapshot(), - "azurerm_sql_active_directory_administrator": resourceArmSqlAdministrator(), - "azurerm_sql_database": resourceArmSqlDatabase(), - "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), - "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), - "azurerm_sql_server": resourceArmSqlServer(), - "azurerm_sql_virtual_network_rule": resourceArmSqlVirtualNetworkRule(), - "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_blob": resourceArmStorageBlob(), - "azurerm_storage_container": resourceArmStorageContainer(), - "azurerm_storage_queue": resourceArmStorageQueue(), - "azurerm_storage_share": resourceArmStorageShare(), - "azurerm_storage_table": resourceArmStorageTable(), - "azurerm_stream_analytics_job": resourceArmStreamAnalyticsJob(), - "azurerm_stream_analytics_function_javascript_udf": resourceArmStreamAnalyticsFunctionUDF(), - "azurerm_stream_analytics_output_blob": resourceArmStreamAnalyticsOutputBlob(), - "azurerm_stream_analytics_output_eventhub": resourceArmStreamAnalyticsOutputEventHub(), - "azurerm_stream_analytics_output_servicebus_queue": resourceArmStreamAnalyticsOutputServiceBusQueue(), - "azurerm_stream_analytics_stream_input_blob": resourceArmStreamAnalyticsStreamInputBlob(), - "azurerm_stream_analytics_stream_input_eventhub": resourceArmStreamAnalyticsStreamInputEventHub(), - "azurerm_stream_analytics_stream_input_iothub": resourceArmStreamAnalyticsStreamInputIoTHub(), - "azurerm_subnet_network_security_group_association": resourceArmSubnetNetworkSecurityGroupAssociation(), - "azurerm_subnet_route_table_association": resourceArmSubnetRouteTableAssociation(), - "azurerm_subnet": resourceArmSubnet(), - "azurerm_template_deployment": resourceArmTemplateDeployment(), - "azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(), - "azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(), - "azurerm_user_assigned_identity": resourceArmUserAssignedIdentity(), - "azurerm_virtual_machine_data_disk_attachment": resourceArmVirtualMachineDataDiskAttachment(), - "azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(), - "azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(), - "azurerm_virtual_machine": resourceArmVirtualMachine(), - "azurerm_virtual_network_gateway_connection": resourceArmVirtualNetworkGatewayConnection(), - "azurerm_virtual_network_gateway": resourceArmVirtualNetworkGateway(), - "azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(), - "azurerm_virtual_network": resourceArmVirtualNetwork(), - }, + DataSourcesMap: dataSources, + ResourcesMap: resources, } p.ConfigureFunc = providerConfigure(p) @@ -451,11 +599,25 @@ func Provider() terraform.ResourceProvider { func providerConfigure(p *schema.Provider) schema.ConfigureFunc { return func(d *schema.ResourceData) (interface{}, error) { + var auxTenants []string + if v, ok := d.Get("auxiliary_tenant_ids").([]interface{}); ok && len(v) > 0 { + auxTenants = *utils.ExpandStringSlice(v) + } else { + if v := os.Getenv("ARM_AUXILIARY_TENANT_IDS"); v != "" { + auxTenants = strings.Split(v, ";") + } + } + + if len(auxTenants) > 3 { + return nil, fmt.Errorf("The provider onlt supports 3 auxiliary tenant IDs") + } + builder := &authentication.Builder{ SubscriptionID: d.Get("subscription_id").(string), ClientID: d.Get("client_id").(string), ClientSecret: d.Get("client_secret").(string), TenantID: d.Get("tenant_id").(string), + AuxiliaryTenantIDs: auxTenants, Environment: d.Get("environment").(string), MsiEndpoint: d.Get("msi_endpoint").(string), ClientCertPassword: d.Get("client_certificate_password").(string), @@ -466,6 +628,10 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { SupportsClientSecretAuth: true, SupportsManagedServiceIdentity: d.Get("use_msi").(bool), SupportsAzureCliToken: true, + SupportsAuxiliaryTenants: len(auxTenants) > 0, + + // Doc Links + ClientSecretDocsLink: "https://www.terraform.io/docs/providers/azurerm/auth/service_principal_client_secret.html", } config, err := builder.Build() @@ -475,17 +641,29 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { partnerId := d.Get("partner_id").(string) skipProviderRegistration := d.Get("skip_provider_registration").(bool) - client, err := getArmClient(config, skipProviderRegistration, partnerId) + disableCorrelationRequestID := d.Get("disable_correlation_request_id").(bool) + + terraformVersion := p.TerraformVersion + if terraformVersion == "" { + // Terraform 0.12 introduced this field to the protocol + // We can therefore assume that if it's missing it's 0.10 or 0.11 + terraformVersion = "0.11+compatible" + } + client, err := getArmClient(config, skipProviderRegistration, terraformVersion, partnerId, disableCorrelationRequestID) if err != nil { return nil, err } + // TODO: clean this up when ArmClient is removed client.StopContext = p.StopContext() + client.Client.StopContext = p.StopContext() // replaces the context between tests p.MetaReset = func() error { + // TODO: remove the old reference here client.StopContext = p.StopContext() + client.Client.StopContext = p.StopContext() return nil } @@ -494,7 +672,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { // List all the available providers and their registration state to avoid unnecessary // requests. This also lets us check if the provider credentials are correct. ctx := client.StopContext - providerList, err := client.providersClient.List(ctx, nil, "") + providerList, err := client.resource.ProvidersClient.List(ctx, nil, "") if err != nil { return nil, fmt.Errorf("Unable to list provider registration status, it is possible that this is due to invalid "+ "credentials or the service principal does not have permission to use the Resource Manager API, Azure "+ @@ -505,7 +683,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { availableResourceProviders := providerList.Values() requiredResourceProviders := requiredResourceProviders() - err := ensureResourceProvidersAreRegistered(ctx, client.providersClient, availableResourceProviders, requiredResourceProviders) + err := ensureResourceProvidersAreRegistered(ctx, *client.resource.ProvidersClient, availableResourceProviders, requiredResourceProviders) if err != nil { return nil, fmt.Errorf("Error ensuring Resource Providers are registered: %s", err) } @@ -515,58 +693,3 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { return client, nil } } - -// armMutexKV is the instance of MutexKV for ARM resources -var armMutexKV = mutexkv.NewMutexKV() - -// Deprecated: use `suppress.CaseDifference` instead -func ignoreCaseDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { - return suppress.CaseDifference(k, old, new, d) -} - -// ignoreCaseStateFunc is a StateFunc from helper/schema that converts the -// supplied value to lower before saving to state for consistency. -func ignoreCaseStateFunc(val interface{}) string { - return strings.ToLower(val.(string)) -} - -func userDataDiffSuppressFunc(_, old, new string, _ *schema.ResourceData) bool { - return userDataStateFunc(old) == new -} - -func userDataStateFunc(v interface{}) string { - switch s := v.(type) { - case string: - s = base64Encode(s) - hash := sha1.Sum([]byte(s)) - return hex.EncodeToString(hash[:]) - default: - return "" - } -} - -func base64EncodedStateFunc(v interface{}) string { - switch s := v.(type) { - case string: - return base64Encode(s) - default: - return "" - } -} - -// base64Encode encodes data if the input isn't already encoded using -// base64.StdEncoding.EncodeToString. If the input is already base64 encoded, -// return the original input unchanged. -func base64Encode(data string) string { - // Check whether the data is already Base64 encoded; don't double-encode - if isBase64Encoded(data) { - return data - } - // data has not been encoded encode and return - return base64.StdEncoding.EncodeToString([]byte(data)) -} - -func isBase64Encoded(data string) bool { - _, err := base64.StdEncoding.DecodeString(data) - return err == nil -} diff --git a/azurerm/provider_test.go b/azurerm/provider_test.go index 2a5b80be2589..f5d2f3cecbf3 100644 --- a/azurerm/provider_test.go +++ b/azurerm/provider_test.go @@ -43,6 +43,7 @@ func testAccPreCheck(t *testing.T) { "ARM_TENANT_ID", "ARM_TEST_LOCATION", "ARM_TEST_LOCATION_ALT", + "ARM_TEST_LOCATION_ALT2", } for _, variable := range variables { @@ -61,6 +62,10 @@ func testAltLocation() string { return os.Getenv("ARM_TEST_LOCATION_ALT") } +func testAltLocation2() string { + return os.Getenv("ARM_TEST_LOCATION_ALT2") +} + func testArmEnvironmentName() string { envName, exists := os.LookupEnv("ARM_ENVIRONMENT") if !exists { diff --git a/azurerm/required_resource_providers.go b/azurerm/required_resource_providers.go index 5494a99d14c3..04cf72d6c565 100644 --- a/azurerm/required_resource_providers.go +++ b/azurerm/required_resource_providers.go @@ -19,6 +19,7 @@ func requiredResourceProviders() map[string]struct{} { "Microsoft.ApiManagement": {}, "Microsoft.Authorization": {}, "Microsoft.Automation": {}, + "Microsoft.BotService": {}, "Microsoft.Cache": {}, "Microsoft.Cdn": {}, "Microsoft.CognitiveServices": {}, @@ -39,10 +40,13 @@ func requiredResourceProviders() map[string]struct{} { "Microsoft.EventHub": {}, "Microsoft.HDInsight": {}, "Microsoft.KeyVault": {}, + "Microsoft.Kusto": {}, "microsoft.insights": {}, "Microsoft.Logic": {}, "Microsoft.ManagedIdentity": {}, "Microsoft.Management": {}, + "Microsoft.Maps": {}, + "Microsoft.MarketplaceOrdering": {}, "Microsoft.Media": {}, "Microsoft.Network": {}, "Microsoft.NotificationHubs": {}, diff --git a/azurerm/required_resource_providers_test.go b/azurerm/required_resource_providers_test.go index f273243f7ae2..eb7735f2c69f 100644 --- a/azurerm/required_resource_providers_test.go +++ b/azurerm/required_resource_providers_test.go @@ -14,12 +14,12 @@ func TestAccAzureRMEnsureRequiredResourceProvidersAreRegistered(t *testing.T) { } // this test intentionally checks all the RP's are registered - so this is intentional - armClient, err := getArmClient(config, true, "") + armClient, err := getArmClient(config, true, "0.0.0", "", true) if err != nil { t.Fatalf("Error building ARM Client: %+v", err) } - client := armClient.providersClient + client := armClient.resource.ProvidersClient ctx := testAccProvider.StopContext() providerList, err := client.List(ctx, nil, "") if err != nil { @@ -30,7 +30,7 @@ func TestAccAzureRMEnsureRequiredResourceProvidersAreRegistered(t *testing.T) { availableResourceProviders := providerList.Values() requiredResourceProviders := requiredResourceProviders() - err = ensureResourceProvidersAreRegistered(ctx, client, availableResourceProviders, requiredResourceProviders) + err = ensureResourceProvidersAreRegistered(ctx, *client, availableResourceProviders, requiredResourceProviders) if err != nil { t.Fatalf("Error registering Resource Providers: %+v", err) } diff --git a/azurerm/resource_arm_analysis_services_server.go b/azurerm/resource_arm_analysis_services_server.go new file mode 100644 index 000000000000..b46af3a64793 --- /dev/null +++ b/azurerm/resource_arm_analysis_services_server.go @@ -0,0 +1,414 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/analysisservices/mgmt/2017-08-01/analysisservices" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmAnalysisServicesServer() *schema.Resource { + return &schema.Resource{ + Create: resourceArmAnalysisServicesServerCreate, + Read: resourceArmAnalysisServicesServerRead, + Update: resourceArmAnalysisServicesServerUpdate, + Delete: resourceArmAnalysisServicesServerDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAnalysisServicesServerName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "sku": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "D1", + "B1", + "B2", + "S0", + "S1", + "S2", + "S4", + "S8", + "S9", + }, false), + }, + + "admin_users": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "enable_power_bi_service": { + Type: schema.TypeBool, + Optional: true, + }, + + "ipv4_firewall_rule": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "range_start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.IPv4Address, + }, + "range_end": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.IPv4Address, + }, + }, + }, + }, + + "querypool_connection_mode": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateQuerypoolConnectionMode(), + }, + + "backup_blob_container_uri": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "server_full_name": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmAnalysisServicesServerCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).analysisservices.ServerClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Azure ARM Analysis Services Server creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + server, err := client.GetDetails(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(server.Response) { + return fmt.Errorf("Error checking for presence of existing Analysis Services Server %q (Resource Group %q): %s", name, resourceGroup, err) + } + } + + if server.ID != nil && *server.ID != "" { + return tf.ImportAsExistsError("azurerm_analysis_services_server", *server.ID) + } + } + + sku := d.Get("sku").(string) + location := azure.NormalizeLocation(d.Get("location").(string)) + + serverProperties := expandAnalysisServicesServerProperties(d) + + t := d.Get("tags").(map[string]interface{}) + + analysisServicesServer := analysisservices.Server{ + Name: &name, + Location: &location, + Sku: &analysisservices.ResourceSku{Name: &sku}, + ServerProperties: serverProperties, + Tags: tags.Expand(t), + } + + future, err := client.Create(ctx, resourceGroup, name, analysisServicesServer) + if err != nil { + return fmt.Errorf("Error creating Analysis Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Analysis Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + resp, getDetailsErr := client.GetDetails(ctx, resourceGroup, name) + if getDetailsErr != nil { + return fmt.Errorf("Error retrieving Analytics Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read ID for Analytics Services Server %q (Resource Group %q)", name, resourceGroup) + } + + d.SetId(*resp.ID) + + return resourceArmAnalysisServicesServerRead(d, meta) +} + +func resourceArmAnalysisServicesServerRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).analysisservices.ServerClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["servers"] + + server, err := client.GetDetails(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(server.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Analytics Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + + if location := server.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if server.Sku != nil { + d.Set("sku", server.Sku.Name) + } + + if serverProps := server.ServerProperties; serverProps != nil { + if serverProps.AsAdministrators == nil { + d.Set("admin_users", []string{}) + } else { + d.Set("admin_users", serverProps.AsAdministrators.Members) + } + + enablePowerBi, fwRules := flattenAnalysisServicesServerFirewallSettings(serverProps) + d.Set("enable_power_bi_service", enablePowerBi) + if err := d.Set("ipv4_firewall_rule", fwRules); err != nil { + return fmt.Errorf("Error setting `ipv4_firewall_rule`: %s", err) + } + + d.Set("querypool_connection_mode", string(serverProps.QuerypoolConnectionMode)) + + d.Set("server_full_name", serverProps.ServerFullName) + + if containerUri, ok := d.GetOk("backup_blob_container_uri"); ok { + d.Set("backup_blob_container_uri", containerUri) + } + } + + return tags.FlattenAndSet(d, server.Tags) +} + +func resourceArmAnalysisServicesServerUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).analysisservices.ServerClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Azure ARM Analysis Services Server creation.") + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["servers"] + + serverProperties := expandAnalysisServicesServerMutableProperties(d) + sku := d.Get("sku").(string) + t := d.Get("tags").(map[string]interface{}) + + analysisServicesServer := analysisservices.ServerUpdateParameters{ + Sku: &analysisservices.ResourceSku{Name: &sku}, + Tags: tags.Expand(t), + ServerMutableProperties: serverProperties, + } + + future, err := client.Update(ctx, resourceGroup, name, analysisServicesServer) + if err != nil { + return fmt.Errorf("Error creating Analysis Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Analysis Services Server %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + return resourceArmAnalysisServicesServerRead(d, meta) +} + +func resourceArmAnalysisServicesServerDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).analysisservices.ServerClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["servers"] + + future, err := client.Delete(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error deleting Analysis Services Server %q (Resource Group %q): %+v", name, resGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of Analysis Services Server %q (Resource Group %q): %+v", name, resGroup, err) + } + + return nil +} + +func validateAnalysisServicesServerName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^[a-z][0-9a-z]{2,62}$`).Match([]byte(value)) { + errors = append(errors, fmt.Errorf("%q must begin with a letter, be lowercase alphanumeric, and be between 3 and 63 characters in length", k)) + } + + return warnings, errors +} + +func validateQuerypoolConnectionMode() schema.SchemaValidateFunc { + connectionModes := make([]string, len(analysisservices.PossibleConnectionModeValues())) + for i, v := range analysisservices.PossibleConnectionModeValues() { + connectionModes[i] = string(v) + } + + return validation.StringInSlice(connectionModes, true) +} + +func expandAnalysisServicesServerProperties(d *schema.ResourceData) *analysisservices.ServerProperties { + adminUsers := expandAnalysisServicesServerAdminUsers(d) + + serverProperties := analysisservices.ServerProperties{AsAdministrators: adminUsers} + + serverProperties.IPV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) + + if querypoolConnectionMode, ok := d.GetOk("querypool_connection_mode"); ok { + serverProperties.QuerypoolConnectionMode = analysisservices.ConnectionMode(querypoolConnectionMode.(string)) + } + + if containerUri, ok := d.GetOk("backup_blob_container_uri"); ok { + serverProperties.BackupBlobContainerURI = utils.String(containerUri.(string)) + } + + return &serverProperties +} + +func expandAnalysisServicesServerMutableProperties(d *schema.ResourceData) *analysisservices.ServerMutableProperties { + adminUsers := expandAnalysisServicesServerAdminUsers(d) + + serverProperties := analysisservices.ServerMutableProperties{AsAdministrators: adminUsers} + + serverProperties.IPV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) + + serverProperties.QuerypoolConnectionMode = analysisservices.ConnectionMode(d.Get("querypool_connection_mode").(string)) + + if containerUri, ok := d.GetOk("backup_blob_container_uri"); ok { + serverProperties.BackupBlobContainerURI = utils.String(containerUri.(string)) + } + + return &serverProperties +} + +func expandAnalysisServicesServerAdminUsers(d *schema.ResourceData) *analysisservices.ServerAdministrators { + adminUsers := d.Get("admin_users").(*schema.Set) + members := make([]string, 0) + + for _, admin := range adminUsers.List() { + if adm, ok := admin.(string); ok { + members = append(members, adm) + } + } + + return &analysisservices.ServerAdministrators{Members: &members} +} + +func expandAnalysisServicesServerFirewallSettings(d *schema.ResourceData) *analysisservices.IPv4FirewallSettings { + firewallSettings := analysisservices.IPv4FirewallSettings{ + EnablePowerBIService: utils.Bool(d.Get("enable_power_bi_service").(bool)), + } + + firewallRules := d.Get("ipv4_firewall_rule").([]interface{}) + + fwRules := make([]analysisservices.IPv4FirewallRule, len(firewallRules)) + for i, v := range firewallRules { + fwRule := v.(map[string]interface{}) + fwRules[i] = analysisservices.IPv4FirewallRule{ + FirewallRuleName: utils.String(fwRule["name"].(string)), + RangeStart: utils.String(fwRule["range_start"].(string)), + RangeEnd: utils.String(fwRule["range_end"].(string)), + } + } + firewallSettings.FirewallRules = &fwRules + + return &firewallSettings +} + +func flattenAnalysisServicesServerFirewallSettings(serverProperties *analysisservices.ServerProperties) (enablePowerBi *bool, fwRules []interface{}) { + if serverProperties == nil || serverProperties.IPV4FirewallSettings == nil { + return utils.Bool(false), make([]interface{}, 0) + } + + firewallSettings := serverProperties.IPV4FirewallSettings + + enablePowerBi = utils.Bool(false) + if firewallSettings.EnablePowerBIService != nil { + enablePowerBi = firewallSettings.EnablePowerBIService + } + + fwRules = make([]interface{}, 0) + if firewallSettings.FirewallRules != nil { + for _, fwRule := range *firewallSettings.FirewallRules { + output := make(map[string]interface{}) + if fwRule.FirewallRuleName != nil { + output["name"] = *fwRule.FirewallRuleName + } + + if fwRule.RangeStart != nil { + output["range_start"] = *fwRule.RangeStart + } + + if fwRule.RangeEnd != nil { + output["range_end"] = *fwRule.RangeEnd + } + + fwRules = append(fwRules, output) + } + } + + return enablePowerBi, fwRules +} diff --git a/azurerm/resource_arm_analysis_services_server_test.go b/azurerm/resource_arm_analysis_services_server_test.go new file mode 100644 index 000000000000..06de35fdb4e1 --- /dev/null +++ b/azurerm/resource_arm_analysis_services_server_test.go @@ -0,0 +1,523 @@ +package azurerm + +import ( + "fmt" + "os" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMAnalysisServicesServer_basic(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAnalysisServicesServer_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMAnalysisServicesServer_withTags(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMAnalysisServicesServer_withTags(ri, testLocation()) + postConfig := testAccAzureRMAnalysisServicesServer_withTagsUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test1"), + resource.TestCheckResourceAttr(resourceName, "tags.ENV", "prod"), + ), + }, + }, + }) +} + +func TestAccAzureRMAnalysisServicesServer_querypoolConnectionMode(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMAnalysisServicesServer_querypoolConnectionMode(ri, testLocation(), "All") + postConfig := testAccAzureRMAnalysisServicesServer_querypoolConnectionMode(ri, testLocation(), "ReadOnly") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "querypool_connection_mode", "All"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "querypool_connection_mode", "ReadOnly"), + ), + }, + }, + }) +} + +func TestAccAzureRMAnalysisServicesServer_firewallSettings(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + + config1 := testAccAzureRMAnalysisServicesServer_firewallSettings1(ri, testLocation(), true) + + config2 := testAccAzureRMAnalysisServicesServer_firewallSettings2(ri, testLocation(), false) + + config3 := testAccAzureRMAnalysisServicesServer_firewallSettings3(ri, testLocation(), true) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: config1, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_power_bi_service", "true"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.#", "0"), + ), + }, + { + Config: config2, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_power_bi_service", "false"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.name", "test1"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.range_start", "92.123.234.11"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.range_end", "92.123.234.12"), + ), + }, + { + Config: config3, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_power_bi_service", "true"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.#", "2"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.name", "test1"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.range_start", "92.123.234.11"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.0.range_end", "92.123.234.13"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.1.name", "test2"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.1.range_start", "226.202.187.57"), + resource.TestCheckResourceAttr(resourceName, "ipv4_firewall_rule.1.range_end", "226.208.192.47"), + ), + }, + }, + }) +} + +// ARM_ACC_EMAIL1 and ARM_ACC_EMAIL2 must be set and existing emails in the tenant's AD to work properly +func TestAccAzureRMAnalysisServicesServer_adminUsers(t *testing.T) { + const ArmAccAdminEmail1 = "ARM_ACCTEST_ADMIN_EMAIL1" + const ArmAccAdminEmail2 = "ARM_ACCTEST_ADMIN_EMAIL2" + if os.Getenv(ArmAccAdminEmail1) == "" || os.Getenv(ArmAccAdminEmail2) == "" { + t.Skip(fmt.Sprintf("Acceptance test skipped unless env '%s' and '%s' set", ArmAccAdminEmail1, ArmAccAdminEmail2)) + return + } + + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + email1 := os.Getenv(ArmAccAdminEmail1) + email2 := os.Getenv(ArmAccAdminEmail2) + preAdminUsers := []string{email1} + postAdminUsers := []string{email1, email2} + preConfig := testAccAzureRMAnalysisServicesServer_adminUsers(ri, testLocation(), preAdminUsers) + postConfig := testAccAzureRMAnalysisServicesServer_adminUsers(ri, testLocation(), postAdminUsers) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMAnalysisServicesServer_serverFullName(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAnalysisServicesServer_serverFullName(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "server_full_name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMAnalysisServicesServer_backupBlobContainerUri(t *testing.T) { + resourceName := "azurerm_analysis_services_server.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAnalysisServicesServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAnalysisServicesServer_backupBlobContainerUri(ri, testLocation(), rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAnalysisServicesServerExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "backup_blob_container_uri"), + ), + }, + }, + }) +} + +func testAccAzureRMAnalysisServicesServer_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" +} +`, rInt, location, rInt) +} + +func testAccAzureRMAnalysisServicesServer_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + + tags = { + label = "test" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMAnalysisServicesServer_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + + tags = { + label = "test1" + ENV = "prod" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMAnalysisServicesServer_querypoolConnectionMode(rInt int, location, connectionMode string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + querypool_connection_mode = "%s" +} +`, rInt, location, rInt, connectionMode) +} + +func testAccAzureRMAnalysisServicesServer_firewallSettings1(rInt int, location string, enablePowerBIService bool) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + enable_power_bi_service = %t +} +`, rInt, location, rInt, enablePowerBIService) +} + +func testAccAzureRMAnalysisServicesServer_firewallSettings2(rInt int, location string, enablePowerBIService bool) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + enable_power_bi_service = %t + + ipv4_firewall_rule { + name = "test1" + range_start = "92.123.234.11" + range_end = "92.123.234.12" + } +} +`, rInt, location, rInt, enablePowerBIService) +} + +func testAccAzureRMAnalysisServicesServer_firewallSettings3(rInt int, location string, enablePowerBIService bool) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + enable_power_bi_service = %t + + ipv4_firewall_rule { + name = "test1" + range_start = "92.123.234.11" + range_end = "92.123.234.13" + } + + ipv4_firewall_rule { + name = "test2" + range_start = "226.202.187.57" + range_end = "226.208.192.47" + } +} +`, rInt, location, rInt, enablePowerBIService) +} + +func testAccAzureRMAnalysisServicesServer_adminUsers(rInt int, location string, adminUsers []string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + admin_users = ["%s"] +} +`, rInt, location, rInt, strings.Join(adminUsers, "\", \"")) +} + +func testAccAzureRMAnalysisServicesServer_serverFullName(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" +} +`, rInt, location, rInt) +} + +func testAccAzureRMAnalysisServicesServer_backupBlobContainerUri(rInt int, location string, rString string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestass%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_kind = "BlobStorage" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "assbackup" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +data "azurerm_storage_account_blob_container_sas" "test" { + connection_string = "${azurerm_storage_account.test.primary_connection_string}" + container_name = "${azurerm_storage_container.test.name}" + https_only = true + + start = "2018-06-01" + expiry = "2048-06-01" + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + +resource "azurerm_analysis_services_server" "test" { + name = "acctestass%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "B1" + + backup_blob_container_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}${data.azurerm_storage_account_blob_container_sas.test.sas}" +} +`, rInt, location, rString, rInt) +} + +func testCheckAzureRMAnalysisServicesServerDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).analysisservices.ServerClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_analysis_services_server" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.GetDetails(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return nil + } + + return nil +} + +func testCheckAzureRMAnalysisServicesServerExists(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) + } + + analysisServicesServerName := 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 Analysis Services Server: %s", analysisServicesServerName) + } + + client := testAccProvider.Meta().(*ArmClient).analysisservices.ServerClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.GetDetails(ctx, resourceGroup, analysisServicesServerName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Analysis Services Server %q (resource group: %q) does not exist", analysisServicesServerName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on analysisServicesServerClient: %+v", err) + } + + return nil + } +} diff --git a/azurerm/resource_arm_api_management.go b/azurerm/resource_arm_api_management.go index 16a8d09df96e..fc8c9f9c2675 100644 --- a/azurerm/resource_arm_api_management.go +++ b/azurerm/resource_arm_api_management.go @@ -13,6 +13,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -62,10 +64,14 @@ func resourceArmApiManagementService() *schema.Resource { ValidateFunc: validate.ApiManagementServicePublisherEmail, }, + // TODO: Remove in 2.0 "sku": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "This property has been deprecated in favour of the 'sku_name' property and will be removed in version 2.0 of the provider", + ConflictsWith: []string{"sku_name"}, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -78,15 +84,29 @@ func resourceArmApiManagementService() *schema.Resource { string(apimanagement.SkuTypePremium), }, false), }, + "capacity": { Type: schema.TypeInt, - Required: true, + Optional: true, ValidateFunc: validation.IntAtLeast(0), }, }, }, }, + "sku_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, // TODO: Remove computed in 2.0 + ConflictsWith: []string{"sku"}, + ValidateFunc: azure.MinCapacitySkuNameInSlice([]string{ + string(apimanagement.SkuTypeDeveloper), + string(apimanagement.SkuTypeBasic), + string(apimanagement.SkuTypeStandard), + string(apimanagement.SkuTypePremium), + }, 1, false), + }, + "identity": { Type: schema.TypeList, Optional: true, @@ -121,7 +141,6 @@ func resourceArmApiManagementService() *schema.Resource { "additional_location": { Type: schema.TypeList, Optional: true, - MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "location": azure.SchemaLocation(), @@ -175,7 +194,7 @@ func resourceArmApiManagementService() *schema.Resource { "security": { Type: schema.TypeList, Optional: true, - Computed: true, // todo remove in 2.0 ? + Computed: true, // TODO: Remove in 2.0 ? MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -197,15 +216,15 @@ func resourceArmApiManagementService() *schema.Resource { "disable_triple_des_chipers": { Type: schema.TypeBool, Optional: true, - Computed: true, // todo remove in 2.0 + Computed: true, // TODO: Remove in 2.0 Deprecated: "This field has been deprecated in favour of the `disable_triple_des_ciphers` property to correct the spelling. it will be removed in version 2.0 of the provider", ConflictsWith: []string{"security.0.disable_triple_des_ciphers"}, }, "disable_triple_des_ciphers": { Type: schema.TypeBool, Optional: true, - // Default: false, // todo remove in 2.0 - Computed: true, // todo remove in 2.0 + // Default: false, // TODO: Remove in 2.0 + Computed: true, // TODO: Remove in 2.0 ConflictsWith: []string{"security.0.disable_triple_des_chipers"}, }, "disable_frontend_ssl30": { @@ -343,7 +362,7 @@ func resourceArmApiManagementService() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "gateway_url": { Type: schema.TypeString, @@ -374,15 +393,23 @@ func resourceArmApiManagementService() *schema.Resource { } func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ServiceClient + client := meta.(*ArmClient).apiManagement.ServiceClient ctx := meta.(*ArmClient).StopContext + // TODO: Remove in 2.0 + sku := expandAzureRmApiManagementSku(d) + if sku == nil { + if sku = expandAzureRmApiManagementSkuName(d); sku == nil { + return fmt.Errorf("either 'sku_name' or 'sku' must be defined in the configuration file") + } + } + log.Printf("[INFO] preparing arguments for API Management Service creation.") name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -396,9 +423,7 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) - - sku := expandAzureRmApiManagementSku(d) + t := d.Get("tags").(map[string]interface{}) publisherName := d.Get("publisher_name").(string) publisherEmail := d.Get("publisher_email").(string) @@ -417,7 +442,7 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in Certificates: certificates, HostnameConfigurations: hostnameConfigurations, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), Sku: sku, } @@ -455,19 +480,19 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in signInSettingsRaw := d.Get("sign_in").([]interface{}) signInSettings := expandApiManagementSignInSettings(signInSettingsRaw) - signInClient := meta.(*ArmClient).apimgmt.SignInClient + signInClient := meta.(*ArmClient).apiManagement.SignInClient if _, err := signInClient.CreateOrUpdate(ctx, resourceGroup, name, signInSettings); err != nil { return fmt.Errorf("Error setting Sign In settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } signUpSettingsRaw := d.Get("sign_up").([]interface{}) signUpSettings := expandApiManagementSignUpSettings(signUpSettingsRaw) - signUpClient := meta.(*ArmClient).apimgmt.SignUpClient + signUpClient := meta.(*ArmClient).apiManagement.SignUpClient if _, err := signUpClient.CreateOrUpdate(ctx, resourceGroup, name, signUpSettings); err != nil { return fmt.Errorf("Error setting Sign Up settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } - policyClient := meta.(*ArmClient).apimgmt.PolicyClient + policyClient := meta.(*ArmClient).apiManagement.PolicyClient policiesRaw := d.Get("policy").([]interface{}) policy, err := expandApiManagementPolicies(policiesRaw) if err != nil { @@ -494,10 +519,10 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in } func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ServiceClient + client := meta.(*ArmClient).apiManagement.ServiceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -516,19 +541,19 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error making Read request on API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } - signInClient := meta.(*ArmClient).apimgmt.SignInClient + signInClient := meta.(*ArmClient).apiManagement.SignInClient signInSettings, err := signInClient.Get(ctx, resourceGroup, name) if err != nil { return fmt.Errorf("Error retrieving Sign In Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } - signUpClient := meta.(*ArmClient).apimgmt.SignUpClient + signUpClient := meta.(*ArmClient).apiManagement.SignUpClient signUpSettings, err := signUpClient.Get(ctx, resourceGroup, name) if err != nil { return fmt.Errorf("Error retrieving Sign Up Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } - policyClient := meta.(*ArmClient).apimgmt.PolicyClient + policyClient := meta.(*ArmClient).apiManagement.PolicyClient policy, err := policyClient.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(policy.Response) { @@ -573,8 +598,14 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ } } - if err := d.Set("sku", flattenApiManagementServiceSku(resp.Sku)); err != nil { - return fmt.Errorf("Error setting `sku`: %+v", err) + if sku := resp.Sku; sku != nil { + // TODO: Remove in 2.0 + if err := d.Set("sku", flattenApiManagementServiceSku(resp.Sku)); err != nil { + return fmt.Errorf("Error setting `sku`: %+v", err) + } + if err := d.Set("sku_name", flattenApiManagementServiceSkuName(resp.Sku)); err != nil { + return fmt.Errorf("Error setting `sku_name`: %+v", err) + } } if err := d.Set("sign_in", flattenApiManagementSignInSettings(signInSettings)); err != nil { @@ -585,20 +616,18 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error setting `sign_up`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - if err := d.Set("policy", flattenApiManagementPolicies(d, policy)); err != nil { return fmt.Errorf("Error setting `policy`: %+v", err) } - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmApiManagementServiceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ServiceClient + client := meta.(*ArmClient).apiManagement.ServiceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -860,22 +889,61 @@ func flattenAzureRmApiManagementMachineIdentity(identity *apimanagement.ServiceI return []interface{}{result} } +// TODO: Remove in 2.0 timeframe func expandAzureRmApiManagementSku(d *schema.ResourceData) *apimanagement.ServiceSkuProperties { + var name string + var capacity int32 + vs := d.Get("sku").([]interface{}) + + if len(vs) == 0 { + return nil + } + // guaranteed by MinItems in the schema v := vs[0].(map[string]interface{}) - name := apimanagement.SkuType(v["name"].(string)) - capacity := int32(v["capacity"].(int)) + name = v["name"].(string) + capacity = int32(v["capacity"].(int)) sku := &apimanagement.ServiceSkuProperties{ - Name: name, + Name: apimanagement.SkuType(name), Capacity: utils.Int32(capacity), } return sku } +func expandAzureRmApiManagementSkuName(d *schema.ResourceData) *apimanagement.ServiceSkuProperties { + vs := d.Get("sku_name").(string) + + if len(vs) == 0 { + return nil + } + + name, capacity, err := azure.SplitSku(vs) + if err != nil { + return nil + } + + sku := &apimanagement.ServiceSkuProperties{ + Name: apimanagement.SkuType(name), + Capacity: utils.Int32(capacity), + } + + return sku +} + +func flattenApiManagementServiceSkuName(input *apimanagement.ServiceSkuProperties) string { + if input == nil { + return "" + } + + sku := fmt.Sprintf("%s_%d", string(input.Name), *input.Capacity) + + return sku +} + func flattenApiManagementServiceSku(input *apimanagement.ServiceSkuProperties) []interface{} { if input == nil { return []interface{}{} @@ -940,7 +1008,7 @@ func flattenApiManagementCustomProperties(input map[string]*string) []interface{ output["disable_frontend_ssl30"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolSsl3) output["disable_frontend_tls10"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolTls10) output["disable_frontend_tls11"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolTls11) - output["disable_triple_des_chipers"] = parseApiManagementNilableDictionary(input, apimTripleDesCiphers) // todo remove in 2.0 + output["disable_triple_des_chipers"] = parseApiManagementNilableDictionary(input, apimTripleDesCiphers) // TODO: Remove in 2.0 output["disable_triple_des_ciphers"] = parseApiManagementNilableDictionary(input, apimTripleDesCiphers) return []interface{}{output} diff --git a/azurerm/resource_arm_api_management_api.go b/azurerm/resource_arm_api_management_api.go index 774345f6e2b7..f17e0d779f58 100644 --- a/azurerm/resource_arm_api_management_api.go +++ b/azurerm/resource_arm_api_management_api.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -180,7 +181,7 @@ func resourceArmApiManagementApi() *schema.Resource { } func resourceArmApiManagementApiCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiClient + client := meta.(*ArmClient).apiManagement.ApiClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -190,7 +191,7 @@ func resourceArmApiManagementApiCreateUpdate(d *schema.ResourceData, meta interf path := d.Get("path").(string) apiId := fmt.Sprintf("%s;rev=%s", name, revision) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, apiId) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -285,9 +286,9 @@ func resourceArmApiManagementApiCreateUpdate(d *schema.ResourceData, meta interf func resourceArmApiManagementApiRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).apimgmt.ApiClient + client := meta.(*ArmClient).apiManagement.ApiClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -343,10 +344,10 @@ func resourceArmApiManagementApiRead(d *schema.ResourceData, meta interface{}) e } func resourceArmApiManagementApiDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiClient + client := meta.(*ArmClient).apiManagement.ApiClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_api_operation.go b/azurerm/resource_arm_api_management_api_operation.go index c9368be11ea1..9c32f2a15d3b 100644 --- a/azurerm/resource_arm_api_management_api_operation.go +++ b/azurerm/resource_arm_api_management_api_operation.go @@ -98,7 +98,7 @@ func resourceArmApiManagementApiOperation() *schema.Resource { } func resourceArmApiManagementApiOperationCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationsClient + client := meta.(*ArmClient).apiManagement.ApiOperationsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -153,10 +153,10 @@ func resourceArmApiManagementApiOperationCreateUpdate(d *schema.ResourceData, me } func resourceArmApiManagementApiOperationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationsClient + client := meta.(*ArmClient).apiManagement.ApiOperationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -208,10 +208,10 @@ func resourceArmApiManagementApiOperationRead(d *schema.ResourceData, meta inter } func resourceArmApiManagementApiOperationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationsClient + client := meta.(*ArmClient).apiManagement.ApiOperationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_api_operation_policy.go b/azurerm/resource_arm_api_management_api_operation_policy.go index 223032b4a553..a1c1e896bbb8 100644 --- a/azurerm/resource_arm_api_management_api_operation_policy.go +++ b/azurerm/resource_arm_api_management_api_operation_policy.go @@ -9,6 +9,7 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -49,7 +50,7 @@ func resourceArmApiManagementApiOperationPolicy() *schema.Resource { } func resourceArmApiManagementAPIOperationPolicyCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationPoliciesClient + client := meta.(*ArmClient).apiManagement.ApiOperationPoliciesClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -57,7 +58,7 @@ func resourceArmApiManagementAPIOperationPolicyCreateUpdate(d *schema.ResourceDa apiName := d.Get("api_name").(string) operationID := d.Get("operation_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, apiName, operationID) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -110,10 +111,10 @@ func resourceArmApiManagementAPIOperationPolicyCreateUpdate(d *schema.ResourceDa } func resourceArmApiManagementAPIOperationPolicyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationPoliciesClient + client := meta.(*ArmClient).apiManagement.ApiOperationPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -148,10 +149,10 @@ func resourceArmApiManagementAPIOperationPolicyRead(d *schema.ResourceData, meta } func resourceArmApiManagementAPIOperationPolicyDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiOperationPoliciesClient + client := meta.(*ArmClient).apiManagement.ApiOperationPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_api_operation_policy_test.go b/azurerm/resource_arm_api_management_api_operation_policy_test.go index 980fb2acde31..8957b3627e98 100644 --- a/azurerm/resource_arm_api_management_api_operation_policy_test.go +++ b/azurerm/resource_arm_api_management_api_operation_policy_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMApiManagementAPIOperationPolicy_basic(t *testing.T) { } func TestAccAzureRMApiManagementAPIOperationPolicy_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -110,7 +111,7 @@ func testCheckAzureRMApiManagementAPIOperationPolicyExists(resourceName string) resourceGroup := rs.Primary.Attributes["resource_group_name"] operationID := rs.Primary.Attributes["operation_id"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiOperationPoliciesClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiOperationPoliciesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, serviceName, apiName, operationID) if err != nil { @@ -126,7 +127,7 @@ func testCheckAzureRMApiManagementAPIOperationPolicyExists(resourceName string) } func testCheckAzureRMApiManagementAPIOperationPolicyDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiOperationPoliciesClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiOperationPoliciesClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_api_operation_policy" { @@ -161,11 +162,11 @@ func testAccAzureRMApiManagementAPIOperationPolicy_basic(rInt int, location stri %s resource "azurerm_api_management_api_operation_policy" "test" { - api_name = "${azurerm_api_management_api.test.name}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - operation_id = "${azurerm_api_management_api_operation.test.operation_id}" - xml_link = "https://gist.githubusercontent.com/tombuildsstuff/4f58581599d2c9f64b236f505a361a67/raw/0d29dcb0167af1e5afe4bd52a6d7f69ba1e05e1f/example.xml" + api_name = "${azurerm_api_management_api.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + operation_id = "${azurerm_api_management_api_operation.test.operation_id}" + xml_link = "https://gist.githubusercontent.com/tombuildsstuff/4f58581599d2c9f64b236f505a361a67/raw/0d29dcb0167af1e5afe4bd52a6d7f69ba1e05e1f/example.xml" } `, template) } @@ -176,11 +177,11 @@ func testAccAzureRMApiManagementAPIOperationPolicy_requiresImport(rInt int, loca %s resource "azurerm_api_management_api_operation_policy" "import" { - api_name = "${azurerm_api_management_api_policy.test.api_name}" - api_management_name = "${azurerm_api_management_api_policy.test.api_management_name}" - resource_group_name = "${azurerm_api_management_api_policy.test.resource_group_name}" - operation_id = "${azurerm_api_management_api_operation.test.operation_id}" - xml_link = "${azurerm_api_management_api_policy.test.xml_link}" + api_name = "${azurerm_api_management_api_policy.test.api_name}" + api_management_name = "${azurerm_api_management_api_policy.test.api_management_name}" + resource_group_name = "${azurerm_api_management_api_policy.test.resource_group_name}" + operation_id = "${azurerm_api_management_api_operation.test.operation_id}" + xml_link = "${azurerm_api_management_api_policy.test.xml_link}" } `, template) } @@ -191,11 +192,12 @@ func testAccAzureRMApiManagementAPIOperationPolicy_updated(rInt int, location st %s resource "azurerm_api_management_api_operation_policy" "test" { - api_name = "${azurerm_api_management_api.test.name}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - operation_id = "${azurerm_api_management_api_operation.test.operation_id}" - xml_content = < diff --git a/azurerm/resource_arm_api_management_api_operation_test.go b/azurerm/resource_arm_api_management_api_operation_test.go index 15bd9aa4e040..b7d3483a8437 100644 --- a/azurerm/resource_arm_api_management_api_operation_test.go +++ b/azurerm/resource_arm_api_management_api_operation_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMApiManagementApiOperation_basic(t *testing.T) { } func TestAccAzureRMApiManagementApiOperation_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -189,7 +190,7 @@ func TestAccAzureRMApiManagementApiOperation_representations(t *testing.T) { } func testCheckAzureRMApiManagementApiOperationDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiOperationsClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiOperationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -230,7 +231,7 @@ func testCheckAzureRMApiManagementApiOperationExists(name string) resource.TestC serviceName := rs.Primary.Attributes["api_management_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiOperationsClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiOperationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, serviceName, apiName, operationId) @@ -311,17 +312,16 @@ resource "azurerm_api_management_api_operation" "test" { method = "DELETE" url_template = "/user1" description = "This can only be done by the logged in user." - + request { description = "Created user object" representation { content_type = "application/json" - type_name = "User" + type_name = "User" } } } - `, template) } @@ -339,17 +339,16 @@ resource "azurerm_api_management_api_operation" "test" { method = "DELETE" url_template = "/user1" description = "This can only be done by the logged in user." - + request { description = "Created user object" representation { content_type = "application/json" - type_name = "User" + type_name = "User" } } } - `, template) } @@ -367,10 +366,10 @@ resource "azurerm_api_management_api_operation" "test" { method = "DELETE" url_template = "/user1" description = "This can only be done by the logged in user." - + request { description = "Created user object" - + header { name = "X-Test-Operation" required = true @@ -379,14 +378,14 @@ resource "azurerm_api_management_api_operation" "test" { representation { content_type = "application/json" - type_name = "User" + type_name = "User" } } response { status_code = 200 description = "successful operation" - + header { name = "X-Test-Operation" required = true @@ -395,7 +394,8 @@ resource "azurerm_api_management_api_operation" "test" { representation { content_type = "application/xml" - sample = < @@ -408,7 +408,6 @@ SAMPLE } } } - `, template) } @@ -426,13 +425,13 @@ resource "azurerm_api_management_api_operation" "test" { method = "DELETE" url_template = "/user1" description = "This can only be done by the logged in user." - + request { description = "Created user object" representation { content_type = "application/json" - type_name = "User" + type_name = "User" } } @@ -442,7 +441,8 @@ resource "azurerm_api_management_api_operation" "test" { representation { content_type = "application/xml" - sample = < @@ -455,7 +455,6 @@ SAMPLE } } } - `, template) } @@ -473,13 +472,13 @@ resource "azurerm_api_management_api_operation" "test" { method = "DELETE" url_template = "/user1" description = "This can only be done by the logged in user." - + request { description = "Created user object" representation { content_type = "application/json" - type_name = "User" + type_name = "User" } } @@ -489,7 +488,8 @@ resource "azurerm_api_management_api_operation" "test" { representation { content_type = "application/xml" - sample = < @@ -503,7 +503,8 @@ SAMPLE representation { content_type = "application/json" - sample = < diff --git a/azurerm/resource_arm_api_management_api_schema.go b/azurerm/resource_arm_api_management_api_schema.go index 40441cecfae6..68255089568f 100644 --- a/azurerm/resource_arm_api_management_api_schema.go +++ b/azurerm/resource_arm_api_management_api_schema.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -47,7 +48,7 @@ func resourceArmApiManagementApiSchema() *schema.Resource { } func resourceArmApiManagementApiSchemaCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiSchemasClient + client := meta.(*ArmClient).apiManagement.ApiSchemasClient ctx := meta.(*ArmClient).StopContext schemaID := d.Get("schema_id").(string) @@ -55,7 +56,7 @@ func resourceArmApiManagementApiSchemaCreateUpdate(d *schema.ResourceData, meta serviceName := d.Get("api_management_name").(string) apiName := d.Get("api_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, apiName, schemaID) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -96,10 +97,10 @@ func resourceArmApiManagementApiSchemaCreateUpdate(d *schema.ResourceData, meta } func resourceArmApiManagementApiSchemaRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiSchemasClient + client := meta.(*ArmClient).apiManagement.ApiSchemasClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -135,10 +136,10 @@ func resourceArmApiManagementApiSchemaRead(d *schema.ResourceData, meta interfac } func resourceArmApiManagementApiSchemaDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiSchemasClient + client := meta.(*ArmClient).apiManagement.ApiSchemasClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_api_schema_test.go b/azurerm/resource_arm_api_management_api_schema_test.go index 2cfa4213de00..559715622fd5 100644 --- a/azurerm/resource_arm_api_management_api_schema_test.go +++ b/azurerm/resource_arm_api_management_api_schema_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMApiManagementApiSchema_basic(t *testing.T) { } func TestAccAzureRMApiManagementApiSchema_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -65,7 +66,7 @@ func TestAccAzureRMApiManagementApiSchema_requiresImport(t *testing.T) { } func testCheckAzureRMApiManagementApiSchemaDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiSchemasClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiSchemasClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -106,7 +107,7 @@ func testCheckAzureRMApiManagementApiSchemaExists(name string) resource.TestChec serviceName := rs.Primary.Attributes["api_management_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiSchemasClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiSchemasClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, serviceName, apiName, schemaID) diff --git a/azurerm/resource_arm_api_management_api_test.go b/azurerm/resource_arm_api_management_api_test.go index 46cf826f94e9..8ceab983a6c5 100644 --- a/azurerm/resource_arm_api_management_api_test.go +++ b/azurerm/resource_arm_api_management_api_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,6 +39,35 @@ func TestAccAzureRMApiManagementApi_basic(t *testing.T) { }) } +// Remove in 2.0 +func TestAccAzureRMApiManagementApi_basicClassic(t *testing.T) { + resourceName := "azurerm_api_management_api.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementApiDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementApi_basicClassic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementApiExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "soap_pass_through", "false"), + resource.TestCheckResourceAttr(resourceName, "is_current", "true"), + resource.TestCheckResourceAttr(resourceName, "is_online", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { resourceName := "azurerm_api_management_api.test" ri := tf.AccRandTimeInt() @@ -65,7 +95,7 @@ func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { } func TestAccAzureRMApiManagementApi_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -246,7 +276,7 @@ func TestAccAzureRMApiManagementApi_complete(t *testing.T) { } func testCheckAzureRMApiManagementApiDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_api" { @@ -288,7 +318,7 @@ func testCheckAzureRMApiManagementApiExists(name string) resource.TestCheckFunc resourceGroup := rs.Primary.Attributes["resource_group_name"] revision := rs.Primary.Attributes["revision"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ApiClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ApiClient ctx := testAccProvider.Meta().(*ArmClient).StopContext apiId := fmt.Sprintf("%s;rev=%s", name, revision) @@ -322,6 +352,24 @@ resource "azurerm_api_management_api" "test" { `, template, rInt) } +// Remove in 2.0 +func testAccAzureRMApiManagementApi_basicClassic(rInt int, location string) string { + template := testAccAzureRMApiManagementApi_templateClassic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_api" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + display_name = "api1" + path = "api1" + protocols = ["https"] + revision = "1" +} +`, template, rInt) +} + func testAccAzureRMApiManagementApi_wordRevision(rInt int, location string) string { template := testAccAzureRMApiManagementApi_template(rInt, location) return fmt.Sprintf(` @@ -454,6 +502,26 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku_name = "Developer_1" +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMApiManagementApi_templateClassic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + resource "azurerm_api_management" "test" { name = "acctestAM-%d" location = "${azurerm_resource_group.test.location}" diff --git a/azurerm/resource_arm_api_management_api_version_set.go b/azurerm/resource_arm_api_management_api_version_set.go index b7c0cdc6160c..fd87e0fcce5f 100644 --- a/azurerm/resource_arm_api_management_api_version_set.go +++ b/azurerm/resource_arm_api_management_api_version_set.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -71,14 +72,14 @@ func resourceArmApiManagementApiVersionSet() *schema.Resource { } func resourceArmApiManagementApiVersionSetCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiVersionSetClient + client := meta.(*ArmClient).apiManagement.ApiVersionSetClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -153,10 +154,10 @@ func resourceArmApiManagementApiVersionSetCreateUpdate(d *schema.ResourceData, m } func resourceArmApiManagementApiVersionSetRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiVersionSetClient + client := meta.(*ArmClient).apiManagement.ApiVersionSetClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -191,10 +192,10 @@ func resourceArmApiManagementApiVersionSetRead(d *schema.ResourceData, meta inte } func resourceArmApiManagementApiVersionSetDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ApiVersionSetClient + client := meta.(*ArmClient).apiManagement.ApiVersionSetClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_api_version_set_test.go b/azurerm/resource_arm_api_management_api_version_set_test.go index f0abe1b8c4c8..e7075f7ed0ef 100644 --- a/azurerm/resource_arm_api_management_api_version_set_test.go +++ b/azurerm/resource_arm_api_management_api_version_set_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMApiManagementApiVersionSet_basic(t *testing.T) { } func TestAccAzureRMApiManagementApiVersionSet_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -150,7 +151,7 @@ func TestAccAzureRMApiManagementApiVersionSet_update(t *testing.T) { } func testCheckAzureRMApiManagementApiVersionSetDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.ApiVersionSetClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ApiVersionSetClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_api_version_set" { continue @@ -185,7 +186,7 @@ func testCheckAzureRMApiManagementApiVersionSetExists(resourceName string) resou resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.ApiVersionSetClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ApiVersionSetClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { diff --git a/azurerm/resource_arm_api_management_authorization_server.go b/azurerm/resource_arm_api_management_authorization_server.go index 2fe7ada316e2..3e401a50b6b9 100644 --- a/azurerm/resource_arm_api_management_authorization_server.go +++ b/azurerm/resource_arm_api_management_authorization_server.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -175,14 +176,14 @@ func resourceArmApiManagementAuthorizationServer() *schema.Resource { } func resourceArmApiManagementAuthorizationServerCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.AuthorizationServersClient + client := meta.(*ArmClient).apiManagement.AuthorizationServersClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -269,9 +270,9 @@ func resourceArmApiManagementAuthorizationServerCreateUpdate(d *schema.ResourceD func resourceArmApiManagementAuthorizationServerRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).apimgmt.AuthorizationServersClient + client := meta.(*ArmClient).apiManagement.AuthorizationServersClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -333,10 +334,10 @@ func resourceArmApiManagementAuthorizationServerRead(d *schema.ResourceData, met } func resourceArmApiManagementAuthorizationServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.AuthorizationServersClient + client := meta.(*ArmClient).apiManagement.AuthorizationServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_authorization_server_test.go b/azurerm/resource_arm_api_management_authorization_server_test.go index 204aadc9a4f8..dac89f439cdd 100644 --- a/azurerm/resource_arm_api_management_authorization_server_test.go +++ b/azurerm/resource_arm_api_management_authorization_server_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMAPIManagementAuthorizationServer_basic(t *testing.T) { } func TestAccAzureRMAPIManagementAuthorizationServer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -90,7 +91,7 @@ func TestAccAzureRMAPIManagementAuthorizationServer_complete(t *testing.T) { } func testCheckAzureRMAPIManagementAuthorizationServerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.AuthorizationServersClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.AuthorizationServersClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_authorization_server" { continue @@ -125,7 +126,7 @@ func testCheckAzureRMAPIManagementAuthorizationServerExists(resourceName string) resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.AuthorizationServersClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.AuthorizationServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { @@ -152,10 +153,12 @@ resource "azurerm_api_management_authorization_server" "test" { authorization_endpoint = "https://azacctest.hashicorptest.com/client/authorize" client_id = "42424242-4242-4242-4242-424242424242" client_registration_endpoint = "https://azacctest.hashicorptest.com/client/register" + grant_types = [ "implicit", ] - authorization_methods = [ + + authorization_methods = [ "GET", ] } @@ -176,7 +179,8 @@ resource "azurerm_api_management_authorization_server" "import" { client_id = "${azurerm_api_management_authorization_server.test.client_id}" client_registration_endpoint = "${azurerm_api_management_authorization_server.test.client_registration_endpoint}" grant_types = "${azurerm_api_management_authorization_server.test.grant_types}" - authorization_methods = [ + + authorization_methods = [ "GET", ] } @@ -196,22 +200,26 @@ resource "azurerm_api_management_authorization_server" "test" { authorization_endpoint = "https://azacctest.hashicorptest.com/client/authorize" client_id = "42424242-4242-4242-4242-424242424242" client_registration_endpoint = "https://azacctest.hashicorptest.com/client/register" + grant_types = [ - "authorizationCode", + "authorizationCode", ] - authorization_methods = [ + + authorization_methods = [ "GET", "POST", ] + bearer_token_sending_methods = [ - "authorizationHeader" + "authorizationHeader", ] - client_secret = "n1n3-m0re-s3a5on5-m0r1y" - default_scope = "read write" - token_endpoint = "https://azacctest.hashicorptest.com/client/token" - resource_owner_username = "rick" - resource_owner_password = "C-193P" - support_state = true + + client_secret = "n1n3-m0re-s3a5on5-m0r1y" + default_scope = "read write" + token_endpoint = "https://azacctest.hashicorptest.com/client/token" + resource_owner_username = "rick" + resource_owner_password = "C-193P" + support_state = true } `, template, rInt) } diff --git a/azurerm/resource_arm_api_management_backend.go b/azurerm/resource_arm_api_management_backend.go new file mode 100644 index 000000000000..ba66aa70a98d --- /dev/null +++ b/azurerm/resource_arm_api_management_backend.go @@ -0,0 +1,615 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmApiManagementBackend() *schema.Resource { + return &schema.Resource{ + Create: resourceArmApiManagementBackendCreateUpdate, + Read: resourceArmApiManagementBackendRead, + Update: resourceArmApiManagementBackendCreateUpdate, + Delete: resourceArmApiManagementBackendDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ApiManagementBackendName, + }, + + "api_management_name": azure.SchemaApiManagementName(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "credentials": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "authorization": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "parameter": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "scheme": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "certificate": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "header": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "query": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 2000), + }, + + "protocol": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(apimanagement.BackendProtocolHTTP), + string(apimanagement.BackendProtocolSoap), + }, false), + }, + + "proxy": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "username": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + + "resource_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 2000), + }, + + "service_fabric_cluster": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_certificate_thumbprint": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "management_endpoints": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "max_partition_resolution_retries": { + Type: schema.TypeInt, + Required: true, + }, + "server_certificate_thumbprints": { + Type: schema.TypeSet, + Optional: true, + ConflictsWith: []string{"service_fabric_cluster.0.server_x509_name"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "server_x509_name": { + Type: schema.TypeSet, + Optional: true, + ConflictsWith: []string{"service_fabric_cluster.0.server_certificate_thumbprints"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "issuer_certificate_thumbprint": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + }, + }, + + "title": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 300), + }, + + "tls": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "validate_certificate_chain": { + Type: schema.TypeBool, + Optional: true, + }, + "validate_certificate_name": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + } +} + +func resourceArmApiManagementBackendCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagement.BackendClient + ctx := meta.(*ArmClient).StopContext + resourceGroup := d.Get("resource_group_name").(string) + serviceName := d.Get("api_management_name").(string) + name := d.Get("name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing backend %q (API Management Service %q / Resource Group %q): %s", name, serviceName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_api_management_backend", *existing.ID) + } + } + + credentialsRaw := d.Get("credentials").([]interface{}) + credentials := expandApiManagementBackendCredentials(credentialsRaw) + protocol := d.Get("protocol").(string) + proxyRaw := d.Get("proxy").([]interface{}) + proxy := expandApiManagementBackendProxy(proxyRaw) + tlsRaw := d.Get("tls").([]interface{}) + tls := expandApiManagementBackendTls(tlsRaw) + url := d.Get("url").(string) + + backendContract := apimanagement.BackendContract{ + BackendContractProperties: &apimanagement.BackendContractProperties{ + Credentials: credentials, + Protocol: apimanagement.BackendProtocol(protocol), + Proxy: proxy, + TLS: tls, + URL: utils.String(url), + }, + } + if description, ok := d.GetOk("description"); ok { + backendContract.BackendContractProperties.Description = utils.String(description.(string)) + } + if resourceID, ok := d.GetOk("resource_id"); ok { + backendContract.BackendContractProperties.ResourceID = utils.String(resourceID.(string)) + } + if title, ok := d.GetOk("title"); ok { + backendContract.BackendContractProperties.Title = utils.String(title.(string)) + } + + if serviceFabricClusterRaw, ok := d.GetOk("service_fabric_cluster"); ok { + err, serviceFabricCluster := expandApiManagementBackendServiceFabricCluster(serviceFabricClusterRaw.([]interface{})) + if err != nil { + return err + } + backendContract.BackendContractProperties.Properties = &apimanagement.BackendProperties{ + ServiceFabricCluster: serviceFabricCluster, + } + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, name, backendContract, ""); err != nil { + return fmt.Errorf("Error creating/updating backend %q (API Management Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + return fmt.Errorf("Error retrieving backend %q (API Management Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err) + } + + if read.ID == nil { + return fmt.Errorf("Cannot read ID for backend %q (API Management Service %q / Resource Group %q)", name, serviceName, resourceGroup) + } + + d.SetId(*read.ID) + return resourceArmApiManagementBackendRead(d, meta) +} + +func resourceArmApiManagementBackendRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagement.BackendClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serviceName := id.Path["service"] + name := id.Path["backends"] + + resp, err := client.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] backend %q (API Management Service %q / Resource Group %q) does not exist - removing from state!", name, serviceName, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving backend %q (API Management Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("api_management_name", serviceName) + d.Set("resource_group_name", resourceGroup) + + if props := resp.BackendContractProperties; props != nil { + d.Set("description", props.Description) + d.Set("protocol", string(props.Protocol)) + d.Set("resource_id", props.ResourceID) + d.Set("title", props.Title) + d.Set("url", props.URL) + if err := d.Set("credentials", flattenApiManagementBackendCredentials(props.Credentials)); err != nil { + return fmt.Errorf("Error setting `credentials`: %s", err) + } + if err := d.Set("proxy", flattenApiManagementBackendProxy(props.Proxy)); err != nil { + return fmt.Errorf("Error setting `proxy`: %s", err) + } + if properties := props.Properties; properties != nil { + if err := d.Set("service_fabric_cluster", flattenApiManagementBackendServiceFabricCluster(properties.ServiceFabricCluster)); err != nil { + return fmt.Errorf("Error setting `service_fabric_cluster`: %s", err) + } + } + if err := d.Set("tls", flattenApiManagementBackendTls(props.TLS)); err != nil { + return fmt.Errorf("Error setting `tls`: %s", err) + } + } + + return nil +} + +func resourceArmApiManagementBackendDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagement.BackendClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serviceName := id.Path["service"] + name := id.Path["backends"] + + if resp, err := client.Delete(ctx, resourceGroup, serviceName, name, ""); err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error deleting backend %q (API Management Service %q / Resource Group %q): %s", name, serviceName, resourceGroup, err) + } + } + + return nil +} + +func expandApiManagementBackendCredentials(input []interface{}) *apimanagement.BackendCredentialsContract { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + contract := apimanagement.BackendCredentialsContract{} + if authorizationRaw := v["authorization"]; authorizationRaw != nil { + authorization := expandApiManagementBackendCredentialsAuthorization(authorizationRaw.([]interface{})) + contract.Authorization = authorization + } + if certificate := v["certificate"]; certificate != nil { + certificates := utils.ExpandStringSlice(certificate.([]interface{})) + if certificates != nil && len(*certificates) > 0 { + contract.Certificate = certificates + } + } + if headerRaw := v["header"]; headerRaw != nil { + header := expandApiManagementBackendCredentialsObject(headerRaw.(map[string]interface{})) + contract.Header = *header + } + if queryRaw := v["query"]; queryRaw != nil { + query := expandApiManagementBackendCredentialsObject(queryRaw.(map[string]interface{})) + contract.Query = *query + } + return &contract +} + +func expandApiManagementBackendCredentialsAuthorization(input []interface{}) *apimanagement.BackendAuthorizationHeaderCredentials { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + credentials := apimanagement.BackendAuthorizationHeaderCredentials{} + if parameter := v["parameter"]; parameter != nil { + credentials.Parameter = utils.String(parameter.(string)) + } + if scheme := v["scheme"]; scheme != nil { + credentials.Scheme = utils.String(scheme.(string)) + } + return &credentials +} + +func expandApiManagementBackendCredentialsObject(input map[string]interface{}) *map[string][]string { + output := make(map[string][]string) + for k, v := range input { + output[k] = strings.Split(v.(string), ",") + } + return &output +} + +func expandApiManagementBackendProxy(input []interface{}) *apimanagement.BackendProxyContract { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + contract := apimanagement.BackendProxyContract{} + if password := v["password"]; password != nil { + contract.Password = utils.String(password.(string)) + } + if url := v["url"]; url != nil { + contract.URL = utils.String(url.(string)) + } + if username := v["username"]; username != nil { + contract.Username = utils.String(username.(string)) + } + return &contract +} + +func expandApiManagementBackendServiceFabricCluster(input []interface{}) (error, *apimanagement.BackendServiceFabricClusterProperties) { + if len(input) == 0 { + return nil, nil + } + v := input[0].(map[string]interface{}) + clientCertificatethumbprint := v["client_certificate_thumbprint"].(string) + managementEndpoints := v["management_endpoints"].(*schema.Set).List() + maxPartitionResolutionRetries := int32(v["max_partition_resolution_retries"].(int)) + properties := apimanagement.BackendServiceFabricClusterProperties{ + ClientCertificatethumbprint: utils.String(clientCertificatethumbprint), + ManagementEndpoints: utils.ExpandStringSlice(managementEndpoints), + MaxPartitionResolutionRetries: utils.Int32(maxPartitionResolutionRetries), + } + serverCertificateThumbprintsUnset := true + serverX509NamesUnset := true + if serverCertificateThumbprints := v["server_certificate_thumbprints"]; serverCertificateThumbprints != nil { + properties.ServerCertificateThumbprints = utils.ExpandStringSlice(serverCertificateThumbprints.(*schema.Set).List()) + serverCertificateThumbprintsUnset = false + } + if serverX509Names := v["server_x509_name"]; serverX509Names != nil { + properties.ServerX509Names = expandApiManagementBackendServiceFabricClusterServerX509Names(serverX509Names.(*schema.Set).List()) + serverX509NamesUnset = false + } + if serverCertificateThumbprintsUnset && serverX509NamesUnset { + return fmt.Errorf("One of `server_certificate_thumbprints` or `server_x509_name` must be set"), nil + } + return nil, &properties +} + +func expandApiManagementBackendServiceFabricClusterServerX509Names(input []interface{}) *[]apimanagement.X509CertificateName { + results := make([]apimanagement.X509CertificateName, 0) + for _, certificateName := range input { + v := certificateName.(map[string]interface{}) + result := apimanagement.X509CertificateName{ + IssuerCertificateThumbprint: utils.String(v["issuer_certificate_thumbprint"].(string)), + Name: utils.String(v["name"].(string)), + } + results = append(results, result) + } + return &results +} + +func expandApiManagementBackendTls(input []interface{}) *apimanagement.BackendTLSProperties { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + properties := apimanagement.BackendTLSProperties{} + if validateCertificateChain := v["validate_certificate_chain"]; validateCertificateChain != nil { + properties.ValidateCertificateChain = utils.Bool(validateCertificateChain.(bool)) + } + if validateCertificateName := v["validate_certificate_name"]; validateCertificateName != nil { + properties.ValidateCertificateName = utils.Bool(validateCertificateName.(bool)) + } + return &properties +} + +func flattenApiManagementBackendCredentials(input *apimanagement.BackendCredentialsContract) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + result := make(map[string]interface{}) + result["authorization"] = flattenApiManagementBackendCredentialsAuthorization(input.Authorization) + if input.Certificate != nil { + result["certificate"] = *input.Certificate + } + result["header"] = flattenApiManagementBackendCredentialsObject(input.Header) + result["query"] = flattenApiManagementBackendCredentialsObject(input.Query) + return append(results, result) +} + +func flattenApiManagementBackendCredentialsObject(input map[string][]string) map[string]interface{} { + results := make(map[string]interface{}) + if input == nil { + return results + } + for k, v := range input { + results[k] = strings.Join(v, ",") + } + return results +} + +func flattenApiManagementBackendCredentialsAuthorization(input *apimanagement.BackendAuthorizationHeaderCredentials) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + result := make(map[string]interface{}) + if parameter := input.Parameter; parameter != nil { + result["parameter"] = *parameter + } + if scheme := input.Scheme; scheme != nil { + result["scheme"] = *scheme + } + return append(results, result) +} + +func flattenApiManagementBackendProxy(input *apimanagement.BackendProxyContract) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + result := make(map[string]interface{}) + if password := input.Password; password != nil { + result["password"] = *password + } + if url := input.URL; url != nil { + result["url"] = *url + } + if username := input.Username; username != nil { + result["username"] = *username + } + return append(results, result) +} + +func flattenApiManagementBackendServiceFabricCluster(input *apimanagement.BackendServiceFabricClusterProperties) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + result := make(map[string]interface{}) + if clientCertificatethumbprint := input.ClientCertificatethumbprint; clientCertificatethumbprint != nil { + result["client_certificate_thumbprint"] = *clientCertificatethumbprint + } + if managementEndpoints := input.ManagementEndpoints; managementEndpoints != nil { + result["management_endpoints"] = *managementEndpoints + } + if maxPartitionResolutionRetries := input.MaxPartitionResolutionRetries; maxPartitionResolutionRetries != nil { + result["max_partition_resolution_retries"] = int(*maxPartitionResolutionRetries) + } + if serverCertificateThumbprints := input.ServerCertificateThumbprints; serverCertificateThumbprints != nil { + result["server_certificate_thumbprints"] = *serverCertificateThumbprints + } + result["server_x509_name"] = flattenApiManagementBackendServiceFabricClusterServerX509Names(input.ServerX509Names) + return append(results, result) +} + +func flattenApiManagementBackendServiceFabricClusterServerX509Names(input *[]apimanagement.X509CertificateName) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + for _, certificateName := range *input { + result := make(map[string]interface{}) + if issuerCertificateThumbprint := certificateName.IssuerCertificateThumbprint; issuerCertificateThumbprint != nil { + result["issuer_certificate_thumbprint"] = *issuerCertificateThumbprint + } + if name := certificateName.Name; name != nil { + result["name"] = *name + } + results = append(results, result) + } + return results +} + +func flattenApiManagementBackendTls(input *apimanagement.BackendTLSProperties) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + result := make(map[string]interface{}) + if validateCertificateChain := input.ValidateCertificateChain; validateCertificateChain != nil { + result["validate_certificate_chain"] = *validateCertificateChain + } + if validateCertificateName := input.ValidateCertificateName; validateCertificateName != nil { + result["validate_certificate_name"] = *validateCertificateName + } + return append(results, result) +} diff --git a/azurerm/resource_arm_api_management_backend_test.go b/azurerm/resource_arm_api_management_backend_test.go new file mode 100644 index 000000000000..4f0fee31d754 --- /dev/null +++ b/azurerm/resource_arm_api_management_backend_test.go @@ -0,0 +1,534 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMApiManagementBackend_basic(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_basic(ri, "basic", location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "http"), + resource.TestCheckResourceAttr(resourceName, "url", "https://acctest"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_allProperties(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_allProperties(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "http"), + resource.TestCheckResourceAttr(resourceName, "url", "https://acctest"), + resource.TestCheckResourceAttr(resourceName, "description", "description"), + resource.TestCheckResourceAttr(resourceName, "resource_id", "https://resourceid"), + resource.TestCheckResourceAttr(resourceName, "title", "title"), + resource.TestCheckResourceAttr(resourceName, "credentials.#", "1"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.authorization.0.parameter", "parameter"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.authorization.0.scheme", "scheme"), + resource.TestCheckResourceAttrSet(resourceName, "credentials.0.certificate.0"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.header.header1", "header1value1,header1value2"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.header.header2", "header2value1,header2value2"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.query.query1", "query1value1,query1value2"), + resource.TestCheckResourceAttr(resourceName, "credentials.0.query.query2", "query2value1,query2value2"), + resource.TestCheckResourceAttr(resourceName, "proxy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.url", "http://192.168.1.1:8080"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.username", "username"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.password", "password"), + resource.TestCheckResourceAttr(resourceName, "tls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls.0.validate_certificate_chain", "false"), + resource.TestCheckResourceAttr(resourceName, "tls.0.validate_certificate_name", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_credentialsNoCertificate(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_credentialsNoCertificate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_update(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_basic(ri, "update", location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "http"), + resource.TestCheckResourceAttr(resourceName, "url", "https://acctest"), + ), + }, + { + Config: testAccAzureRMApiManagementBackend_update(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "soap"), + resource.TestCheckResourceAttr(resourceName, "url", "https://updatedacctest"), + resource.TestCheckResourceAttr(resourceName, "description", "description"), + resource.TestCheckResourceAttr(resourceName, "resource_id", "https://resourceid"), + resource.TestCheckResourceAttr(resourceName, "proxy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.url", "http://192.168.1.1:8080"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.username", "username"), + resource.TestCheckResourceAttr(resourceName, "proxy.0.password", "password"), + resource.TestCheckResourceAttr(resourceName, "tls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls.0.validate_certificate_chain", "false"), + resource.TestCheckResourceAttr(resourceName, "tls.0.validate_certificate_name", "true"), + ), + }, + { + Config: testAccAzureRMApiManagementBackend_basic(ri, "update", location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "http"), + resource.TestCheckResourceAttr(resourceName, "url", "https://acctest"), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "resource_id", ""), + resource.TestCheckResourceAttr(resourceName, "proxy.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tls.#", "0"), + ), + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_serviceFabric(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_serviceFabric(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "protocol", "http"), + resource.TestCheckResourceAttr(resourceName, "url", "https://acctest"), + resource.TestCheckResourceAttr(resourceName, "service_fabric_cluster.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "service_fabric_cluster.0.client_certificate_thumbprint"), + resource.TestCheckResourceAttr(resourceName, "service_fabric_cluster.0.max_partition_resolution_retries", "5"), + resource.TestCheckResourceAttr(resourceName, "service_fabric_cluster.0.management_endpoints.#", "1"), + resource.TestCheckResourceAttr(resourceName, "service_fabric_cluster.0.server_certificate_thumbprints.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_disappears(t *testing.T) { + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_basic(ri, "disappears", location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + testCheckAzureRMApiManagementBackendDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMApiManagementBackend_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_api_management_backend.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementBackendDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementBackend_basic(ri, "import", location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementBackendExists(resourceName), + ), + }, + { + Config: testAccAzureRMApiManagementBackend_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_api_management_backend"), + }, + }, + }) +} + +func testCheckAzureRMApiManagementBackendDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).apiManagement.BackendClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_api_management_backend" { + continue + } + + name := rs.Primary.Attributes["name"] + serviceName := rs.Primary.Attributes["api_management_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := conn.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return nil + } + + return nil +} + +func testCheckAzureRMApiManagementBackendExists(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"] + serviceName := rs.Primary.Attributes["api_management_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + conn := testAccProvider.Meta().(*ArmClient).apiManagement.BackendClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := conn.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Backend %q (API Management Service %q / Resource Group: %q) does not exist", name, serviceName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on BackendClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMApiManagementBackendDisappears(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"] + serviceName := rs.Primary.Attributes["api_management_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for backend: %s", name) + } + + conn := testAccProvider.Meta().(*ArmClient).apiManagement.BackendClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := conn.Delete(ctx, resourceGroup, serviceName, name, "") + if err != nil { + if utils.ResponseWasNotFound(resp) { + return nil + } + return fmt.Errorf("Bad: Delete on BackendClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMApiManagementBackend_basic(rInt int, testName string, location string) string { + template := testAccAzureRMApiManagementBackend_template(rInt, testName, location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_backend" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + protocol = "http" + url = "https://acctest" +} +`, template, rInt) +} + +func testAccAzureRMApiManagementBackend_update(rInt int, location string) string { + template := testAccAzureRMApiManagementBackend_template(rInt, "update", location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_backend" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + protocol = "soap" + url = "https://updatedacctest" + description = "description" + resource_id = "https://resourceid" + proxy { + url = "http://192.168.1.1:8080" + username = "username" + password = "password" + } + tls { + validate_certificate_chain = false + validate_certificate_name = true + } +} +`, template, rInt) +} + +func testAccAzureRMApiManagementBackend_allProperties(rInt int, location string) string { + template := testAccAzureRMApiManagementBackend_template(rInt, "all", location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_certificate" "test" { + name = "example-cert" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + data = "${filebase64("testdata/keyvaultcert.pfx")}" + password = "" +} + +resource "azurerm_api_management_backend" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + protocol = "http" + url = "https://acctest" + description = "description" + resource_id = "https://resourceid" + title = "title" + credentials { + authorization { + parameter = "parameter" + scheme = "scheme" + } + certificate = [ + "${azurerm_api_management_certificate.test.thumbprint}", + ] + header = { + header1 = "header1value1,header1value2" + header2 = "header2value1,header2value2" + } + query = { + query1 = "query1value1,query1value2" + query2 = "query2value1,query2value2" + } + } + proxy { + url = "http://192.168.1.1:8080" + username = "username" + password = "password" + } + tls { + validate_certificate_chain = false + validate_certificate_name = true + } +} +`, template, rInt) +} + +func testAccAzureRMApiManagementBackend_serviceFabric(rInt int, location string) string { + template := testAccAzureRMApiManagementBackend_template(rInt, "sf", location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_certificate" "test" { + name = "example-cert" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + data = "${filebase64("testdata/keyvaultcert.pfx")}" + password = "" +} + +resource "azurerm_api_management_backend" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + protocol = "http" + url = "https://acctest" + service_fabric_cluster { + client_certificate_thumbprint = "${azurerm_api_management_certificate.test.thumbprint}" + management_endpoints = [ + "https://acctestsf.com", + ] + max_partition_resolution_retries = 5 + server_certificate_thumbprints = [ + "thumb1", + "thumb2", + ] + } +} +`, template, rInt) +} + +func testAccAzureRMApiManagementBackend_requiresImport(rInt int, location string) string { + template := testAccAzureRMApiManagementBackend_basic(rInt, "requiresimport", location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_backend" "import" { + name = "${azurerm_api_management_backend.test.name}" + resource_group_name = "${azurerm_api_management_backend.test.resource_group_name}" + api_management_name = "${azurerm_api_management_backend.test.api_management_name}" + protocol = "${azurerm_api_management_backend.test.protocol}" + url = "${azurerm_api_management_backend.test.url}" +} +`, template) +} + +func testAccAzureRMApiManagementBackend_template(rInt int, testName string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d-%s" + location = "%s" +} + +resource "azurerm_api_management" "test" { + name = "acctestAM-%d-%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku { + name = "Developer" + capacity = 1 + } +} +`, rInt, testName, location, rInt, testName) +} + +func testAccAzureRMApiManagementBackend_credentialsNoCertificate(rInt int, location string) string { + template := testAccAzureRMApiManagementBackend_template(rInt, "all", location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_backend" "test" { + name = "acctestapi-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + protocol = "http" + url = "https://acctest" + description = "description" + resource_id = "https://resourceid" + title = "title" + credentials { + authorization { + parameter = "parameter" + scheme = "scheme" + } + header = { + header1 = "header1value1,header1value2" + header2 = "header2value1,header2value2" + } + query = { + query1 = "query1value1,query1value2" + query2 = "query2value1,query2value2" + } + } + proxy { + url = "http://192.168.1.1:8080" + username = "username" + password = "password" + } + tls { + validate_certificate_chain = false + validate_certificate_name = true + } +} +`, template, rInt) +} diff --git a/azurerm/resource_arm_api_management_certificate.go b/azurerm/resource_arm_api_management_certificate.go index 7d05b16cb87e..aa697ebbaf22 100644 --- a/azurerm/resource_arm_api_management_certificate.go +++ b/azurerm/resource_arm_api_management_certificate.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -62,7 +63,7 @@ func resourceArmApiManagementCertificate() *schema.Resource { } func resourceArmApiManagementCertificateCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.CertificatesClient + client := meta.(*ArmClient).apiManagement.CertificatesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -71,7 +72,7 @@ func resourceArmApiManagementCertificateCreateUpdate(d *schema.ResourceData, met data := d.Get("data").(string) password := d.Get("password").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -108,10 +109,10 @@ func resourceArmApiManagementCertificateCreateUpdate(d *schema.ResourceData, met } func resourceArmApiManagementCertificateRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.CertificatesClient + client := meta.(*ArmClient).apiManagement.CertificatesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -135,7 +136,6 @@ func resourceArmApiManagementCertificateRead(d *schema.ResourceData, meta interf d.Set("api_management_name", serviceName) if props := resp.CertificateContractProperties; props != nil { - if expiration := props.ExpirationDate; expiration != nil { formatted := expiration.Format(time.RFC3339) d.Set("expiration", formatted) @@ -149,10 +149,10 @@ func resourceArmApiManagementCertificateRead(d *schema.ResourceData, meta interf } func resourceArmApiManagementCertificateDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.CertificatesClient + client := meta.(*ArmClient).apiManagement.CertificatesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_certificate_test.go b/azurerm/resource_arm_api_management_certificate_test.go index c5c0f47074e2..a72a7e6ddf7d 100644 --- a/azurerm/resource_arm_api_management_certificate_test.go +++ b/azurerm/resource_arm_api_management_certificate_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,7 +45,7 @@ func TestAccAzureRMAPIManagementCertificate_basic(t *testing.T) { } func TestAccAzureRMAPIManagementCertificate_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -73,7 +74,7 @@ func TestAccAzureRMAPIManagementCertificate_requiresImport(t *testing.T) { } func testCheckAzureRMAPIManagementCertificateDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.CertificatesClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.CertificatesClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_certificate" { continue @@ -108,7 +109,7 @@ func testCheckAzureRMAPIManagementCertificateExists(resourceName string) resourc resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.CertificatesClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.CertificatesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { diff --git a/azurerm/resource_arm_api_management_group.go b/azurerm/resource_arm_api_management_group.go index dc44f313142f..825572bd9566 100644 --- a/azurerm/resource_arm_api_management_group.go +++ b/azurerm/resource_arm_api_management_group.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -62,7 +63,7 @@ func resourceArmApiManagementGroup() *schema.Resource { } func resourceArmApiManagementGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupClient + client := meta.(*ArmClient).apiManagement.GroupClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -74,7 +75,7 @@ func resourceArmApiManagementGroupCreateUpdate(d *schema.ResourceData, meta inte externalID := d.Get("external_id").(string) groupType := d.Get("type").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -113,10 +114,10 @@ func resourceArmApiManagementGroupCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmApiManagementGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupClient + client := meta.(*ArmClient).apiManagement.GroupClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -150,10 +151,10 @@ func resourceArmApiManagementGroupRead(d *schema.ResourceData, meta interface{}) } func resourceArmApiManagementGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupClient + client := meta.(*ArmClient).apiManagement.GroupClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_group_test.go b/azurerm/resource_arm_api_management_group_test.go index 7704831f945f..4dd51e2a0cb5 100644 --- a/azurerm/resource_arm_api_management_group_test.go +++ b/azurerm/resource_arm_api_management_group_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMAPIManagementGroup_basic(t *testing.T) { } func TestAccAzureRMAPIManagementGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -139,7 +140,7 @@ func TestAccAzureRMAPIManagementGroup_descriptionDisplayNameUpdate(t *testing.T) } func testCheckAzureRMAPIManagementGroupDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.GroupClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.GroupClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_group" { continue @@ -174,14 +175,14 @@ func testCheckAzureRMAPIManagementGroupExists(resourceName string) resource.Test resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.GroupClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.GroupClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: API Management Group %q (Resource Group %q / API Management Service %q) does not exist", name, resourceGroup, serviceName) } - return fmt.Errorf("Bad: Get on apimgmt.GroupClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.GroupClient: %+v", err) } return nil @@ -202,10 +203,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_group" "test" { @@ -245,10 +243,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_group" "test" { diff --git a/azurerm/resource_arm_api_management_group_user.go b/azurerm/resource_arm_api_management_group_user.go index 305365b9ed03..07efdaca1bce 100644 --- a/azurerm/resource_arm_api_management_group_user.go +++ b/azurerm/resource_arm_api_management_group_user.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func resourceArmApiManagementGroupUser() *schema.Resource { } func resourceArmApiManagementGroupUserCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupUsersClient + client := meta.(*ArmClient).apiManagement.GroupUsersClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -40,7 +41,7 @@ func resourceArmApiManagementGroupUserCreate(d *schema.ResourceData, meta interf groupName := d.Get("group_name").(string) userId := d.Get("user_id").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) if err != nil { if !utils.ResponseWasNotFound(resp) { @@ -67,10 +68,10 @@ func resourceArmApiManagementGroupUserCreate(d *schema.ResourceData, meta interf } func resourceArmApiManagementGroupUserRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupUsersClient + client := meta.(*ArmClient).apiManagement.GroupUsersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -99,10 +100,10 @@ func resourceArmApiManagementGroupUserRead(d *schema.ResourceData, meta interfac } func resourceArmApiManagementGroupUserDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.GroupUsersClient + client := meta.(*ArmClient).apiManagement.GroupUsersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_group_user_test.go b/azurerm/resource_arm_api_management_group_user_test.go index 4351bb0ded57..9ad52c011808 100644 --- a/azurerm/resource_arm_api_management_group_user_test.go +++ b/azurerm/resource_arm_api_management_group_user_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMAPIManagementGroupUser_basic(t *testing.T) { } func TestAccAzureRMAPIManagementGroupUser_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -65,7 +66,7 @@ func TestAccAzureRMAPIManagementGroupUser_requiresImport(t *testing.T) { } func testCheckAzureRMAPIManagementGroupUserDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.GroupUsersClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.GroupUsersClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_group_user" { continue @@ -101,14 +102,14 @@ func testCheckAzureRMAPIManagementGroupUserExists(resourceName string) resource. resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.GroupUsersClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.GroupUsersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) if err != nil { if utils.ResponseWasNotFound(resp) { return fmt.Errorf("Bad: User %q / Group %q (API Management Service %q / Resource Group %q) does not exist", userId, groupName, serviceName, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.GroupUsersClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.GroupUsersClient: %+v", err) } return nil @@ -129,10 +130,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_group" "test" { @@ -143,19 +141,19 @@ resource "azurerm_api_management_group" "test" { } resource "azurerm_api_management_user" "test" { - user_id = "acctestuser%d" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - first_name = "Acceptance" - last_name = "Test" - email = "azure-acctest%d@example.com" + user_id = "acctestuser%d" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + first_name = "Acceptance" + last_name = "Test" + email = "azure-acctest%d@example.com" } resource "azurerm_api_management_group_user" "test" { - user_id = "${azurerm_api_management_user.test.user_id}" - group_name = "${azurerm_api_management_group.test.name}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" + user_id = "${azurerm_api_management_user.test.user_id}" + group_name = "${azurerm_api_management_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" } `, rInt, location, rInt, rInt, rInt, rInt) } @@ -166,10 +164,10 @@ func testAccAzureRMAPIManagementGroupUser_requiresImport(rInt int, location stri %s resource "azurerm_api_management_group_user" "import" { - user_id = "${azurerm_api_management_group_user.test.user_id}" - group_name = "${azurerm_api_management_group_user.test.group_name}" - api_management_name = "${azurerm_api_management_group_user.test.api_management_name}" - resource_group_name = "${azurerm_api_management_group_user.test.resource_group_name}" + user_id = "${azurerm_api_management_group_user.test.user_id}" + group_name = "${azurerm_api_management_group_user.test.group_name}" + api_management_name = "${azurerm_api_management_group_user.test.api_management_name}" + resource_group_name = "${azurerm_api_management_group_user.test.resource_group_name}" } `, template) } diff --git a/azurerm/resource_arm_api_management_logger.go b/azurerm/resource_arm_api_management_logger.go index 7a2077c72e00..481b9e363742 100644 --- a/azurerm/resource_arm_api_management_logger.go +++ b/azurerm/resource_arm_api_management_logger.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -87,7 +88,7 @@ func resourceArmApiManagementLogger() *schema.Resource { } func resourceArmApiManagementLoggerCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.LoggerClient + client := meta.(*ArmClient).apiManagement.LoggerClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -101,7 +102,7 @@ func resourceArmApiManagementLoggerCreate(d *schema.ResourceData, meta interface return fmt.Errorf("Either `eventhub` or `application_insights` is required") } - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -146,10 +147,10 @@ func resourceArmApiManagementLoggerCreate(d *schema.ResourceData, meta interface } func resourceArmApiManagementLoggerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.LoggerClient + client := meta.(*ArmClient).apiManagement.LoggerClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -183,7 +184,7 @@ func resourceArmApiManagementLoggerRead(d *schema.ResourceData, meta interface{} } func resourceArmApiManagementLoggerUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.LoggerClient + client := meta.(*ArmClient).apiManagement.LoggerClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -216,10 +217,10 @@ func resourceArmApiManagementLoggerUpdate(d *schema.ResourceData, meta interface } func resourceArmApiManagementLoggerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.LoggerClient + client := meta.(*ArmClient).apiManagement.LoggerClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_logger_test.go b/azurerm/resource_arm_api_management_logger_test.go index bfebe4d426df..bd53160c6dab 100644 --- a/azurerm/resource_arm_api_management_logger_test.go +++ b/azurerm/resource_arm_api_management_logger_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -41,7 +42,7 @@ func TestAccAzureRMApiManagementLogger_basicEventHub(t *testing.T) { } func TestAccAzureRMApiManagementLogger_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -225,14 +226,14 @@ func testCheckAzureRMApiManagementLoggerExists(resourceName string) resource.Tes resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.LoggerClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.LoggerClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if resp, err := client.Get(ctx, resourceGroup, serviceName, name); err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: Logger %q (Resource Group %q / API Management Service %q) does not exist", name, resourceGroup, serviceName) } - return fmt.Errorf("Bad: Get on apimgmt.LoggerClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.LoggerClient: %+v", err) } return nil @@ -240,7 +241,7 @@ func testCheckAzureRMApiManagementLoggerExists(resourceName string) resource.Tes } func testCheckAzureRMApiManagementLoggerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.LoggerClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.LoggerClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -254,7 +255,7 @@ func testCheckAzureRMApiManagementLoggerDestroy(s *terraform.State) error { if resp, err := client.Get(ctx, resourceGroup, serviceName, name); err != nil { if !utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: Get on apimgmt.LoggerClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.LoggerClient: %+v", err) } } @@ -293,10 +294,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_logger" "test" { @@ -305,7 +303,7 @@ resource "azurerm_api_management_logger" "test" { resource_group_name = "${azurerm_resource_group.test.name}" eventhub { - name = "${azurerm_eventhub.test.name}" + name = "${azurerm_eventhub.test.name}" connection_string = "${azurerm_eventhub_namespace.test.default_primary_connection_string}" } } @@ -323,7 +321,7 @@ resource "azurerm_api_management_logger" "import" { resource_group_name = "${azurerm_api_management_logger.test.resource_group_name}" eventhub { - name = "${azurerm_eventhub.test.name}" + name = "${azurerm_eventhub.test.name}" connection_string = "${azurerm_eventhub_namespace.test.default_primary_connection_string}" } } @@ -351,10 +349,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_logger" "test" { @@ -390,10 +385,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_logger" "test" { diff --git a/azurerm/resource_arm_api_management_openid_connect_provider.go b/azurerm/resource_arm_api_management_openid_connect_provider.go index 41b9f8ddc99c..0c0f4f3a9a3c 100644 --- a/azurerm/resource_arm_api_management_openid_connect_provider.go +++ b/azurerm/resource_arm_api_management_openid_connect_provider.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -65,14 +66,14 @@ func resourceArmApiManagementOpenIDConnectProvider() *schema.Resource { } func resourceArmApiManagementOpenIDConnectProviderCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.OpenIdConnectClient + client := meta.(*ArmClient).apiManagement.OpenIdConnectClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -112,10 +113,10 @@ func resourceArmApiManagementOpenIDConnectProviderCreateUpdate(d *schema.Resourc } func resourceArmApiManagementOpenIDConnectProviderRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.OpenIdConnectClient + client := meta.(*ArmClient).apiManagement.OpenIdConnectClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -149,10 +150,10 @@ func resourceArmApiManagementOpenIDConnectProviderRead(d *schema.ResourceData, m } func resourceArmApiManagementOpenIDConnectProviderDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.OpenIdConnectClient + client := meta.(*ArmClient).apiManagement.OpenIdConnectClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_openid_connect_provider_test.go b/azurerm/resource_arm_api_management_openid_connect_provider_test.go index e0a0f353fa81..8c9776d764fe 100644 --- a/azurerm/resource_arm_api_management_openid_connect_provider_test.go +++ b/azurerm/resource_arm_api_management_openid_connect_provider_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMApiManagementOpenIDConnectProvider_basic(t *testing.T) { } func TestAccAzureRMApiManagementOpenIDConnectProvider_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -106,14 +107,14 @@ func testCheckAzureRMApiManagementOpenIDConnectProviderExists(resourceName strin resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.OpenIdConnectClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.OpenIdConnectClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if resp, err := client.Get(ctx, resourceGroup, serviceName, name); err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: OpenID Connect Provider %q (Resource Group %q / API Management Service %q) does not exist", name, resourceGroup, serviceName) } - return fmt.Errorf("Bad: Get on apimgmt.OpenIdConnectClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.OpenIdConnectClient: %+v", err) } return nil @@ -121,7 +122,7 @@ func testCheckAzureRMApiManagementOpenIDConnectProviderExists(resourceName strin } func testCheckAzureRMApiManagementOpenIDConnectProviderDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.OpenIdConnectClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.OpenIdConnectClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -135,7 +136,7 @@ func testCheckAzureRMApiManagementOpenIDConnectProviderDestroy(s *terraform.Stat if resp, err := client.Get(ctx, resourceGroup, serviceName, name); err != nil { if !utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: Get on apimgmt.OpenIdConnectClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.OpenIdConnectClient: %+v", err) } } diff --git a/azurerm/resource_arm_api_management_product.go b/azurerm/resource_arm_api_management_product.go index 8616b7d034cf..b3c94fbb175c 100644 --- a/azurerm/resource_arm_api_management_product.go +++ b/azurerm/resource_arm_api_management_product.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -69,7 +70,7 @@ func resourceArmApiManagementProduct() *schema.Resource { } func resourceArmApiManagementProductCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductsClient + client := meta.(*ArmClient).apiManagement.ProductsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for API Management Product creation.") @@ -86,7 +87,7 @@ func resourceArmApiManagementProductCreateUpdate(d *schema.ResourceData, meta in subscriptionsLimit := d.Get("subscriptions_limit").(int) published := d.Get("published").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, productId) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -118,6 +119,8 @@ func resourceArmApiManagementProductCreateUpdate(d *schema.ResourceData, meta in if subscriptionRequired && subscriptionsLimit > 0 { properties.ProductContractProperties.ApprovalRequired = utils.Bool(approvalRequired) properties.ProductContractProperties.SubscriptionsLimit = utils.Int32(int32(subscriptionsLimit)) + } else if approvalRequired { + return fmt.Errorf("`subscription_required` must be true and `subscriptions_limit` must be greater than 0 to use `approval_required`") } if _, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, productId, properties, ""); err != nil { @@ -139,10 +142,10 @@ func resourceArmApiManagementProductCreateUpdate(d *schema.ResourceData, meta in } func resourceArmApiManagementProductRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductsClient + client := meta.(*ArmClient).apiManagement.ProductsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -180,10 +183,10 @@ func resourceArmApiManagementProductRead(d *schema.ResourceData, meta interface{ } func resourceArmApiManagementProductDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductsClient + client := meta.(*ArmClient).apiManagement.ProductsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_product_api.go b/azurerm/resource_arm_api_management_product_api.go index 68b8ebd263ee..2055e13c5c56 100644 --- a/azurerm/resource_arm_api_management_product_api.go +++ b/azurerm/resource_arm_api_management_product_api.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func resourceArmApiManagementProductApi() *schema.Resource { } func resourceArmApiManagementProductApiCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductApisClient + client := meta.(*ArmClient).apiManagement.ProductApisClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -40,7 +41,7 @@ func resourceArmApiManagementProductApiCreate(d *schema.ResourceData, meta inter apiName := d.Get("api_name").(string) productId := d.Get("product_id").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, apiName) if err != nil { if !utils.ResponseWasNotFound(resp) { @@ -67,10 +68,10 @@ func resourceArmApiManagementProductApiCreate(d *schema.ResourceData, meta inter } func resourceArmApiManagementProductApiRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductApisClient + client := meta.(*ArmClient).apiManagement.ProductApisClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -99,10 +100,10 @@ func resourceArmApiManagementProductApiRead(d *schema.ResourceData, meta interfa } func resourceArmApiManagementProductApiDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductApisClient + client := meta.(*ArmClient).apiManagement.ProductApisClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_product_api_test.go b/azurerm/resource_arm_api_management_product_api_test.go index d068bdb71c13..cc19aa3cb51a 100644 --- a/azurerm/resource_arm_api_management_product_api_test.go +++ b/azurerm/resource_arm_api_management_product_api_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMAPIManagementProductApi_basic(t *testing.T) { } func TestAccAzureRMAPIManagementProductApi_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -65,7 +66,7 @@ func TestAccAzureRMAPIManagementProductApi_requiresImport(t *testing.T) { } func testCheckAzureRMAPIManagementProductApiDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.ProductApisClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ProductApisClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_product_api" { continue @@ -101,14 +102,14 @@ func testCheckAzureRMAPIManagementProductApiExists(resourceName string) resource resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.ProductApisClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ProductApisClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, apiName) if err != nil { if utils.ResponseWasNotFound(resp) { return fmt.Errorf("Bad: API %q / Product %q (API Management Service %q / Resource Group %q) does not exist", apiName, productId, serviceName, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.ProductApisClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.ProductApisClient: %+v", err) } return nil @@ -129,10 +130,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -145,7 +143,6 @@ resource "azurerm_api_management_product" "test" { published = true } - resource "azurerm_api_management_api" "test" { name = "acctestapi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -157,10 +154,10 @@ resource "azurerm_api_management_api" "test" { } resource "azurerm_api_management_product_api" "test" { - product_id = "${azurerm_api_management_product.test.product_id}" - api_name = "${azurerm_api_management_api.test.name}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" + product_id = "${azurerm_api_management_product.test.product_id}" + api_name = "${azurerm_api_management_api.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" } `, rInt, location, rInt, rInt) } diff --git a/azurerm/resource_arm_api_management_product_group.go b/azurerm/resource_arm_api_management_product_group.go index b5f46e6ad2c7..86a130dc5c27 100644 --- a/azurerm/resource_arm_api_management_product_group.go +++ b/azurerm/resource_arm_api_management_product_group.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func resourceArmApiManagementProductGroup() *schema.Resource { } func resourceArmApiManagementProductGroupCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductGroupsClient + client := meta.(*ArmClient).apiManagement.ProductGroupsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -40,7 +41,7 @@ func resourceArmApiManagementProductGroupCreate(d *schema.ResourceData, meta int groupName := d.Get("group_name").(string) productId := d.Get("product_id").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, groupName) if err != nil { if !utils.ResponseWasNotFound(resp) { @@ -67,10 +68,10 @@ func resourceArmApiManagementProductGroupCreate(d *schema.ResourceData, meta int } func resourceArmApiManagementProductGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductGroupsClient + client := meta.(*ArmClient).apiManagement.ProductGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -99,10 +100,10 @@ func resourceArmApiManagementProductGroupRead(d *schema.ResourceData, meta inter } func resourceArmApiManagementProductGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductGroupsClient + client := meta.(*ArmClient).apiManagement.ProductGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_product_group_test.go b/azurerm/resource_arm_api_management_product_group_test.go index 3a0f6f7e81dc..eeac2e488586 100644 --- a/azurerm/resource_arm_api_management_product_group_test.go +++ b/azurerm/resource_arm_api_management_product_group_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMAPIManagementProductGroup_basic(t *testing.T) { } func TestAccAzureRMAPIManagementProductGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -65,7 +66,7 @@ func TestAccAzureRMAPIManagementProductGroup_requiresImport(t *testing.T) { } func testCheckAzureRMAPIManagementProductGroupDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.ProductGroupsClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ProductGroupsClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_product_group" { continue @@ -101,14 +102,14 @@ func testCheckAzureRMAPIManagementProductGroupExists(resourceName string) resour resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.ProductGroupsClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.ProductGroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, groupName) if err != nil { if utils.ResponseWasNotFound(resp) { return fmt.Errorf("Bad: Product %q / Group %q (API Management Service %q / Resource Group %q) does not exist", productId, groupName, serviceName, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.ProductGroupsClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.ProductGroupsClient: %+v", err) } return nil @@ -129,10 +130,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -153,10 +151,10 @@ resource "azurerm_api_management_group" "test" { } resource "azurerm_api_management_product_group" "test" { - product_id = "${azurerm_api_management_product.test.product_id}" - group_name = "${azurerm_api_management_group.test.name}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" + product_id = "${azurerm_api_management_product.test.product_id}" + group_name = "${azurerm_api_management_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" } `, rInt, location, rInt, rInt) } @@ -167,10 +165,10 @@ func testAccAzureRMAPIManagementProductGroup_requiresImport(rInt int, location s %s resource "azurerm_api_management_product_group" "import" { - product_id = "${azurerm_api_management_product_group.test.product_id}" - group_name = "${azurerm_api_management_product_group.test.group_name}" - api_management_name = "${azurerm_api_management_product_group.test.api_management_name}" - resource_group_name = "${azurerm_api_management_product_group.test.resource_group_name}" + product_id = "${azurerm_api_management_product_group.test.product_id}" + group_name = "${azurerm_api_management_product_group.test.group_name}" + api_management_name = "${azurerm_api_management_product_group.test.api_management_name}" + resource_group_name = "${azurerm_api_management_product_group.test.resource_group_name}" } `, template) } diff --git a/azurerm/resource_arm_api_management_product_policy.go b/azurerm/resource_arm_api_management_product_policy.go index c30cb62d3a5f..26267d8285d3 100644 --- a/azurerm/resource_arm_api_management_product_policy.go +++ b/azurerm/resource_arm_api_management_product_policy.go @@ -9,6 +9,7 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -47,14 +48,14 @@ func resourceArmApiManagementProductPolicy() *schema.Resource { } func resourceArmApiManagementProductPolicyCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductPoliciesClient + client := meta.(*ArmClient).apiManagement.ProductPoliciesClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) productID := d.Get("product_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, productID) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -107,10 +108,10 @@ func resourceArmApiManagementProductPolicyCreateUpdate(d *schema.ResourceData, m } func resourceArmApiManagementProductPolicyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductPoliciesClient + client := meta.(*ArmClient).apiManagement.ProductPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -143,10 +144,10 @@ func resourceArmApiManagementProductPolicyRead(d *schema.ResourceData, meta inte } func resourceArmApiManagementProductPolicyDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.ProductPoliciesClient + client := meta.(*ArmClient).apiManagement.ProductPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_product_policy_test.go b/azurerm/resource_arm_api_management_product_policy_test.go index ca1f3ea541e7..0af7e1b6384c 100644 --- a/azurerm/resource_arm_api_management_product_policy_test.go +++ b/azurerm/resource_arm_api_management_product_policy_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMApiManagementProductPolicy_basic(t *testing.T) { } func TestAccAzureRMApiManagementProductPolicy_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -109,7 +110,7 @@ func testCheckAzureRMApiManagementProductPolicyExists(resourceName string) resou serviceName := rs.Primary.Attributes["api_management_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ProductPoliciesClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ProductPoliciesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, serviceName, productID) if err != nil { @@ -117,7 +118,7 @@ func testCheckAzureRMApiManagementProductPolicyExists(resourceName string) resou return fmt.Errorf("Bad: Product Policy (API Management Service %q / Product %q/ Resource Group %q) does not exist", serviceName, productID, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.ProductPoliciesClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.ProductPoliciesClient: %+v", err) } return nil @@ -125,7 +126,7 @@ func testCheckAzureRMApiManagementProductPolicyExists(resourceName string) resou } func testCheckAzureRMApiManagementProductPolicyDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ProductPoliciesClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ProductPoliciesClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_product_policy" { @@ -181,10 +182,10 @@ resource "azurerm_api_management_product" "test" { } resource "azurerm_api_management_product_policy" "test" { - product_id = "${azurerm_api_management_product.test.product_id}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - xml_link = "https://gist.githubusercontent.com/tombuildsstuff/4f58581599d2c9f64b236f505a361a67/raw/0d29dcb0167af1e5afe4bd52a6d7f69ba1e05e1f/example.xml" + product_id = "${azurerm_api_management_product.test.product_id}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + xml_link = "https://gist.githubusercontent.com/tombuildsstuff/4f58581599d2c9f64b236f505a361a67/raw/0d29dcb0167af1e5afe4bd52a6d7f69ba1e05e1f/example.xml" } `, rInt, location, rInt) } @@ -195,10 +196,10 @@ func testAccAzureRMApiManagementProductPolicy_requiresImport(rInt int, location %s resource "azurerm_api_management_product_policy" "import" { - product_id = "${azurerm_api_management_product_policy.test.product_id}" - api_management_name = "${azurerm_api_management_product_policy.test.api_management_name}" - resource_group_name = "${azurerm_api_management_product_policy.test.resource_group_name}" - xml_link = "${azurerm_api_management_product_policy.test.xml_link}" + product_id = "${azurerm_api_management_product_policy.test.product_id}" + api_management_name = "${azurerm_api_management_product_policy.test.api_management_name}" + resource_group_name = "${azurerm_api_management_product_policy.test.resource_group_name}" + xml_link = "${azurerm_api_management_product_policy.test.xml_link}" } `, template) } @@ -233,9 +234,10 @@ resource "azurerm_api_management_product" "test" { } resource "azurerm_api_management_product_policy" "test" { - product_id = "${azurerm_api_management_product.test.product_id}" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" + product_id = "${azurerm_api_management_product.test.product_id}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + xml_content = < diff --git a/azurerm/resource_arm_api_management_product_test.go b/azurerm/resource_arm_api_management_product_test.go index b8eb4de3ffc6..604851b009a4 100644 --- a/azurerm/resource_arm_api_management_product_test.go +++ b/azurerm/resource_arm_api_management_product_test.go @@ -2,11 +2,13 @@ package azurerm import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,7 +45,7 @@ func TestAccAzureRMApiManagementProduct_basic(t *testing.T) { } func TestAccAzureRMApiManagementProduct_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -72,7 +74,7 @@ func TestAccAzureRMApiManagementProduct_requiresImport(t *testing.T) { } func testCheckAzureRMApiManagementProductDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ProductsClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ProductsClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_product" { @@ -221,6 +223,26 @@ func TestAccAzureRMApiManagementProduct_complete(t *testing.T) { }) } +func TestAccAzureRMApiManagementProduct_approvalRequiredError(t *testing.T) { + resourceName := "azurerm_api_management_product.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementProductDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementProduct_approvalRequiredError(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementProductExists(resourceName)), + ExpectError: regexp.MustCompile("`subscription_required` must be true and `subscriptions_limit` must be greater than 0 to use `approval_required`"), + }, + }, + }) +} + func testCheckAzureRMApiManagementProductExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -233,7 +255,7 @@ func testCheckAzureRMApiManagementProductExists(resourceName string) resource.Te serviceName := rs.Primary.Attributes["api_management_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ProductsClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ProductsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, serviceName, productId) if err != nil { @@ -241,7 +263,7 @@ func testCheckAzureRMApiManagementProductExists(resourceName string) resource.Te return fmt.Errorf("Bad: Product %q (API Management Service %q / Resource Group %q) does not exist", productId, serviceName, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.ProductsClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.ProductsClient: %+v", err) } return nil @@ -262,10 +284,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -310,10 +329,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -323,6 +339,7 @@ resource "azurerm_api_management_product" "test" { display_name = "Test Updated Product" subscription_required = true approval_required = true + subscriptions_limit = 1 published = true } `, rInt, location, rInt) @@ -342,10 +359,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -368,6 +382,38 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku_name = "Developer_1" +} + +resource "azurerm_api_management_product" "test" { + product_id = "test-product" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + display_name = "Test Product" + subscription_required = true + approval_required = true + published = true + subscriptions_limit = 2 + description = "This is an example description" + terms = "These are some example terms and conditions" +} +`, rInt, location, rInt) +} + +func testAccAzureRMApiManagementProduct_approvalRequiredError(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_api_management" "test" { name = "acctestAM-%d" location = "${azurerm_resource_group.test.location}" @@ -386,10 +432,9 @@ resource "azurerm_api_management_product" "test" { api_management_name = "${azurerm_api_management.test.name}" resource_group_name = "${azurerm_resource_group.test.name}" display_name = "Test Product" - subscription_required = true approval_required = true + subscription_required = false published = true - subscriptions_limit = 2 description = "This is an example description" terms = "These are some example terms and conditions" } diff --git a/azurerm/resource_arm_api_management_property.go b/azurerm/resource_arm_api_management_property.go index 6a5de9c5adc7..f3167fdeaaeb 100644 --- a/azurerm/resource_arm_api_management_property.go +++ b/azurerm/resource_arm_api_management_property.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -59,14 +60,14 @@ func resourceArmApiManagementProperty() *schema.Resource { } func resourceArmApiManagementPropertyCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.PropertyClient + client := meta.(*ArmClient).apiManagement.PropertyClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) serviceName := d.Get("api_management_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -108,10 +109,10 @@ func resourceArmApiManagementPropertyCreateUpdate(d *schema.ResourceData, meta i } func resourceArmApiManagementPropertyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.PropertyClient + client := meta.(*ArmClient).apiManagement.PropertyClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -145,10 +146,10 @@ func resourceArmApiManagementPropertyRead(d *schema.ResourceData, meta interface } func resourceArmApiManagementPropertyDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.PropertyClient + client := meta.(*ArmClient).apiManagement.PropertyClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_property_test.go b/azurerm/resource_arm_api_management_property_test.go index 8b8ceefe936a..964523264369 100644 --- a/azurerm/resource_arm_api_management_property_test.go +++ b/azurerm/resource_arm_api_management_property_test.go @@ -81,7 +81,7 @@ func TestAccAzureRMAPIManagementProperty_update(t *testing.T) { } func testCheckAzureRMAPIManagementPropertyDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.PropertyClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.PropertyClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_property" { continue @@ -116,14 +116,14 @@ func testCheckAzureRMAPIManagementPropertyExists(resourceName string) resource.T resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.PropertyClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.PropertyClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: API Management Property %q (Resource Group %q / API Management Service %q) does not exist", name, resourceGroup, serviceName) } - return fmt.Errorf("Bad: Get on apimgmt.PropertyClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.PropertyClient: %+v", err) } return nil @@ -148,10 +148,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_property" "test" { @@ -179,10 +176,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_property" "test" { diff --git a/azurerm/resource_arm_api_management_subscription.go b/azurerm/resource_arm_api_management_subscription.go index 70f080a45a8e..efd49fd7ca5c 100644 --- a/azurerm/resource_arm_api_management_subscription.go +++ b/azurerm/resource_arm_api_management_subscription.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -79,7 +80,7 @@ func resourceArmApiManagementSubscription() *schema.Resource { } func resourceArmApiManagementSubscriptionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.SubscriptionsClient + client := meta.(*ArmClient).apiManagement.SubscriptionsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) @@ -89,7 +90,7 @@ func resourceArmApiManagementSubscriptionCreateUpdate(d *schema.ResourceData, me subscriptionId = uuid.NewV4().String() } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { resp, err := client.Get(ctx, resourceGroup, serviceName, subscriptionId) if err != nil { if !utils.ResponseWasNotFound(resp.Response) { @@ -141,10 +142,10 @@ func resourceArmApiManagementSubscriptionCreateUpdate(d *schema.ResourceData, me } func resourceArmApiManagementSubscriptionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.SubscriptionsClient + client := meta.(*ArmClient).apiManagement.SubscriptionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -180,10 +181,10 @@ func resourceArmApiManagementSubscriptionRead(d *schema.ResourceData, meta inter } func resourceArmApiManagementSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apimgmt.SubscriptionsClient + client := meta.(*ArmClient).apiManagement.SubscriptionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_api_management_subscription_test.go b/azurerm/resource_arm_api_management_subscription_test.go index a5ffcefb933f..26ca2ab251cf 100644 --- a/azurerm/resource_arm_api_management_subscription_test.go +++ b/azurerm/resource_arm_api_management_subscription_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -39,7 +40,7 @@ func TestAccAzureRMAPIManagementSubscription_basic(t *testing.T) { } func TestAccAzureRMAPIManagementSubscription_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -145,7 +146,7 @@ func TestAccAzureRMAPIManagementSubscription_complete(t *testing.T) { } func testCheckAzureRMAPIManagementSubscriptionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).apimgmt.SubscriptionsClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.SubscriptionsClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management_subscription" { continue @@ -178,14 +179,14 @@ func testCheckAzureRMAPIManagementSubscriptionExists(resourceName string) resour resourceGroup := rs.Primary.Attributes["resource_group_name"] serviceName := rs.Primary.Attributes["api_management_name"] - client := testAccProvider.Meta().(*ArmClient).apimgmt.SubscriptionsClient + client := testAccProvider.Meta().(*ArmClient).apiManagement.SubscriptionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serviceName, subscriptionId) if err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: Subscription %q (API Management Service %q / Resource Group %q) does not exist", subscriptionId, serviceName, resourceGroup) } - return fmt.Errorf("Bad: Get on apimgmt.SubscriptionsClient: %+v", err) + return fmt.Errorf("Bad: Get on apiManagement.SubscriptionsClient: %+v", err) } return nil @@ -268,10 +269,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" } resource "azurerm_api_management_product" "test" { @@ -285,12 +283,12 @@ resource "azurerm_api_management_product" "test" { } resource "azurerm_api_management_user" "test" { - user_id = "acctestuser%d" - api_management_name = "${azurerm_api_management.test.name}" - resource_group_name = "${azurerm_resource_group.test.name}" - first_name = "Acceptance" - last_name = "Test" - email = "azure-acctest%d@example.com" + user_id = "acctestuser%d" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + first_name = "Acceptance" + last_name = "Test" + email = "azure-acctest%d@example.com" } `, rInt, location, rInt, rInt, rInt) } diff --git a/azurerm/resource_arm_api_management_test.go b/azurerm/resource_arm_api_management_test.go index 97ff1fce702d..14f790b6abd6 100644 --- a/azurerm/resource_arm_api_management_test.go +++ b/azurerm/resource_arm_api_management_test.go @@ -2,11 +2,13 @@ package azurerm import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,8 +37,52 @@ func TestAccAzureRMApiManagement_basic(t *testing.T) { }) } +// Remove in 2.0 +func TestAccAzureRMApiManagement_basicClassic(t *testing.T) { + resourceName := "azurerm_api_management.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMApiManagement_basicClassic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMApiManagement_basicNotDefined(t *testing.T) { + ri := tf.AccRandTimeInt() + config := testAccAzureRMApiManagement_basicNotDefined(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApiManagementDestroy, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile("either 'sku_name' or 'sku' must be defined in the configuration file"), + }, + }, + }) +} + func TestAccAzureRMApiManagement_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -92,7 +138,7 @@ func TestAccAzureRMApiManagement_customProps(t *testing.T) { func TestAccAzureRMApiManagement_complete(t *testing.T) { resourceName := "azurerm_api_management.test" ri := tf.AccRandTimeInt() - config := testAccAzureRMApiManagement_complete(ri, testLocation(), testAltLocation()) + config := testAccAzureRMApiManagement_complete(ri, testLocation(), testAltLocation(), testAltLocation2()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -178,10 +224,12 @@ func TestAccAzureRMApiManagement_policy(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"policy.0.xml_link"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "policy.0.xml_link", + }, }, { Config: testAccAzureRMApiManagement_policyRemoved(ri, location), @@ -199,7 +247,7 @@ func TestAccAzureRMApiManagement_policy(t *testing.T) { } func testCheckAzureRMApiManagementDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ServiceClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ServiceClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_api_management" { @@ -239,7 +287,7 @@ func testCheckAzureRMApiManagementExists(resourceName string) resource.TestCheck return fmt.Errorf("Bad: no resource group found in state for Api Management: %s", apiMangementName) } - conn := testAccProvider.Meta().(*ArmClient).apimgmt.ServiceClient + conn := testAccProvider.Meta().(*ArmClient).apiManagement.ServiceClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, apiMangementName) if err != nil { @@ -261,6 +309,26 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku_name = "Developer_1" +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMApiManagement_basicClassic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_api_management" "test" { name = "acctestAM-%d" location = "${azurerm_resource_group.test.location}" @@ -269,9 +337,27 @@ resource "azurerm_api_management" "test" { publisher_email = "pub1@email.com" sku { - name = "Developer" + name = "Developer" capacity = 1 - } + } +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMApiManagement_basicNotDefined(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" } `, rInt, location, rInt) } @@ -290,10 +376,7 @@ resource "azurerm_api_management" "test" { publisher_name = "pub1" publisher_email = "pub1@email.com" - sku { - name = "Developer" - capacity = 1 - } + sku_name = "Developer_1" policy { xml_content = <= i { - existingCerts := existingVals[i].(map[string]interface{}) - if data := existingCerts["data"]; data != nil { - output["data"] = data.(string) - } + results = append(results, output) + } + + return results +} + +func flattenApplicationGatewayTrustedRootCertificates(certs *[]network.ApplicationGatewayTrustedRootCertificate, d *schema.ResourceData) []interface{} { + results := make([]interface{}, 0) + if certs == nil { + return results + } + + // since the certificate data isn't returned lets load any existing data + nameToDataMap := map[string]string{} + if existing, ok := d.GetOk("trusted_root_certificate"); ok && existing != nil { + for _, c := range existing.([]interface{}) { + b := c.(map[string]interface{}) + nameToDataMap[b["name"].(string)] = b["data"].(string) + } + } + + for _, cert := range *certs { + output := map[string]interface{}{} + + if v := cert.ID; v != nil { + output["id"] = *v + } + + /*kvsid := "" + if props := cert.ApplicationGatewayTrustedRootCertificatePropertiesFormat; props != nil { + if v := props.KeyVaultSecretID; v != nil { + kvsid = *v + output["key_vault_secret_id"] = *v + } + }*/ + + if v := cert.Name; v != nil { + output["name"] = *v + + // if theres no key vauld ID and we have a name, so try and look up the old data to pass it along + if data, ok := nameToDataMap[*v]; ok && data != "" { + output["data"] = data } } @@ -1864,7 +2067,7 @@ func flattenApplicationGatewayBackendHTTPSettings(input *[]network.ApplicationGa continue } - certId, err := parseAzureResourceID(*cert.ID) + certId, err := azure.ParseAzureResourceID(*cert.ID) if err != nil { return nil, err } @@ -1881,7 +2084,7 @@ func flattenApplicationGatewayBackendHTTPSettings(input *[]network.ApplicationGa if probe := props.Probe; probe != nil { if probe.ID != nil { - id, err := parseAzureResourceID(*probe.ID) + id, err := azure.ParseAzureResourceID(*probe.ID) if err != nil { return results, err } @@ -2103,7 +2306,7 @@ func flattenApplicationGatewayHTTPListeners(input *[]network.ApplicationGatewayH if props := v.ApplicationGatewayHTTPListenerPropertiesFormat; props != nil { if port := props.FrontendPort; port != nil { if port.ID != nil { - portId, err := parseAzureResourceID(*port.ID) + portId, err := azure.ParseAzureResourceID(*port.ID) if err != nil { return nil, err } @@ -2115,7 +2318,7 @@ func flattenApplicationGatewayHTTPListeners(input *[]network.ApplicationGatewayH if feConfig := props.FrontendIPConfiguration; feConfig != nil { if feConfig.ID != nil { - feConfigId, err := parseAzureResourceID(*feConfig.ID) + feConfigId, err := azure.ParseAzureResourceID(*feConfig.ID) if err != nil { return nil, err } @@ -2133,7 +2336,7 @@ func flattenApplicationGatewayHTTPListeners(input *[]network.ApplicationGatewayH if cert := props.SslCertificate; cert != nil { if cert.ID != nil { - certId, err := parseAzureResourceID(*cert.ID) + certId, err := azure.ParseAzureResourceID(*cert.ID) if err != nil { return nil, err } @@ -2186,7 +2389,7 @@ func expandApplicationGatewayIPConfigurations(d *schema.ResourceData) (*[]networ // If we're creating the application gateway return the current gateway ip configuration. if len(oldVS) == 0 { - return &results, stopApplicationGateway + return &results, false } // The application gateway needs to be stopped if a gateway ip configuration is added or removed @@ -2579,7 +2782,6 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa for _, config := range *input { if props := config.ApplicationGatewayRequestRoutingRulePropertiesFormat; props != nil { - output := map[string]interface{}{ "rule_type": string(props.RuleType), } @@ -2594,7 +2796,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if pool := props.BackendAddressPool; pool != nil { if pool.ID != nil { - poolId, err := parseAzureResourceID(*pool.ID) + poolId, err := azure.ParseAzureResourceID(*pool.ID) if err != nil { return nil, err } @@ -2606,7 +2808,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if settings := props.BackendHTTPSettings; settings != nil { if settings.ID != nil { - settingsId, err := parseAzureResourceID(*settings.ID) + settingsId, err := azure.ParseAzureResourceID(*settings.ID) if err != nil { return nil, err } @@ -2618,7 +2820,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if listener := props.HTTPListener; listener != nil { if listener.ID != nil { - listenerId, err := parseAzureResourceID(*listener.ID) + listenerId, err := azure.ParseAzureResourceID(*listener.ID) if err != nil { return nil, err } @@ -2630,7 +2832,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if pathMap := props.URLPathMap; pathMap != nil { if pathMap.ID != nil { - pathMapId, err := parseAzureResourceID(*pathMap.ID) + pathMapId, err := azure.ParseAzureResourceID(*pathMap.ID) if err != nil { return nil, err } @@ -2642,7 +2844,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if redirect := props.RedirectConfiguration; redirect != nil { if redirect.ID != nil { - redirectId, err := parseAzureResourceID(*redirect.ID) + redirectId, err := azure.ParseAzureResourceID(*redirect.ID) if err != nil { return nil, err } @@ -2654,7 +2856,7 @@ func flattenApplicationGatewayRequestRoutingRules(input *[]network.ApplicationGa if rewrite := props.RewriteRuleSet; rewrite != nil { if rewrite.ID != nil { - rewriteId, err := parseAzureResourceID(*rewrite.ID) + rewriteId, err := azure.ParseAzureResourceID(*rewrite.ID) if err != nil { return nil, err } @@ -2750,7 +2952,6 @@ func flattenApplicationGatewayRewriteRuleSets(input *[]network.ApplicationGatewa for _, config := range *input { if props := config.ApplicationGatewayRewriteRuleSetPropertiesFormat; props != nil { - output := map[string]interface{}{} if config.ID != nil { @@ -2764,7 +2965,6 @@ func flattenApplicationGatewayRewriteRuleSets(input *[]network.ApplicationGatewa if rulesConfig := props.RewriteRules; rulesConfig != nil { rules := make([]interface{}, 0) for _, rule := range *rulesConfig { - ruleOutput := map[string]interface{}{} if rule.Name != nil { @@ -2853,7 +3053,6 @@ func flattenApplicationGatewayRewriteRuleSets(input *[]network.ApplicationGatewa } func expandApplicationGatewayRedirectConfigurations(d *schema.ResourceData, gatewayID string) (*[]network.ApplicationGatewayRedirectConfiguration, error) { - vs := d.Get("redirect_configuration").([]interface{}) results := make([]network.ApplicationGatewayRedirectConfiguration, 0) @@ -2913,7 +3112,6 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio for _, config := range *input { if props := config.ApplicationGatewayRedirectConfigurationPropertiesFormat; props != nil { - output := map[string]interface{}{ "redirect_type": string(props.RedirectType), } @@ -2928,7 +3126,7 @@ func flattenApplicationGatewayRedirectConfigurations(input *[]network.Applicatio if listener := props.TargetListener; listener != nil { if listener.ID != nil { - listenerId, err := parseAzureResourceID(*listener.ID) + listenerId, err := azure.ParseAzureResourceID(*listener.ID) if err != nil { return nil, err } @@ -3037,7 +3235,7 @@ func expandApplicationGatewaySslCertificates(d *schema.ResourceData) *[]network. password := v["password"].(string) // data must be base64 encoded - data = base64Encode(data) + data = utils.Base64EncodeIfNot(data) output := network.ApplicationGatewaySslCertificate{ Name: utils.String(name), @@ -3088,7 +3286,7 @@ func flattenApplicationGatewaySslCertificates(input *[]network.ApplicationGatewa if name == existingName { if data := existingCerts["data"]; data != nil { - v := base64Encode(data.(string)) + v := utils.Base64EncodeIfNot(data.(string)) output["data"] = v } @@ -3246,7 +3444,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL if props := v.ApplicationGatewayURLPathMapPropertiesFormat; props != nil { if backendPool := props.DefaultBackendAddressPool; backendPool != nil && backendPool.ID != nil { - poolId, err := parseAzureResourceID(*backendPool.ID) + poolId, err := azure.ParseAzureResourceID(*backendPool.ID) if err != nil { return nil, err } @@ -3256,7 +3454,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if settings := props.DefaultBackendHTTPSettings; settings != nil && settings.ID != nil { - settingsId, err := parseAzureResourceID(*settings.ID) + settingsId, err := azure.ParseAzureResourceID(*settings.ID) if err != nil { return nil, err } @@ -3266,7 +3464,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if redirect := props.DefaultRedirectConfiguration; redirect != nil && redirect.ID != nil { - settingsId, err := parseAzureResourceID(*redirect.ID) + settingsId, err := azure.ParseAzureResourceID(*redirect.ID) if err != nil { return nil, err } @@ -3276,7 +3474,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if rewrite := props.DefaultRewriteRuleSet; rewrite != nil && rewrite.ID != nil { - settingsId, err := parseAzureResourceID(*rewrite.ID) + settingsId, err := azure.ParseAzureResourceID(*rewrite.ID) if err != nil { return nil, err } @@ -3300,7 +3498,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL if ruleProps := rule.ApplicationGatewayPathRulePropertiesFormat; ruleProps != nil { if pool := ruleProps.BackendAddressPool; pool != nil && pool.ID != nil { - poolId, err := parseAzureResourceID(*pool.ID) + poolId, err := azure.ParseAzureResourceID(*pool.ID) if err != nil { return nil, err } @@ -3310,7 +3508,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if backend := ruleProps.BackendHTTPSettings; backend != nil && backend.ID != nil { - backendId, err := parseAzureResourceID(*backend.ID) + backendId, err := azure.ParseAzureResourceID(*backend.ID) if err != nil { return nil, err } @@ -3320,7 +3518,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if redirect := ruleProps.RedirectConfiguration; redirect != nil && redirect.ID != nil { - redirectId, err := parseAzureResourceID(*redirect.ID) + redirectId, err := azure.ParseAzureResourceID(*redirect.ID) if err != nil { return nil, err } @@ -3330,7 +3528,7 @@ func flattenApplicationGatewayURLPathMaps(input *[]network.ApplicationGatewayURL } if rewrite := ruleProps.RewriteRuleSet; rewrite != nil && rewrite.ID != nil { - rewriteId, err := parseAzureResourceID(*rewrite.ID) + rewriteId, err := azure.ParseAzureResourceID(*rewrite.ID) if err != nil { return nil, err } @@ -3478,7 +3676,6 @@ func flattenApplicationGateWayDisabledRuleGroups(input *[]network.ApplicationGat ruleGroupOutput["rules"] = ruleOutputs ruleGroups = append(ruleGroups, ruleGroupOutput) - } return ruleGroups } @@ -3525,7 +3722,6 @@ func flattenApplicationGatewayFirewallExclusion(input *[]network.ApplicationGate exclusionListOutput["selector"] = *exclusionList.Selector } exclusionLists = append(exclusionLists, exclusionListOutput) - } return exclusionLists } diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index aeef2e5bb7d6..e7dfd9528404 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -5,9 +5,11 @@ import ( "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -187,7 +189,7 @@ func TestAccAzureRMApplicationGateway_http2(t *testing.T) { } func TestAccAzureRMApplicationGateway_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -261,6 +263,81 @@ func TestAccAzureRMApplicationGateway_authCertificate(t *testing.T) { }) } +// TODO required soft delete on the keyvault +func TestAccAzureRMApplicationGateway_trustedRootCertificate_keyvault(t *testing.T) { + t.Skip() + + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_trustedRootCertificate_keyvault(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "trusted_root_certificate.0.name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApplicationGateway_trustedRootCertificate(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_trustedRootCertificate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "trusted_root_certificate.0.name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // since these are read from the existing state + "trusted_root_certificate.0.data", + }, + }, + { + Config: testAccAzureRMApplicationGateway_trustedRootCertificateUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "trusted_root_certificate.0.name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // since these are read from the existing state + "trusted_root_certificate.0.data", + }, + }, + }, + }) +} + func TestAccAzureRMApplicationGateway_pathBasedRouting(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -790,6 +867,7 @@ func TestAccAzureRMApplicationGateway_webApplicationFirewall_exclusions(t *testi }, }) } + func TestAccAzureRMApplicationGateway_sslPolicy_policyType_predefined(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -834,6 +912,7 @@ func TestAccAzureRMApplicationGateway_sslPolicy_policyType_custom(t *testing.T) }, }) } + func TestAccAzureRMApplicationGateway_sslPolicy_disabledProtocols(t *testing.T) { resourceName := "azurerm_application_gateway.test" ri := tf.AccRandTimeInt() @@ -948,6 +1027,28 @@ func TestAccAzureRMApplicationGateway_gatewayIP(t *testing.T) { }) } +func TestAccAzureRMApplicationGateway_UserAssignedIdentity(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(14) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_UserDefinedIdentity(ri, testLocation(), rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "UserAssigned"), + resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"), + ), + }, + }, + }) +} + func testCheckAzureRMApplicationGatewayExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -961,7 +1062,7 @@ func testCheckAzureRMApplicationGatewayExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Application Gateway: %q", gatewayName) } - client := testAccProvider.Meta().(*ArmClient).applicationGatewayClient + client := testAccProvider.Meta().(*ArmClient).network.ApplicationGatewaysClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, gatewayName) @@ -978,7 +1079,7 @@ func testCheckAzureRMApplicationGatewayExists(resourceName string) resource.Test } func testCheckAzureRMApplicationGatewayDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).applicationGatewayClient + client := testAccProvider.Meta().(*ArmClient).network.ApplicationGatewaysClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -1076,6 +1177,96 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } +func testAccAzureRMApplicationGateway_UserDefinedIdentity(rInt int, location string, rString string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + +resource "azurerm_user_assigned_identity" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + name = "acctest%s" +} + +resource "azurerm_public_ip" "test_standard" { + name = "acctest-pubip-%d-standard" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + allocation_method = "Static" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "Standard_v2" + tier = "Standard_v2" + capacity = 1 + } + + identity { + identity_ids = ["${azurerm_user_assigned_identity.test.id}"] + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rString, rInt, rInt) +} + func testAccAzureRMApplicationGateway_zones(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` @@ -1254,26 +1445,231 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } -resource "azurerm_public_ip" "test_standard" { - name = "acctest-pubip-%d-standard" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - sku = "Standard" - allocation_method = "Static" -} +resource "azurerm_public_ip" "test_standard" { + name = "acctest-pubip-%d-standard" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + allocation_method = "Static" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "Standard_v2" + tier = "Standard_v2" + } + + autoscale_configuration { + min_capacity = 2 + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMApplicationGateway_overridePath(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "Standard_Small" + tier = "Standard" + capacity = 2 + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + path = "/path1/" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rInt) +} + +func testAccAzureRMApplicationGateway_http2(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + enable_http2 = true + + sku { + name = "Standard_Small" + tier = "Standard" + capacity = 2 + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rInt) +} + +func testAccAzureRMApplicationGateway_requiresImport(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_basic(rInt, location) + return fmt.Sprintf(` +%s -resource "azurerm_application_gateway" "test" { - name = "acctestag-%d" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" +resource "azurerm_application_gateway" "import" { + name = "${azurerm_application_gateway.test.name}" + resource_group_name = "${azurerm_application_gateway.test.resource_group_name}" + location = "${azurerm_application_gateway.test.location}" sku { - name = "Standard_v2" - tier = "Standard_v2" - } - - autoscale_configuration { - min_capacity = 2 + name = "Standard_Small" + tier = "Standard" + capacity = 2 } gateway_ip_configuration { @@ -1288,7 +1684,7 @@ resource "azurerm_application_gateway" "test" { frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" - public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + public_ip_address_id = "${azurerm_public_ip.test.id}" } backend_address_pool { @@ -1318,16 +1714,17 @@ resource "azurerm_application_gateway" "test" { backend_http_settings_name = "${local.http_setting_name}" } } -`, template, rInt, rInt) +`, template) } -func testAccAzureRMApplicationGateway_overridePath(rInt int, location string) string { +func testAccAzureRMApplicationGateway_authCertificate(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` %s # since these variables are re-used - a locals block makes this more maintainable locals { + auth_cert_name = "${azurerm_virtual_network.test.name}-auth" backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" frontend_port_name = "${azurerm_virtual_network.test.name}-feport" frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" @@ -1369,10 +1766,18 @@ resource "azurerm_application_gateway" "test" { backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" - path = "/path1/" - port = 80 - protocol = "Http" + port = 443 + protocol = "Https" request_timeout = 1 + + authentication_certificate { + name = "${local.auth_cert_name}" + } + } + + authentication_certificate { + name = "${local.auth_cert_name}" + data = "${file("testdata/application_gateway_test.cer")}" } http_listener { @@ -1393,13 +1798,14 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } -func testAccAzureRMApplicationGateway_http2(rInt int, location string) string { +func testAccAzureRMApplicationGateway_trustedRootCertificate_keyvault(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` -%s +%[1]s # since these variables are re-used - a locals block makes this more maintainable locals { + auth_cert_name = "${azurerm_virtual_network.test.name}-auth" backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" frontend_port_name = "${azurerm_virtual_network.test.name}-feport" frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" @@ -1408,15 +1814,84 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } +data "azurerm_client_config" "test" {} + +data "azuread_service_principal" "test" { + display_name = "Microsoft Azure App Service" +} + +resource "azurerm_user_assigned_identity" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + name = "acctest%[2]d" +} + +resource "azurerm_public_ip" "testStd" { + name = "acctest-PubIpStd-%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_key_vault" "test" { + name = "acct%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.test.tenant_id}" + sku_name = "standard" + + access_policy { + tenant_id = "${data.azurerm_client_config.test.tenant_id}" + object_id = "${data.azurerm_client_config.test.service_principal_object_id }" + secret_permissions = ["delete", "get", "set"] + certificate_permissions = ["create", "delete", "get", "import"] + } + + access_policy { + tenant_id = "${data.azurerm_client_config.test.tenant_id}" + object_id = "${azurerm_user_assigned_identity.test.principal_id}" + secret_permissions = ["get"] + certificate_permissions = ["get"] + } +} + +resource "azurerm_key_vault_certificate" "test" { + name = "acctest%[2]d" + key_vault_id = "${azurerm_key_vault.test.id}" + + certificate { + contents = filebase64("testdata/app_service_certificate.pfx") + password = "terraform" + } + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = false + } + + secret_properties { + content_type = "application/x-pkcs12" + } + } +} + resource "azurerm_application_gateway" "test" { - name = "acctestag-%d" + name = "acctestag-%[2]d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" - enable_http2 = true sku { - name = "Standard_Small" - tier = "Standard" + name = "WAF_v2" + tier = "WAF_v2" capacity = 2 } @@ -1425,6 +1900,10 @@ resource "azurerm_application_gateway" "test" { subnet_id = "${azurerm_subnet.test.id}" } + identity { + identity_ids = ["${azurerm_user_assigned_identity.test.id}"] + } + frontend_port { name = "${local.frontend_port_name}" port = 80 @@ -1432,7 +1911,7 @@ resource "azurerm_application_gateway" "test" { frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" - public_ip_address_id = "${azurerm_public_ip.test.id}" + public_ip_address_id = "${azurerm_public_ip.testStd.id}" } backend_address_pool { @@ -1442,11 +1921,16 @@ resource "azurerm_application_gateway" "test" { backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" - port = 80 - protocol = "Http" + port = 443 + protocol = "Https" request_timeout = 1 } + trusted_root_certificate { + name = "${local.auth_cert_name}" + key_vault_secret_id = "${azurerm_key_vault_certificate.test.secret_id}" + } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" @@ -1465,19 +1949,38 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } -func testAccAzureRMApplicationGateway_requiresImport(rInt int, location string) string { - template := testAccAzureRMApplicationGateway_basic(rInt, location) +func testAccAzureRMApplicationGateway_trustedRootCertificate(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` -%s +%[1]s -resource "azurerm_application_gateway" "import" { - name = "${azurerm_application_gateway.test.name}" - resource_group_name = "${azurerm_application_gateway.test.resource_group_name}" - location = "${azurerm_application_gateway.test.location}" +# since these variables are re-used - a locals block makes this more maintainable +locals { + auth_cert_name = "${azurerm_virtual_network.test.name}-auth" + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + +resource "azurerm_public_ip" "teststd" { + name = "acctest-PubIpStd-%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%[2]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" sku { - name = "Standard_Small" - tier = "Standard" + name = "WAF_v2" + tier = "WAF_v2" capacity = 2 } @@ -1493,7 +1996,7 @@ resource "azurerm_application_gateway" "import" { frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" - public_ip_address_id = "${azurerm_public_ip.test.id}" + public_ip_address_id = "${azurerm_public_ip.teststd.id}" } backend_address_pool { @@ -1503,11 +2006,16 @@ resource "azurerm_application_gateway" "import" { backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" - port = 80 - protocol = "Http" + port = 443 + protocol = "Https" request_timeout = 1 } + trusted_root_certificate { + name = "${local.auth_cert_name}" + data = "${file("testdata/application_gateway_test.cer")}" + } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" @@ -1523,17 +2031,17 @@ resource "azurerm_application_gateway" "import" { backend_http_settings_name = "${local.http_setting_name}" } } -`, template) +`, template, rInt) } -func testAccAzureRMApplicationGateway_authCertificate(rInt int, location string) string { +func testAccAzureRMApplicationGateway_authCertificateUpdated(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` -%s +%[1]s # since these variables are re-used - a locals block makes this more maintainable locals { - auth_cert_name = "${azurerm_virtual_network.test.name}-auth" + auth_cert_name = "${azurerm_virtual_network.test.name}-auth2" backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" frontend_port_name = "${azurerm_virtual_network.test.name}-feport" frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" @@ -1543,7 +2051,7 @@ locals { } resource "azurerm_application_gateway" "test" { - name = "acctestag-%d" + name = "acctestag-%[2]d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" @@ -1586,7 +2094,7 @@ resource "azurerm_application_gateway" "test" { authentication_certificate { name = "${local.auth_cert_name}" - data = "${file("testdata/application_gateway_test.cer")}" + data = "${file("testdata/application_gateway_test_2.crt")}" } http_listener { @@ -1607,10 +2115,10 @@ resource "azurerm_application_gateway" "test" { `, template, rInt) } -func testAccAzureRMApplicationGateway_authCertificateUpdated(rInt int, location string) string { +func testAccAzureRMApplicationGateway_trustedRootCertificateUpdated(rInt int, location string) string { template := testAccAzureRMApplicationGateway_template(rInt, location) return fmt.Sprintf(` -%s +%[1]s # since these variables are re-used - a locals block makes this more maintainable locals { @@ -1623,14 +2131,23 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } +resource "azurerm_public_ip" "teststd" { + name = "acctest-PubIpStd-%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + allocation_method = "Static" + sku = "Standard" +} + + resource "azurerm_application_gateway" "test" { - name = "acctestag-%d" + name = "acctestag-%[2]d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" sku { - name = "Standard_Small" - tier = "Standard" + name = "WAF_v2" + tier = "WAF_v2" capacity = 2 } @@ -1646,7 +2163,7 @@ resource "azurerm_application_gateway" "test" { frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" - public_ip_address_id = "${azurerm_public_ip.test.id}" + public_ip_address_id = "${azurerm_public_ip.teststd.id}" } backend_address_pool { @@ -1659,13 +2176,9 @@ resource "azurerm_application_gateway" "test" { port = 443 protocol = "Https" request_timeout = 1 - - authentication_certificate { - name = "${local.auth_cert_name}" - } } - authentication_certificate { + trusted_root_certificate { name = "${local.auth_cert_name}" data = "${file("testdata/application_gateway_test_2.crt")}" } @@ -1783,17 +2296,17 @@ func testAccAzureRMApplicationGateway_routingRedirect_httpListener(rInt int, loc # since these variables are re-used - a locals block makes this more maintainable locals { - backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" - frontend_port_name = "${azurerm_virtual_network.test.name}-feport" - frontend_port_name2 = "${azurerm_virtual_network.test.name}-feport2" - frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" - http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" - listener_name = "${azurerm_virtual_network.test.name}-httplstn" - target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" - request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" - path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" - url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" - redirect_configuration_name = "${azurerm_virtual_network.test.name}-Port80To8888Redirect" + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_port_name2 = "${azurerm_virtual_network.test.name}-feport2" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" + path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" + url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" + redirect_configuration_name = "${azurerm_virtual_network.test.name}-Port80To8888Redirect" } resource "azurerm_application_gateway" "test" { @@ -1878,17 +2391,17 @@ func testAccAzureRMApplicationGateway_routingRedirect_httpListenerError(rInt int # since these variables are re-used - a locals block makes this more maintainable locals { - backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" - frontend_port_name = "${azurerm_virtual_network.test.name}-feport" - frontend_port_name2 = "${azurerm_virtual_network.test.name}-feport2" - frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" - http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" - listener_name = "${azurerm_virtual_network.test.name}-httplstn" - target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" - request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" - path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" - url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" - redirect_configuration_name = "${azurerm_virtual_network.test.name}-Port80To8888Redirect" + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_port_name2 = "${azurerm_virtual_network.test.name}-feport2" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + target_listener_name = "${azurerm_virtual_network.test.name}-trgthttplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" + path_rule_name = "${azurerm_virtual_network.test.name}-pathrule1" + url_path_map_name = "${azurerm_virtual_network.test.name}-urlpath1" + redirect_configuration_name = "${azurerm_virtual_network.test.name}-Port80To8888Redirect" } resource "azurerm_application_gateway" "test" { @@ -2062,6 +2575,7 @@ resource "azurerm_application_gateway" "test" { path_rule { name = "${local.path_rule_name}" redirect_configuration_name = "${local.redirect_configuration_name}" + paths = [ "/test", ] @@ -2075,7 +2589,6 @@ resource "azurerm_application_gateway" "test" { "/test2", ] } - } redirect_configuration { @@ -2617,10 +3130,10 @@ resource "azurerm_application_gateway" "test" { ] waf_configuration { - enabled = true - firewall_mode = "Detection" - rule_set_type = "OWASP" - rule_set_version = "3.0" + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" file_upload_limit_mb = 100 request_body_check = true max_request_body_size_kb = 100 @@ -2724,7 +3237,7 @@ resource "azurerm_application_gateway" "test" { protocol = "Http" request_timeout = 1 - connection_draining { + connection_draining { enabled = true drain_timeout_sec = 1984 } @@ -2762,7 +3275,6 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } - resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -2783,29 +3295,27 @@ resource "azurerm_application_gateway" "test" { } waf_configuration { - enabled = true - firewall_mode = "Detection" - rule_set_type = "OWASP" - rule_set_version = "3.0" - request_body_check = true - max_request_body_size_kb = 128 - file_upload_limit_mb = 100 + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 disabled_rule_group { rule_group_name = "REQUEST-921-PROTOCOL-ATTACK" rules = [921110, 921151, 921180] } - + disabled_rule_group { rule_group_name = "REQUEST-930-APPLICATION-ATTACK-LFI" rules = [930120, 930130] } - + disabled_rule_group { rule_group_name = "REQUEST-942-APPLICATION-ATTACK-SQLI" } - - } gateway_ip_configuration { @@ -2868,7 +3378,6 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } - resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -2889,24 +3398,22 @@ resource "azurerm_application_gateway" "test" { } waf_configuration { - enabled = true - firewall_mode = "Detection" - rule_set_type = "OWASP" - rule_set_version = "3.0" - request_body_check = true - max_request_body_size_kb = 128 - file_upload_limit_mb = 100 + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 disabled_rule_group { rule_group_name = "REQUEST-921-PROTOCOL-ATTACK" rules = [921110, 921151, 921180] } - + disabled_rule_group { rule_group_name = "REQUEST-942-APPLICATION-ATTACK-SQLI" } - - } gateway_ip_configuration { @@ -2969,7 +3476,6 @@ locals { request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } - resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -2990,13 +3496,13 @@ resource "azurerm_application_gateway" "test" { } waf_configuration { - enabled = true - firewall_mode = "Detection" - rule_set_type = "OWASP" - rule_set_version = "3.0" - request_body_check = true - max_request_body_size_kb = 128 - file_upload_limit_mb = 100 + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 exclusion { match_variable = "RequestArgNames" @@ -3023,7 +3529,7 @@ resource "azurerm_application_gateway" "test" { } exclusion { - match_variable = "RequestHeaderNames" + match_variable = "RequestHeaderNames" } } @@ -3106,20 +3612,19 @@ resource "azurerm_application_gateway" "test" { } waf_configuration { - enabled = true - firewall_mode = "Detection" - rule_set_type = "OWASP" - rule_set_version = "3.0" - request_body_check = true - max_request_body_size_kb = 128 - file_upload_limit_mb = 100 + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 exclusion { match_variable = "RequestArgNames" selector_match_operator = "Equals" selector = "displayNameHtml" } - } gateway_ip_configuration { @@ -3180,6 +3685,7 @@ locals { listener_name = "${azurerm_virtual_network.test.name}-httplstn" request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } + resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -3187,34 +3693,42 @@ resource "azurerm_public_ip" "test_standard" { sku = "Standard" allocation_method = "Static" } + resource "azurerm_application_gateway" "test" { name = "acctestag-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" + sku { name = "Standard_v2" tier = "Standard_v2" capacity = 1 } + ssl_policy { policy_name = "AppGwSslPolicy20170401S" policy_type = "Predefined" } + gateway_ip_configuration { name = "my-gateway-ip-configuration" subnet_id = "${azurerm_subnet.test.id}" } + frontend_port { name = "${local.frontend_port_name}" port = 80 } + frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" public_ip_address_id = "${azurerm_public_ip.test_standard.id}" } + backend_address_pool { name = "${local.backend_address_pool_name}" } + backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" @@ -3222,12 +3736,14 @@ resource "azurerm_application_gateway" "test" { protocol = "Http" request_timeout = 1 } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" frontend_port_name = "${local.frontend_port_name}" protocol = "Http" } + request_routing_rule { name = "${local.request_routing_rule_name}" rule_type = "Basic" @@ -3252,6 +3768,7 @@ locals { listener_name = "${azurerm_virtual_network.test.name}-httplstn" request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } + resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -3259,35 +3776,43 @@ resource "azurerm_public_ip" "test_standard" { sku = "Standard" allocation_method = "Static" } + resource "azurerm_application_gateway" "test" { name = "acctestag-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" + sku { name = "Standard_v2" tier = "Standard_v2" capacity = 1 } + ssl_policy { - policy_type = "Custom" + policy_type = "Custom" min_protocol_version = "TLSv1_1" - cipher_suites = ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"] + cipher_suites = ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"] } + gateway_ip_configuration { name = "my-gateway-ip-configuration" subnet_id = "${azurerm_subnet.test.id}" } + frontend_port { name = "${local.frontend_port_name}" port = 80 } + frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" public_ip_address_id = "${azurerm_public_ip.test_standard.id}" } + backend_address_pool { name = "${local.backend_address_pool_name}" } + backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" @@ -3295,12 +3820,14 @@ resource "azurerm_application_gateway" "test" { protocol = "Http" request_timeout = 1 } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" frontend_port_name = "${local.frontend_port_name}" protocol = "Http" } + request_routing_rule { name = "${local.request_routing_rule_name}" rule_type = "Basic" @@ -3325,6 +3852,7 @@ locals { listener_name = "${azurerm_virtual_network.test.name}-httplstn" request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } + resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -3332,33 +3860,41 @@ resource "azurerm_public_ip" "test_standard" { sku = "Standard" allocation_method = "Static" } + resource "azurerm_application_gateway" "test" { name = "acctestag-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" + sku { name = "Standard_v2" tier = "Standard_v2" capacity = 1 } + ssl_policy { disabled_protocols = ["TLSv1_0", "TLSv1_1"] } + gateway_ip_configuration { name = "my-gateway-ip-configuration" subnet_id = "${azurerm_subnet.test.id}" } + frontend_port { name = "${local.frontend_port_name}" port = 80 } + frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" public_ip_address_id = "${azurerm_public_ip.test_standard.id}" } + backend_address_pool { name = "${local.backend_address_pool_name}" } + backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" @@ -3366,12 +3902,14 @@ resource "azurerm_application_gateway" "test" { protocol = "Http" request_timeout = 1 } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" frontend_port_name = "${local.frontend_port_name}" protocol = "Http" } + request_routing_rule { name = "${local.request_routing_rule_name}" rule_type = "Basic" @@ -3396,6 +3934,7 @@ locals { listener_name = "${azurerm_virtual_network.test.name}-httplstn" request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" } + resource "azurerm_public_ip" "test_standard" { name = "acctest-pubip-%d-standard" location = "${azurerm_resource_group.test.location}" @@ -3403,31 +3942,39 @@ resource "azurerm_public_ip" "test_standard" { sku = "Standard" allocation_method = "Static" } + resource "azurerm_application_gateway" "test" { name = "acctestag-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" + sku { name = "Standard_v2" tier = "Standard_v2" capacity = 1 } + disabled_ssl_protocols = ["TLSv1_0", "TLSv1_1"] + gateway_ip_configuration { name = "my-gateway-ip-configuration" subnet_id = "${azurerm_subnet.test.id}" } + frontend_port { name = "${local.frontend_port_name}" port = 80 } + frontend_ip_configuration { name = "${local.frontend_ip_configuration_name}" public_ip_address_id = "${azurerm_public_ip.test_standard.id}" } + backend_address_pool { name = "${local.backend_address_pool_name}" } + backend_http_settings { name = "${local.http_setting_name}" cookie_based_affinity = "Disabled" @@ -3435,12 +3982,14 @@ resource "azurerm_application_gateway" "test" { protocol = "Http" request_timeout = 1 } + http_listener { name = "${local.listener_name}" frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" frontend_port_name = "${local.frontend_port_name}" protocol = "Http" } + request_routing_rule { name = "${local.request_routing_rule_name}" rule_type = "Basic" @@ -3542,10 +4091,12 @@ resource "azurerm_application_gateway" "test" { frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" frontend_port_name = "${local.frontend_port_name}" protocol = "Http" + custom_error_configuration { status_code = "HttpStatus403" custom_error_page_url = "http://azure.com/error403_listener.html" } + custom_error_configuration { status_code = "HttpStatus502" custom_error_page_url = "http://azure.com/error502_listener.html" @@ -3656,16 +4207,16 @@ resource "azurerm_application_gateway" "test" { name = "${local.rewrite_rule_set_name}" rewrite_rule { - name = "${local.rewrite_rule_name}" + name = "${local.rewrite_rule_name}" rule_sequence = 1 condition { variable = "var_http_status" - pattern = "502" + pattern = "502" } request_header_configuration { - header_name = "X-custom" + header_name = "X-custom" header_value = "customvalue" } } @@ -3771,16 +4322,16 @@ resource "azurerm_application_gateway" "test" { name = "${local.rewrite_rule_set_name}" rewrite_rule { - name = "${local.rewrite_rule_name}" + name = "${local.rewrite_rule_name}" rule_sequence = 1 condition { variable = "var_http_status" - pattern = "502" + pattern = "502" } request_header_configuration { - header_name = "X-custom" + header_name = "X-custom" header_value = "customvalue" } } @@ -3952,7 +4503,6 @@ resource "azurerm_subnet" "test1" { address_prefix = "10.0.1.0/24" } - # since these variables are re-used - a locals block makes this more maintainable locals { backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" diff --git a/azurerm/resource_arm_application_insights.go b/azurerm/resource_arm_application_insights.go index 4632da12d3e0..19703628df6e 100644 --- a/azurerm/resource_arm_application_insights.go +++ b/azurerm/resource_arm_application_insights.go @@ -8,6 +8,8 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" "github.com/hashicorp/terraform/helper/schema" @@ -53,7 +55,7 @@ func resourceArmApplicationInsights() *schema.Resource { }, true), }, - "tags": tagsSchema(), + "tags": tags.Schema(), "app_id": { Type: schema.TypeString, @@ -70,7 +72,7 @@ func resourceArmApplicationInsights() *schema.Resource { } func resourceArmApplicationInsightsCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsClient + client := meta.(*ArmClient).appInsights.ComponentsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Application Insights creation.") @@ -78,7 +80,7 @@ func resourceArmApplicationInsightsCreateUpdate(d *schema.ResourceData, meta int name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -93,7 +95,7 @@ func resourceArmApplicationInsightsCreateUpdate(d *schema.ResourceData, meta int applicationType := d.Get("application_type").(string) location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) applicationInsightsComponentProperties := insights.ApplicationInsightsComponentProperties{ ApplicationID: &name, @@ -105,7 +107,7 @@ func resourceArmApplicationInsightsCreateUpdate(d *schema.ResourceData, meta int Location: &location, Kind: &applicationType, ApplicationInsightsComponentProperties: &applicationInsightsComponentProperties, - Tags: expandTags(tags), + Tags: tags.Expand(t), } resp, err := client.CreateOrUpdate(ctx, resGroup, name, insightProperties) @@ -132,10 +134,10 @@ func resourceArmApplicationInsightsCreateUpdate(d *schema.ResourceData, meta int } func resourceArmApplicationInsightsRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsClient + client := meta.(*ArmClient).appInsights.ComponentsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -166,16 +168,14 @@ func resourceArmApplicationInsightsRead(d *schema.ResourceData, meta interface{} d.Set("instrumentation_key", props.InstrumentationKey) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmApplicationInsightsDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsClient + client := meta.(*ArmClient).appInsights.ComponentsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_application_insights_analytics_item.go b/azurerm/resource_arm_application_insights_analytics_item.go new file mode 100644 index 000000000000..8e71cadcc015 --- /dev/null +++ b/azurerm/resource_arm_application_insights_analytics_item.go @@ -0,0 +1,237 @@ +package azurerm + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + + "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceArmApplicationInsightsAnalyticsItem() *schema.Resource { + return &schema.Resource{ + Create: resourceArmApplicationInsightsAnalyticsItemCreate, + Read: resourceArmApplicationInsightsAnalyticsItemRead, + Update: resourceArmApplicationInsightsAnalyticsItemUpdate, + Delete: resourceArmApplicationInsightsAnalyticsItemDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "application_insights_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "version": { + Type: schema.TypeString, + Computed: true, + }, + + "content": { + Type: schema.TypeString, + Required: true, + }, + + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.ItemScopeShared), + string(insights.ItemScopeUser), + }, false), + }, + + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.Query), + string(insights.Function), + string(insights.Folder), + string(insights.Recent), + }, false), + }, + + "function_alias": { + Type: schema.TypeString, + Optional: true, + }, + + "time_created": { + Type: schema.TypeString, + Computed: true, + }, + + "time_modified": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmApplicationInsightsAnalyticsItemCreate(d *schema.ResourceData, meta interface{}) error { + return resourceArmApplicationInsightsAnalyticsItemCreateUpdate(d, meta, false) +} +func resourceArmApplicationInsightsAnalyticsItemUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceArmApplicationInsightsAnalyticsItemCreateUpdate(d, meta, true) +} +func resourceArmApplicationInsightsAnalyticsItemCreateUpdate(d *schema.ResourceData, meta interface{}, overwrite bool) error { + client := meta.(*ArmClient).appInsights.AnalyticsItemsClient + ctx := meta.(*ArmClient).StopContext + + appInsightsID := d.Get("application_insights_id").(string) + + resourceID, err := azure.ParseAzureResourceID(appInsightsID) + if err != nil { + return fmt.Errorf("Error parsing resource ID: %s", err) + } + resourceGroupName := resourceID.ResourceGroup + appInsightsName := resourceID.Path["components"] + + id := d.Id() + itemID := "" + if id != "" { + _, _, _, itemID, err = resourcesArmApplicationInsightsAnalyticsItemParseID(id) + if err != nil { + return fmt.Errorf("Error parsing Application Insights Analytics Item ID %s: %s", id, err) + } + } + + name := d.Get("name").(string) + content := d.Get("content").(string) + scopeName := d.Get("scope").(string) + typeName := d.Get("type").(string) + functionAlias := d.Get("function_alias").(string) + + itemType := insights.ItemType(typeName) + itemScope := insights.ItemScope(scopeName) + properties := insights.ApplicationInsightsComponentAnalyticsItem{ + ID: &itemID, + Name: &name, + Type: itemType, + Scope: itemScope, + Content: &content, + } + if functionAlias != "" { + properties.Properties = &insights.ApplicationInsightsComponentAnalyticsItemProperties{ + FunctionAlias: &functionAlias, + } + } + + var itemScopePath insights.ItemScopePath + if itemScope == insights.ItemScopeUser { + itemScopePath = insights.MyanalyticsItems + } else { + itemScopePath = insights.AnalyticsItems + } + result, err := client.Put(ctx, resourceGroupName, appInsightsName, itemScopePath, properties, &overwrite) + if err != nil { + return fmt.Errorf("Error Putting Application Insights Analytics Item %s (Resource Group %s, App Insights Name: %s): %s", name, resourceGroupName, appInsightsName, err) + } + + // See comments in resourcesArmApplicationInsightsAnalyticsItemParseID method about ID format + generatedID := appInsightsID + resourcesArmApplicationInsightsAnalyticsItemGenerateIDSuffix(itemScope, *result.ID) + d.SetId(generatedID) + + return resourceArmApplicationInsightsAnalyticsItemRead(d, meta) +} + +func resourceArmApplicationInsightsAnalyticsItemRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).appInsights.AnalyticsItemsClient + ctx := meta.(*ArmClient).StopContext + + id := d.Id() + resourceGroupName, appInsightsName, itemScopePath, itemID, err := resourcesArmApplicationInsightsAnalyticsItemParseID(id) + if err != nil { + return fmt.Errorf("Error parsing Application Insights Analytics Item ID %s: %s", id, err) + } + + result, err := client.Get(ctx, resourceGroupName, appInsightsName, itemScopePath, itemID, "") + if err != nil { + return fmt.Errorf("Error Getting Application Insights Analytics Item %s (Resource Group %s, App Insights Name: %s): %s", itemID, resourceGroupName, appInsightsName, err) + } + + idSuffix := resourcesArmApplicationInsightsAnalyticsItemGenerateIDSuffix(result.Scope, itemID) + appInsightsID := id[0 : len(id)-len(idSuffix)] + d.Set("application_insights_id", appInsightsID) + d.Set("name", result.Name) + d.Set("version", result.Version) + d.Set("content", result.Content) + d.Set("scope", string(result.Scope)) + d.Set("type", string(result.Type)) + d.Set("time_created", result.TimeCreated) + d.Set("time_modified", result.TimeModified) + + if result.Properties != nil { + d.Set("function_alias", result.Properties.FunctionAlias) + } + + return nil +} + +func resourceArmApplicationInsightsAnalyticsItemDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).appInsights.AnalyticsItemsClient + ctx := meta.(*ArmClient).StopContext + + id := d.Id() + resourceGroupName, appInsightsName, itemScopePath, itemID, err := resourcesArmApplicationInsightsAnalyticsItemParseID(id) + if err != nil { + return fmt.Errorf("Error parsing Application Insights Analytics Item ID %s: %s", id, err) + } + + _, err = client.Delete(ctx, resourceGroupName, appInsightsName, itemScopePath, itemID, "") + if err != nil { + return fmt.Errorf("Error Deleting Application Insights Analytics Item '%s' (Resource Group %s, App Insights Name: %s): %s", itemID, resourceGroupName, appInsightsName, err) + } + + return nil +} + +func resourcesArmApplicationInsightsAnalyticsItemParseID(id string) (string, string, insights.ItemScopePath, string, error) { + resourceID, err := azure.ParseAzureResourceID(id) + if err != nil { + return "", "", "", "", fmt.Errorf("Error parsing resource ID: %s", err) + } + resourceGroupName := resourceID.ResourceGroup + appInsightsName := resourceID.Path["components"] + + // Use the following generated ID format: + // /analyticsItems/ [for shared scope items] + // /myanalyticsItems/ [for user scope items] + // Pull out the itemID and note the scope used + itemID := resourceID.Path["analyticsItems"] + itemScopePath := insights.AnalyticsItems + if itemID == "" { + // no "analyticsItems" component - try "myanalyticsItems" and set scope path + itemID = resourceID.Path["myanalyticsItems"] + itemScopePath = insights.MyanalyticsItems + } + + return resourceGroupName, appInsightsName, itemScopePath, itemID, nil +} + +func resourcesArmApplicationInsightsAnalyticsItemGenerateIDSuffix(itemScope insights.ItemScope, itemID string) string { + // See comments in resourcesArmApplicationInsightsAnalyticsItemParseID method about ID format + if itemScope == insights.ItemScopeShared { + return "/analyticsItems/" + itemID + } else { + return "/myanalyticsItems/" + itemID + } +} diff --git a/azurerm/resource_arm_application_insights_analytics_item_test.go b/azurerm/resource_arm_application_insights_analytics_item_test.go new file mode 100644 index 000000000000..7c04e25b0d15 --- /dev/null +++ b/azurerm/resource_arm_application_insights_analytics_item_test.go @@ -0,0 +1,289 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMApplicationInsightsAnalyticsItem_basic(t *testing.T) { + resourceName := "azurerm_application_insights_analytics_item.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMApplicationInsightsAnalyticsItem_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationInsightAnalyticsItemDestroy(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "testquery"), + resource.TestCheckResourceAttr(resourceName, "scope", "shared"), + resource.TestCheckResourceAttr(resourceName, "type", "query"), + resource.TestCheckResourceAttr(resourceName, "content", "requests #test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApplicationInsightsAnalyticsItem_update(t *testing.T) { + resourceName := "azurerm_application_insights_analytics_item.test" + ri := tf.AccRandTimeInt() + config1 := testAccAzureRMApplicationInsightsAnalyticsItem_basic(ri, testLocation()) + config2 := testAccAzureRMApplicationInsightsAnalyticsItem_basic2(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationInsightAnalyticsItemDestroy(), + Steps: []resource.TestStep{ + { + Config: config1, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "testquery"), + resource.TestCheckResourceAttr(resourceName, "scope", "shared"), + resource.TestCheckResourceAttr(resourceName, "type", "query"), + resource.TestCheckResourceAttr(resourceName, "content", "requests #test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: config2, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "testquery"), + resource.TestCheckResourceAttr(resourceName, "scope", "shared"), + resource.TestCheckResourceAttr(resourceName, "type", "query"), + resource.TestCheckResourceAttr(resourceName, "content", "requests #updated"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMApplicationInsightsAnalyticsItem_multiple(t *testing.T) { + resourceName1 := "azurerm_application_insights_analytics_item.test1" + resourceName2 := "azurerm_application_insights_analytics_item.test2" + resourceName3 := "azurerm_application_insights_analytics_item.test3" + ri := tf.AccRandTimeInt() + config := testAccAzureRMApplicationInsightsAnalyticsItem_multiple(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationInsightAnalyticsItemDestroy(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName1), + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName2), + testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName3), + resource.TestCheckResourceAttr(resourceName1, "name", "testquery1"), + resource.TestCheckResourceAttr(resourceName1, "scope", "shared"), + resource.TestCheckResourceAttr(resourceName1, "type", "query"), + resource.TestCheckResourceAttr(resourceName1, "content", "requests #test1"), + resource.TestCheckResourceAttr(resourceName2, "name", "testquery2"), + resource.TestCheckResourceAttr(resourceName2, "scope", "user"), + resource.TestCheckResourceAttr(resourceName2, "type", "query"), + resource.TestCheckResourceAttr(resourceName2, "content", "requests #test2"), + resource.TestCheckResourceAttr(resourceName3, "name", "testfunction1"), + resource.TestCheckResourceAttr(resourceName3, "scope", "shared"), + resource.TestCheckResourceAttr(resourceName3, "type", "function"), + resource.TestCheckResourceAttr(resourceName3, "content", "requests #test3"), + resource.TestCheckResourceAttr(resourceName3, "function_alias", "myfunction"), + ), + }, + { + ResourceName: resourceName1, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceName2, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceName3, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMApplicationInsightAnalyticsItemDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_application_insights_analytics_item" { + continue + } + name := rs.Primary.Attributes["name"] + + exists, err := testCheckAzureRMApplicationInsightsAnalyticsItemExistsInternal(rs) + if err != nil { + return fmt.Errorf("Error checking if item has been destroyed: %s", err) + } + if exists { + return fmt.Errorf("Bad: Application Insights AnalyticsItem '%q' still exists", name) + } + } + + return nil + } +} + +func testCheckAzureRMApplicationInsightsAnalyticsItemExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + name := rs.Primary.Attributes["name"] + + exists, err := testCheckAzureRMApplicationInsightsAnalyticsItemExistsInternal(rs) + if err != nil { + return fmt.Errorf("Error checking if item exists: %s", err) + } + if !exists { + return fmt.Errorf("Bad: Application Insights AnalyticsItem '%q' does not exist", name) + } + + return nil + } +} + +func testCheckAzureRMApplicationInsightsAnalyticsItemExistsInternal(rs *terraform.ResourceState) (bool, error) { + id := rs.Primary.Attributes["id"] + + resGroup, appInsightsName, itemScopePath, itemID, err := resourcesArmApplicationInsightsAnalyticsItemParseID(id) + if err != nil { + return false, fmt.Errorf("Failed to parse ID (id: %s): %+v", id, err) + } + + conn := testAccProvider.Meta().(*ArmClient).appInsights.AnalyticsItemsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + response, err := conn.Get(ctx, resGroup, appInsightsName, itemScopePath, itemID, "") + if err != nil { + if response.Response.IsHTTPStatus(404) { + return false, nil + } + return false, fmt.Errorf("Bad: Get on appInsightsAnalyticsItemsClient (id: %s): %+v", id, err) + } + _ = response + + return true, nil +} + +func testAccAzureRMApplicationInsightsAnalyticsItem_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + application_type = "web" +} + +resource "azurerm_application_insights_analytics_item" "test" { + name = "testquery" + application_insights_id = "${azurerm_application_insights.test.id}" + content = "requests #test" + scope = "shared" + type = "query" +} +`, rInt, location, rInt) +} + +func testAccAzureRMApplicationInsightsAnalyticsItem_basic2(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + application_type = "web" +} + +resource "azurerm_application_insights_analytics_item" "test" { + name = "testquery" + application_insights_id = "${azurerm_application_insights.test.id}" + content = "requests #updated" + scope = "shared" + type = "query" +} +`, rInt, location, rInt) +} + +func testAccAzureRMApplicationInsightsAnalyticsItem_multiple(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + application_type = "web" +} + +resource "azurerm_application_insights_analytics_item" "test1" { + name = "testquery1" + application_insights_id = "${azurerm_application_insights.test.id}" + content = "requests #test1" + scope = "shared" + type = "query" +} + +resource "azurerm_application_insights_analytics_item" "test2" { + name = "testquery2" + application_insights_id = "${azurerm_application_insights.test.id}" + content = "requests #test2" + scope = "user" + type = "query" +} + +resource "azurerm_application_insights_analytics_item" "test3" { + name = "testfunction1" + application_insights_id = "${azurerm_application_insights.test.id}" + content = "requests #test3" + scope = "shared" + type = "function" + function_alias = "myfunction" +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_application_insights_api_key.go b/azurerm/resource_arm_application_insights_api_key.go index 53810d7e05e2..3426acfed6cd 100644 --- a/azurerm/resource_arm_application_insights_api_key.go +++ b/azurerm/resource_arm_application_insights_api_key.go @@ -6,6 +6,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" "github.com/hashicorp/terraform/helper/schema" @@ -69,7 +70,7 @@ func resourceArmApplicationInsightsAPIKey() *schema.Resource { } func resourceArmApplicationInsightsAPIKeyCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsAPIKeyClient + client := meta.(*ArmClient).appInsights.APIKeyClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Application Insights API key creation.") @@ -77,7 +78,7 @@ func resourceArmApplicationInsightsAPIKeyCreate(d *schema.ResourceData, meta int name := d.Get("name").(string) appInsightsID := d.Get("application_insights_id").(string) - id, err := parseAzureResourceID(appInsightsID) + id, err := azure.ParseAzureResourceID(appInsightsID) if err != nil { return err } @@ -85,7 +86,7 @@ func resourceArmApplicationInsightsAPIKeyCreate(d *schema.ResourceData, meta int resGroup := id.ResourceGroup appInsightsName := id.Path["components"] - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { var existing insights.ApplicationInsightsComponentAPIKey existing, err = client.Get(ctx, resGroup, appInsightsName, name) if err != nil { @@ -123,10 +124,10 @@ func resourceArmApplicationInsightsAPIKeyCreate(d *schema.ResourceData, meta int } func resourceArmApplicationInsightsAPIKeyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsAPIKeyClient + client := meta.(*ArmClient).appInsights.APIKeyClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -163,10 +164,10 @@ func resourceArmApplicationInsightsAPIKeyRead(d *schema.ResourceData, meta inter } func resourceArmApplicationInsightsAPIKeyDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsAPIKeyClient + client := meta.(*ArmClient).appInsights.APIKeyClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_application_insights_api_key_test.go b/azurerm/resource_arm_application_insights_api_key_test.go index 1cb6a16f048a..b0e7e6647d45 100644 --- a/azurerm/resource_arm_application_insights_api_key_test.go +++ b/azurerm/resource_arm_application_insights_api_key_test.go @@ -8,7 +8,9 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMApplicationInsightsAPIKey_no_permission(t *testing.T) { @@ -29,7 +31,7 @@ func TestAccAzureRMApplicationInsightsAPIKey_no_permission(t *testing.T) { } func TestAccAzureRMApplicationInsightsAPIKey_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -147,7 +149,6 @@ func TestAccAzureRMApplicationInsightsAPIKey_authenticate_permission(t *testing. }, }, }) - } func TestAccAzureRMApplicationInsightsAPIKey_full_permissions(t *testing.T) { @@ -178,11 +179,10 @@ func TestAccAzureRMApplicationInsightsAPIKey_full_permissions(t *testing.T) { }, }, }) - } func testCheckAzureRMApplicationInsightsAPIKeyDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).appInsightsAPIKeyClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.APIKeyClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -191,7 +191,7 @@ func testCheckAzureRMApplicationInsightsAPIKeyDestroy(s *terraform.State) error } name := rs.Primary.Attributes["name"] - id, err := parseAzureResourceID(rs.Primary.Attributes["id"]) + id, err := azure.ParseAzureResourceID(rs.Primary.Attributes["id"]) if err != nil { return err } @@ -220,7 +220,7 @@ func testCheckAzureRMApplicationInsightsAPIKeyExists(resourceName string) resour return fmt.Errorf("Not found: %s", resourceName) } - id, err := parseAzureResourceID(rs.Primary.Attributes["id"]) + id, err := azure.ParseAzureResourceID(rs.Primary.Attributes["id"]) if err != nil { return err } @@ -228,7 +228,7 @@ func testCheckAzureRMApplicationInsightsAPIKeyExists(resourceName string) resour resGroup := id.ResourceGroup appInsightsName := id.Path["components"] - conn := testAccProvider.Meta().(*ArmClient).appInsightsAPIKeyClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.APIKeyClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resGroup, appInsightsName, keyID) diff --git a/azurerm/resource_arm_application_insights_test.go b/azurerm/resource_arm_application_insights_test.go index 9067eb57ed7b..4f01b21b5b3e 100644 --- a/azurerm/resource_arm_application_insights_test.go +++ b/azurerm/resource_arm_application_insights_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMApplicationInsights_basicWeb(t *testing.T) { @@ -37,7 +38,7 @@ func TestAccAzureRMApplicationInsights_basicWeb(t *testing.T) { } func TestAccAzureRMApplicationInsights_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -223,7 +224,7 @@ func TestAccAzureRMApplicationInsights_basiciOS(t *testing.T) { } func testCheckAzureRMApplicationInsightsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).appInsightsClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.ComponentsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -262,7 +263,7 @@ func testCheckAzureRMApplicationInsightsExists(resourceName string) resource.Tes return fmt.Errorf("Bad: no resource group found in state for App Insights: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).appInsightsClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.ComponentsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_application_insights_webtests.go b/azurerm/resource_arm_application_insights_webtests.go index b59c8440ee95..a2edf676126f 100644 --- a/azurerm/resource_arm_application_insights_webtests.go +++ b/azurerm/resource_arm_application_insights_webtests.go @@ -10,6 +10,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" "github.com/hashicorp/terraform/helper/schema" @@ -106,7 +108,7 @@ func resourceArmApplicationInsightsWebTests() *schema.Resource { DiffSuppressFunc: suppress.XmlDiff, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "synthetic_monitor_id": { Type: schema.TypeString, @@ -117,7 +119,7 @@ func resourceArmApplicationInsightsWebTests() *schema.Resource { } func resourceArmApplicationInsightsWebTestsCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsWebTestsClient + client := meta.(*ArmClient).appInsights.WebTestsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Application Insights WebTest creation.") @@ -126,14 +128,14 @@ func resourceArmApplicationInsightsWebTestsCreateUpdate(d *schema.ResourceData, resGroup := d.Get("resource_group_name").(string) appInsightsID := d.Get("application_insights_id").(string) - id, err := parseAzureResourceID(appInsightsID) + id, err := azure.ParseAzureResourceID(appInsightsID) if err != nil { return err } appInsightsName := id.Path["components"] - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -157,9 +159,9 @@ func resourceArmApplicationInsightsWebTestsCreateUpdate(d *schema.ResourceData, geoLocations := expandApplicationInsightsWebTestGeoLocations(geoLocationsRaw) testConf := d.Get("configuration").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tagKey := fmt.Sprintf("hidden-link:/subscriptions/%s/resourceGroups/%s/providers/microsoft.insights/components/%s", client.SubscriptionID, resGroup, appInsightsName) - tags[tagKey] = "Resource" + t[tagKey] = "Resource" webTest := insights.WebTest{ Name: &name, @@ -179,7 +181,7 @@ func resourceArmApplicationInsightsWebTestsCreateUpdate(d *schema.ResourceData, WebTest: &testConf, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } resp, err := client.CreateOrUpdate(ctx, resGroup, name, webTest) @@ -193,10 +195,10 @@ func resourceArmApplicationInsightsWebTestsCreateUpdate(d *schema.ResourceData, } func resourceArmApplicationInsightsWebTestsRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsWebTestsClient + client := meta.(*ArmClient).appInsights.WebTestsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -217,8 +219,7 @@ func resourceArmApplicationInsightsWebTestsRead(d *schema.ResourceData, meta int } appInsightsId := "" - tags := resp.Tags - for i := range tags { + for i := range resp.Tags { if strings.HasPrefix(i, "hidden-link") { appInsightsId = strings.Split(i, ":")[1] } @@ -249,16 +250,14 @@ func resourceArmApplicationInsightsWebTestsRead(d *schema.ResourceData, meta int } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmApplicationInsightsWebTestsDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appInsightsWebTestsClient + client := meta.(*ArmClient).appInsights.WebTestsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -302,7 +301,6 @@ func flattenApplicationInsightsWebTestGeoLocations(input *[]insights.WebTestGeol if prop.Location != nil { results = append(results, azure.NormalizeLocation(*prop.Location)) } - } return results diff --git a/azurerm/resource_arm_application_insights_webtests_test.go b/azurerm/resource_arm_application_insights_webtests_test.go index 2cde49ac155e..226620c86b9a 100644 --- a/azurerm/resource_arm_application_insights_webtests_test.go +++ b/azurerm/resource_arm_application_insights_webtests_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMApplicationInsightsWebTests_basic(t *testing.T) { @@ -101,7 +102,7 @@ func TestAccAzureRMApplicationInsightsWebTests_update(t *testing.T) { } func TestAccAzureRMApplicationInsightsWebTests_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -131,7 +132,7 @@ func TestAccAzureRMApplicationInsightsWebTests_requiresImport(t *testing.T) { } func testCheckAzureRMApplicationInsightsWebTestsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).appInsightsWebTestsClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.WebTestsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -166,7 +167,7 @@ func testCheckAzureRMApplicationInsightsWebTestExists(resourceName string) resou name := rs.Primary.Attributes["name"] resGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).appInsightsWebTestsClient + conn := testAccProvider.Meta().(*ArmClient).appInsights.WebTestsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resGroup, name) @@ -262,7 +263,7 @@ resource "azurerm_application_insights_web_test" "import" { resource_group_name = "${azurerm_application_insights_web_test.test.resource_group_name}" application_insights_id = "${azurerm_application_insights_web_test.test.application_insights_id}" kind = "${azurerm_application_insights_web_test.test.kind}" - configuration = "${azurerm_application_insights_web_test.test.configuration}" + configuration = "${azurerm_application_insights_web_test.test.configuration}" } `, template) } diff --git a/azurerm/resource_arm_application_security_group.go b/azurerm/resource_arm_application_security_group.go index fa972cee798b..fde0ef50265e 100644 --- a/azurerm/resource_arm_application_security_group.go +++ b/azurerm/resource_arm_application_security_group.go @@ -4,11 +4,13 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -33,19 +35,19 @@ func resourceArmApplicationSecurityGroup() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmApplicationSecurityGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationSecurityGroupsClient + client := meta.(*ArmClient).network.ApplicationSecurityGroupsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -59,11 +61,11 @@ func resourceArmApplicationSecurityGroupCreateUpdate(d *schema.ResourceData, met } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) securityGroup := network.ApplicationSecurityGroup{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, securityGroup) if err != nil { @@ -88,10 +90,10 @@ func resourceArmApplicationSecurityGroupCreateUpdate(d *schema.ResourceData, met } func resourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationSecurityGroupsClient + client := meta.(*ArmClient).network.ApplicationSecurityGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -113,16 +115,14 @@ func resourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta interf if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmApplicationSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationSecurityGroupsClient + client := meta.(*ArmClient).network.ApplicationSecurityGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_application_security_group_test.go b/azurerm/resource_arm_application_security_group_test.go index f0c85c3462b6..8ed3ef1b9c3d 100644 --- a/azurerm/resource_arm_application_security_group_test.go +++ b/azurerm/resource_arm_application_security_group_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func TestAccAzureRMApplicationSecurityGroup_basic(t *testing.T) { } func TestAccAzureRMApplicationSecurityGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -120,7 +121,7 @@ func testCheckAzureRMApplicationSecurityGroupDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).applicationSecurityGroupsClient + client := testAccProvider.Meta().(*ArmClient).network.ApplicationSecurityGroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -153,7 +154,7 @@ func testCheckAzureRMApplicationSecurityGroupExists(resourceName string) resourc return fmt.Errorf("Bad: no resource group found in state for Application Security Group: %q", name) } - client := testAccProvider.Meta().(*ArmClient).applicationSecurityGroupsClient + client := testAccProvider.Meta().(*ArmClient).network.ApplicationSecurityGroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_automation_account.go b/azurerm/resource_arm_automation_account.go index f59452876fc1..fb3c42e86686 100644 --- a/azurerm/resource_arm_automation_account.go +++ b/azurerm/resource_arm_automation_account.go @@ -11,6 +11,8 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,16 +42,19 @@ func resourceArmAutomationAccount() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), + // Remove in 2.0 "sku": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "This property has been deprecated in favour of the 'sku_name' property and will be removed in version 2.0 of the provider", + ConflictsWith: []string{"sku_name"}, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Optional: true, - Default: string(automation.Basic), DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(automation.Basic), @@ -60,7 +65,18 @@ func resourceArmAutomationAccount() *schema.Resource { }, }, - "tags": tagsSchema(), + "sku_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"sku"}, + ValidateFunc: validation.StringInSlice([]string{ + string(automation.Basic), + string(automation.Free), + }, false), + }, + + "tags": tags.Schema(), "dsc_server_endpoint": { Type: schema.TypeString, @@ -82,16 +98,37 @@ func resourceArmAutomationAccountCreateUpdate(d *schema.ResourceData, meta inter client := meta.(*ArmClient).automation.AccountClient ctx := meta.(*ArmClient).StopContext + // Remove in 2.0 + var sku automation.Sku + + if inputs := d.Get("sku").([]interface{}); len(inputs) != 0 { + input := inputs[0].(map[string]interface{}) + v := input["name"].(string) + + sku = automation.Sku{ + Name: automation.SkuNameEnum(v), + } + } else { + // Keep in 2.0 + sku = automation.Sku{ + Name: automation.SkuNameEnum(d.Get("sku_name").(string)), + } + } + + if sku.Name == "" { + return fmt.Errorf("either 'sku_name' or 'sku' must be defined in the configuration file") + } + log.Printf("[INFO] preparing arguments for Automation Account create/update.") name := d.Get("name").(string) - resGroup := d.Get("resource_group_name").(string) + resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, name) + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Error checking for presence of existing Automation Account %q (Resource Group %q): %s", name, resGroup, err) + return fmt.Errorf("Error checking for presence of existing Automation Account %q (Resource Group %q): %s", name, resourceGroup, err) } } @@ -101,28 +138,27 @@ func resourceArmAutomationAccountCreateUpdate(d *schema.ResourceData, meta inter } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) - sku := expandAutomationAccountSku(d) + t := d.Get("tags").(map[string]interface{}) parameters := automation.AccountCreateOrUpdateParameters{ AccountCreateOrUpdateProperties: &automation.AccountCreateOrUpdateProperties{ - Sku: sku, + Sku: &sku, }, Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), } - if _, err := client.CreateOrUpdate(ctx, resGroup, name, parameters); err != nil { - return fmt.Errorf("Error creating/updating Automation Account %q (Resource Group %q) %+v", name, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters); err != nil { + return fmt.Errorf("Error creating/updating Automation Account %q (Resource Group %q) %+v", name, resourceGroup, err) } - read, err := client.Get(ctx, resGroup, name) + read, err := client.Get(ctx, resourceGroup, name) if err != nil { - return fmt.Errorf("Error retrieving Automation Account %q (Resource Group %q) %+v", name, resGroup, err) + return fmt.Errorf("Error retrieving Automation Account %q (Resource Group %q) %+v", name, resourceGroup, err) } if read.ID == nil { - return fmt.Errorf("Cannot read Automation Account %q (Resource Group %q) ID", name, resGroup) + return fmt.Errorf("Cannot read Automation Account %q (Resource Group %q) ID", name, resourceGroup) } d.SetId(*read.ID) @@ -135,43 +171,52 @@ func resourceArmAutomationAccountRead(d *schema.ResourceData, meta interface{}) registrationClient := meta.(*ArmClient).automation.AgentRegistrationInfoClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } - resGroup := id.ResourceGroup + resourceGroup := id.ResourceGroup name := id.Path["automationAccounts"] - resp, err := client.Get(ctx, resGroup, name) + resp, err := client.Get(ctx, resourceGroup, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[DEBUG] Automation Account %q was not found in Resource Group %q - removing from state!", name, resGroup) + log.Printf("[DEBUG] Automation Account %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 Automation Account %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("Error making Read request on Automation Account %q (Resource Group %q): %+v", name, resourceGroup, err) } - keysResp, err := registrationClient.Get(ctx, resGroup, name) + keysResp, err := registrationClient.Get(ctx, resourceGroup, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[DEBUG] Agent Registration Info for Automation Account %q was not found in Resource Group %q - removing from state!", name, resGroup) + log.Printf("[DEBUG] Agent Registration Info for Automation Account %q was not found in Resource Group %q - removing from state!", name, resourceGroup) d.SetId("") return nil } - return fmt.Errorf("Error making Read request for Agent Registration Info for Automation Account %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("Error making Read request for Agent Registration Info for Automation Account %q (Resource Group %q): %+v", name, resourceGroup, err) } d.Set("name", resp.Name) - d.Set("resource_group_name", resGroup) + d.Set("resource_group_name", resourceGroup) if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } - if err := d.Set("sku", flattenAutomationAccountSku(resp.Sku)); err != nil { - return fmt.Errorf("Error setting `sku`: %+v", err) + if sku := resp.Sku; sku != nil { + // Remove in 2.0 + if err := d.Set("sku", flattenAutomationAccountSku(sku)); err != nil { + return fmt.Errorf("Error setting 'sku': %+v", err) + } + + if err := d.Set("sku_name", string(sku.Name)); err != nil { + return fmt.Errorf("Error setting 'sku_name': %+v", err) + } + } else { + return fmt.Errorf("Error making Read request on Automation Account %q (Resource Group %q): Unable to retrieve 'sku' value", name, resourceGroup) } d.Set("dsc_server_endpoint", keysResp.Endpoint) @@ -180,8 +225,8 @@ func resourceArmAutomationAccountRead(d *schema.ResourceData, meta interface{}) d.Set("dsc_secondary_access_key", keys.Secondary) } - if tags := resp.Tags; tags != nil { - flattenAndSetTags(d, tags) + if t := resp.Tags; t != nil { + return tags.FlattenAndSet(d, t) } return nil @@ -191,14 +236,14 @@ func resourceArmAutomationAccountDelete(d *schema.ResourceData, meta interface{} client := meta.(*ArmClient).automation.AccountClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } - resGroup := id.ResourceGroup + resourceGroup := id.ResourceGroup name := id.Path["automationAccounts"] - resp, err := client.Delete(ctx, resGroup, name) + resp, err := client.Delete(ctx, resourceGroup, name) if err != nil { if utils.ResponseWasNotFound(resp) { @@ -211,6 +256,7 @@ func resourceArmAutomationAccountDelete(d *schema.ResourceData, meta interface{} return nil } +// Remove in 2.0 func flattenAutomationAccountSku(sku *automation.Sku) []interface{} { if sku == nil { return []interface{}{} @@ -220,15 +266,3 @@ func flattenAutomationAccountSku(sku *automation.Sku) []interface{} { result["name"] = string(sku.Name) return []interface{}{result} } - -func expandAutomationAccountSku(d *schema.ResourceData) *automation.Sku { - inputs := d.Get("sku").([]interface{}) - input := inputs[0].(map[string]interface{}) - name := automation.SkuNameEnum(input["name"].(string)) - - sku := automation.Sku{ - Name: name, - } - - return &sku -} diff --git a/azurerm/resource_arm_automation_account_test.go b/azurerm/resource_arm_automation_account_test.go index e5cecebf4ddd..85f7b6ab470b 100644 --- a/azurerm/resource_arm_automation_account_test.go +++ b/azurerm/resource_arm_automation_account_test.go @@ -2,11 +2,13 @@ package azurerm import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -21,6 +23,35 @@ func TestAccAzureRMAutomationAccount_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAzureRMAutomationAccount_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAutomationAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "sku_name", "Basic"), + resource.TestCheckResourceAttrSet(resourceName, "dsc_server_endpoint"), + resource.TestCheckResourceAttrSet(resourceName, "dsc_primary_access_key"), + resource.TestCheckResourceAttrSet(resourceName, "dsc_secondary_access_key"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMAutomationAccount_basicClassic(t *testing.T) { + resourceName := "azurerm_automation_account.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAutomationAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAutomationAccount_basicClassic(ri, testLocation()), Check: resource.ComposeTestCheckFunc( testCheckAzureRMAutomationAccountExists(resourceName), resource.TestCheckResourceAttr(resourceName, "sku.0.name", "Basic"), @@ -38,8 +69,25 @@ func TestAccAzureRMAutomationAccount_basic(t *testing.T) { }) } +// Remove in 2.0 +func TestAccAzureRMAutomationAccount_basicNotDefined(t *testing.T) { + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAutomationAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAutomationAccount_basicNotDefined(ri, testLocation()), + ExpectError: regexp.MustCompile("either 'sku_name' or 'sku' must be defined in the configuration file"), + }, + }, + }) +} + func TestAccAzureRMAutomationAccount_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -80,7 +128,7 @@ func TestAccAzureRMAutomationAccount_complete(t *testing.T) { Config: testAccAzureRMAutomationAccount_complete(ri, testLocation()), Check: resource.ComposeTestCheckFunc( testCheckAzureRMAutomationAccountExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "sku.0.name", "Basic"), + resource.TestCheckResourceAttr(resourceName, "sku_name", "Basic"), resource.TestCheckResourceAttr(resourceName, "tags.hello", "world"), ), }, @@ -122,7 +170,6 @@ func testCheckAzureRMAutomationAccountDestroy(s *terraform.State) error { } func testCheckAzureRMAutomationAccountExists(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] @@ -160,6 +207,24 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_automation_account" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku_name = "Basic" +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMAutomationAccount_basicClassic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_automation_account" "test" { name = "acctest-%d" location = "${azurerm_resource_group.test.location}" @@ -167,7 +232,23 @@ resource "azurerm_automation_account" "test" { sku { name = "Basic" - } + } +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMAutomationAccount_basicNotDefined(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_automation_account" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" } `, rInt, location, rInt) } @@ -182,9 +263,7 @@ resource "azurerm_automation_account" "import" { location = "${azurerm_automation_account.test.location}" resource_group_name = "${azurerm_automation_account.test.resource_group_name}" - sku { - name = "Basic" - } + sku_name = "Basic" } `, template) } @@ -201,10 +280,8 @@ resource "azurerm_automation_account" "test" { location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - sku { - name = "Basic" - } - + sku_name = "Basic" + tags = { "hello" = "world" } diff --git a/azurerm/resource_arm_automation_credential.go b/azurerm/resource_arm_automation_credential.go index 8bca1fc97f2b..2d9003d73314 100644 --- a/azurerm/resource_arm_automation_credential.go +++ b/azurerm/resource_arm_automation_credential.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -66,7 +67,7 @@ func resourceArmAutomationCredentialCreateUpdate(d *schema.ResourceData, meta in resGroup := d.Get("resource_group_name").(string) accName := d.Get("account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -114,7 +115,7 @@ func resourceArmAutomationCredentialRead(d *schema.ResourceData, meta interface{ client := meta.(*ArmClient).automation.CredentialClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -147,7 +148,7 @@ func resourceArmAutomationCredentialDelete(d *schema.ResourceData, meta interfac client := meta.(*ArmClient).automation.CredentialClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_automation_credential_test.go b/azurerm/resource_arm_automation_credential_test.go index 1a509717cf4b..473ff77e46dc 100644 --- a/azurerm/resource_arm_automation_credential_test.go +++ b/azurerm/resource_arm_automation_credential_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMAutomationCredential_basic(t *testing.T) { } func TestAccAzureRMAutomationCredential_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -120,14 +121,12 @@ func testCheckAzureRMAutomationCredentialDestroy(s *terraform.State) error { } return fmt.Errorf("Automation Credential still exists:\n%#v", resp) - } return nil } func testCheckAzureRMAutomationCredentialExists(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] diff --git a/azurerm/resource_arm_automation_dsc_configuration.go b/azurerm/resource_arm_automation_dsc_configuration.go index 4027e1600561..5cf167791d69 100644 --- a/azurerm/resource_arm_automation_dsc_configuration.go +++ b/azurerm/resource_arm_automation_dsc_configuration.go @@ -12,6 +12,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -83,7 +84,7 @@ func resourceArmAutomationDscConfigurationCreateUpdate(d *schema.ResourceData, m resGroup := d.Get("resource_group_name").(string) accName := d.Get("automation_account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -135,7 +136,7 @@ func resourceArmAutomationDscConfigurationRead(d *schema.ResourceData, meta inte client := meta.(*ArmClient).automation.DscConfigurationClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -187,7 +188,7 @@ func resourceArmAutomationDscConfigurationDelete(d *schema.ResourceData, meta in client := meta.(*ArmClient).automation.DscConfigurationClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_automation_dsc_configuration_test.go b/azurerm/resource_arm_automation_dsc_configuration_test.go index 7096247713a1..f7514a0da833 100644 --- a/azurerm/resource_arm_automation_dsc_configuration_test.go +++ b/azurerm/resource_arm_automation_dsc_configuration_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,7 +41,7 @@ func TestAccAzureRMAutomationDscConfiguration_basic(t *testing.T) { } func TestAccAzureRMAutomationDscConfiguration_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -96,14 +97,12 @@ func testCheckAzureRMAutomationDscConfigurationDestroy(s *terraform.State) error } return fmt.Errorf("Automation Dsc Configuration still exists:\n%#v", resp) - } return nil } func testCheckAzureRMAutomationDscConfigurationExists(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] diff --git a/azurerm/resource_arm_automation_dsc_nodeconfiguration.go b/azurerm/resource_arm_automation_dsc_nodeconfiguration.go index 58944ad79ae1..c439c50f40ff 100644 --- a/azurerm/resource_arm_automation_dsc_nodeconfiguration.go +++ b/azurerm/resource_arm_automation_dsc_nodeconfiguration.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -65,7 +66,7 @@ func resourceArmAutomationDscNodeConfigurationCreateUpdate(d *schema.ResourceDat resGroup := d.Get("resource_group_name").(string) accName := d.Get("automation_account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -118,7 +119,7 @@ func resourceArmAutomationDscNodeConfigurationRead(d *schema.ResourceData, meta client := meta.(*ArmClient).automation.DscNodeConfigurationClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -150,7 +151,7 @@ func resourceArmAutomationDscNodeConfigurationDelete(d *schema.ResourceData, met client := meta.(*ArmClient).automation.DscNodeConfigurationClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_automation_dsc_nodeconfiguration_test.go b/azurerm/resource_arm_automation_dsc_nodeconfiguration_test.go index 8c4990fb266e..b20faeff954f 100644 --- a/azurerm/resource_arm_automation_dsc_nodeconfiguration_test.go +++ b/azurerm/resource_arm_automation_dsc_nodeconfiguration_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMAutomationDscNodeConfiguration_basic(t *testing.T) { } func TestAccAzureRMAutomationDscNodeConfiguration_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -94,14 +95,12 @@ func testCheckAzureRMAutomationDscNodeConfigurationDestroy(s *terraform.State) e } return fmt.Errorf("Automation Dsc Node Configuration still exists:\n%#v", resp) - } return nil } func testCheckAzureRMAutomationDscNodeConfigurationExists(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] diff --git a/azurerm/resource_arm_automation_module.go b/azurerm/resource_arm_automation_module.go index afbc6ddb87ab..2123648095f4 100644 --- a/azurerm/resource_arm_automation_module.go +++ b/azurerm/resource_arm_automation_module.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -87,7 +88,7 @@ func resourceArmAutomationModuleCreateUpdate(d *schema.ResourceData, meta interf resGroup := d.Get("resource_group_name").(string) accName := d.Get("automation_account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -135,7 +136,6 @@ func resourceArmAutomationModuleCreateUpdate(d *schema.ResourceData, meta interf Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, resGroup, accName, name) if err2 != nil { return resp, "Error", fmt.Errorf("Error retrieving Module %q (Automation Account %q / Resource Group %q): %+v", name, accName, resGroup, err2) @@ -172,7 +172,7 @@ func resourceArmAutomationModuleRead(d *schema.ResourceData, meta interface{}) e client := meta.(*ArmClient).automation.ModuleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -201,7 +201,7 @@ func resourceArmAutomationModuleDelete(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).automation.ModuleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_automation_module_test.go b/azurerm/resource_arm_automation_module_test.go index ee79ed7b0840..7d875a291c81 100644 --- a/azurerm/resource_arm_automation_module_test.go +++ b/azurerm/resource_arm_automation_module_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMAutomationModule_basic(t *testing.T) { } func TestAccAzureRMAutomationModule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -120,14 +121,12 @@ func testCheckAzureRMAutomationModuleDestroy(s *terraform.State) error { } return fmt.Errorf("Automation Module still exists:\n%#v", resp) - } return nil } func testCheckAzureRMAutomationModuleExists(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] diff --git a/azurerm/resource_arm_automation_runbook.go b/azurerm/resource_arm_automation_runbook.go index 052dc895c865..ebb76c9c7899 100644 --- a/azurerm/resource_arm_automation_runbook.go +++ b/azurerm/resource_arm_automation_runbook.go @@ -10,7 +10,10 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,7 +49,7 @@ func resourceArmAutomationRunbook() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(automation.Graph), string(automation.GraphPowerShell), @@ -115,7 +118,7 @@ func resourceArmAutomationRunbook() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -130,7 +133,7 @@ func resourceArmAutomationRunbookCreateUpdate(d *schema.ResourceData, meta inter accName := d.Get("account_name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -144,7 +147,7 @@ func resourceArmAutomationRunbookCreateUpdate(d *schema.ResourceData, meta inter } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) runbookType := automation.RunbookTypeEnum(d.Get("runbook_type").(string)) logProgress := d.Get("log_progress").(bool) @@ -163,7 +166,7 @@ func resourceArmAutomationRunbookCreateUpdate(d *schema.ResourceData, meta inter }, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, resGroup, accName, name, parameters); err != nil { @@ -202,7 +205,7 @@ func resourceArmAutomationRunbookRead(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).automation.RunbookClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -234,10 +237,6 @@ func resourceArmAutomationRunbookRead(d *schema.ResourceData, meta interface{}) d.Set("description", props.Description) } - if tags := resp.Tags; tags != nil { - flattenAndSetTags(d, tags) - } - response, err := client.GetContent(ctx, resGroup, accName, name) if err != nil { return fmt.Errorf("Error retrieving content for Automation Runbook %q (Account %q / Resource Group %q): %+v", name, accName, resGroup, err) @@ -254,6 +253,10 @@ func resourceArmAutomationRunbookRead(d *schema.ResourceData, meta interface{}) } } + if t := resp.Tags; t != nil { + return tags.FlattenAndSet(d, t) + } + return nil } @@ -261,7 +264,7 @@ func resourceArmAutomationRunbookDelete(d *schema.ResourceData, meta interface{} client := meta.(*ArmClient).automation.RunbookClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_automation_runbook_test.go b/azurerm/resource_arm_automation_runbook_test.go index 0d4a463d003f..372fc50f0bac 100644 --- a/azurerm/resource_arm_automation_runbook_test.go +++ b/azurerm/resource_arm_automation_runbook_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMAutomationRunbook_PSWorkflow(t *testing.T) { } func TestAccAzureRMAutomationRunbook_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -152,7 +153,6 @@ func testCheckAzureRMAutomationRunbookDestroy(s *terraform.State) error { } func testCheckAzureRMAutomationRunbookExists(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] diff --git a/azurerm/resource_arm_automation_schedule.go b/azurerm/resource_arm_automation_schedule.go index 084b3ba2fe50..60a4e05b8291 100644 --- a/azurerm/resource_arm_automation_schedule.go +++ b/azurerm/resource_arm_automation_schedule.go @@ -16,6 +16,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -171,7 +172,6 @@ func resourceArmAutomationSchedule() *schema.Resource { }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - frequency := strings.ToLower(diff.Get("frequency").(string)) interval, _ := diff.GetOk("interval") if frequency == "onetime" && interval.(int) > 0 { @@ -233,7 +233,7 @@ func resourceArmAutomationScheduleCreateUpdate(d *schema.ResourceData, meta inte accountName = v.(string) } - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, accountName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -313,7 +313,7 @@ func resourceArmAutomationScheduleRead(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).automation.ScheduleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -373,7 +373,7 @@ func resourceArmAutomationScheduleDelete(d *schema.ResourceData, meta interface{ client := meta.(*ArmClient).automation.ScheduleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -393,7 +393,6 @@ func resourceArmAutomationScheduleDelete(d *schema.ResourceData, meta interface{ } func expandArmAutomationScheduleAdvanced(d *schema.ResourceData, isUpdate bool) (*automation.AdvancedSchedule, error) { - expandedAdvancedSchedule := automation.AdvancedSchedule{} // If frequency is set to `Month` the `week_days` array cannot be set (even empty), otherwise the API returns an error. diff --git a/azurerm/resource_arm_automation_schedule_test.go b/azurerm/resource_arm_automation_schedule_test.go index a547f10da18a..f870debe8fcd 100644 --- a/azurerm/resource_arm_automation_schedule_test.go +++ b/azurerm/resource_arm_automation_schedule_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -34,7 +35,7 @@ func TestAccAzureRMAutomationSchedule_oneTime_basic(t *testing.T) { }) } func TestAccAzureRMAutomationSchedule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -505,7 +506,6 @@ func checkAccAzureRMAutomationSchedule_recurring_advanced_month(resourceName str } func testAccAzureRMAutomationSchedule_recurring_advanced_month_week_day(rInt int, location string, weekDay string, weekDayOccurrence int) string { - return fmt.Sprintf(` %s diff --git a/azurerm/resource_arm_autoscale_setting.go b/azurerm/resource_arm_autoscale_setting.go index 837fdbcbe1d9..eb9f1c221baf 100644 --- a/azurerm/resource_arm_autoscale_setting.go +++ b/azurerm/resource_arm_autoscale_setting.go @@ -12,8 +12,11 @@ import ( "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -119,7 +122,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will "time_grain": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "statistic": { Type: schema.TypeString, @@ -130,12 +133,12 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will string(insights.MetricStatisticTypeMin), string(insights.MetricStatisticTypeSum), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "time_window": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "time_aggregation": { Type: schema.TypeString, @@ -147,7 +150,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will string(insights.TimeAggregationTypeMinimum), string(insights.TimeAggregationTypeTotal), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "operator": { Type: schema.TypeString, @@ -160,7 +163,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will string(insights.LessThanOrEqual), string(insights.NotEquals), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "threshold": { Type: schema.TypeFloat, @@ -182,7 +185,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will string(insights.ScaleDirectionDecrease), string(insights.ScaleDirectionIncrease), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "type": { Type: schema.TypeString, @@ -192,7 +195,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will string(insights.ExactCount), string(insights.PercentChangeCount), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "value": { Type: schema.TypeInt, @@ -202,7 +205,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will "cooldown": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, }, }, @@ -225,12 +228,12 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will "start": { Type: schema.TypeString, Required: true, - ValidateFunc: validateRFC3339Date, + ValidateFunc: validate.RFC3339Time, }, "end": { Type: schema.TypeString, Required: true, - ValidateFunc: validateRFC3339Date, + ValidateFunc: validate.RFC3339Time, }, }, }, @@ -261,7 +264,7 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will "Saturday", "Sunday", }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, "hours": { @@ -334,6 +337,9 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will "properties": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, }, @@ -342,19 +348,19 @@ As such the existing 'azurerm_autoscale_setting' resource is deprecated and will }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -380,8 +386,8 @@ func resourceArmAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta interf return fmt.Errorf("Error expanding `profile`: %+v", err) } - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := insights.AutoscaleSettingResource{ Location: utils.String(location), @@ -412,10 +418,10 @@ func resourceArmAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta interf } func resourceArmAutoScaleSettingRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -456,17 +462,15 @@ func resourceArmAutoScaleSettingRead(d *schema.ResourceData, meta interface{}) e } // Return a new tag map filtered by the specified tag names. - tagMap := filterTags(resp.Tags, "$type") - flattenAndSetTags(d, tagMap) - - return nil + tagMap := tags.Filter(resp.Tags, "$type") + return tags.FlattenAndSet(d, tagMap) } func resourceArmAutoScaleSettingDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -865,7 +869,6 @@ func flattenAzureRmAutoScaleSettingRecurrence(input *insights.Recurrence) []inte result := make(map[string]interface{}) if schedule := input.Schedule; schedule != nil { - if timezone := schedule.TimeZone; timezone != nil { result["timezone"] = *timezone } diff --git a/azurerm/resource_arm_autoscale_setting_test.go b/azurerm/resource_arm_autoscale_setting_test.go index 30bef543a9d7..063c0de8caa1 100644 --- a/azurerm/resource_arm_autoscale_setting_test.go +++ b/azurerm/resource_arm_autoscale_setting_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMAutoScaleSetting_basic(t *testing.T) { @@ -43,7 +44,7 @@ func TestAccAzureRMAutoScaleSetting_basic(t *testing.T) { } func TestAccAzureRMAutoScaleSetting_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -332,7 +333,7 @@ func testCheckAzureRMAutoScaleSettingExists(resourceName string) resource.TestCh return fmt.Errorf("Bad: no resource group found in state for AutoScale Setting: %s", autoscaleSettingName) } - conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.AutoscaleSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, autoscaleSettingName) @@ -349,7 +350,7 @@ func testCheckAzureRMAutoScaleSettingExists(resourceName string) resource.TestCh } func testCheckAzureRMAutoScaleSettingDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.AutoscaleSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_availability_set.go b/azurerm/resource_arm_availability_set.go index f3f5b7a6e538..85396661b9e7 100644 --- a/azurerm/resource_arm_availability_set.go +++ b/azurerm/resource_arm_availability_set.go @@ -5,11 +5,14 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -57,13 +60,25 @@ func resourceArmAvailabilitySet() *schema.Resource { ForceNew: true, }, - "tags": tagsSchema(), + "proximity_placement_group_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + + // We have to ignore case due to incorrect capitalisation of resource group name in + // proximity placement group ID in the response we get from the API request + // + // todo can be removed when https://github.com/Azure/azure-sdk-for-go/issues/5699 is fixed + DiffSuppressFunc: suppress.CaseDifference, + }, + + "tags": tags.Schema(), }, } } func resourceArmAvailabilitySetCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).availSetClient + client := meta.(*ArmClient).compute.AvailabilitySetsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Availability Set creation.") @@ -71,7 +86,7 @@ func resourceArmAvailabilitySetCreateUpdate(d *schema.ResourceData, meta interfa name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -88,7 +103,7 @@ func resourceArmAvailabilitySetCreateUpdate(d *schema.ResourceData, meta interfa updateDomainCount := d.Get("platform_update_domain_count").(int) faultDomainCount := d.Get("platform_fault_domain_count").(int) managed := d.Get("managed").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) availSet := compute.AvailabilitySet{ Name: &name, @@ -97,7 +112,13 @@ func resourceArmAvailabilitySetCreateUpdate(d *schema.ResourceData, meta interfa PlatformFaultDomainCount: utils.Int32(int32(faultDomainCount)), PlatformUpdateDomainCount: utils.Int32(int32(updateDomainCount)), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), + } + + if v, ok := d.GetOk("proximity_placement_group_id"); ok { + availSet.AvailabilitySetProperties.ProximityPlacementGroup = &compute.SubResource{ + ID: utils.String(v.(string)), + } } if managed { @@ -118,10 +139,10 @@ func resourceArmAvailabilitySetCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmAvailabilitySetRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).availSetClient + client := meta.(*ArmClient).compute.AvailabilitySetsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -149,18 +170,20 @@ func resourceArmAvailabilitySetRead(d *schema.ResourceData, meta interface{}) er if props := resp.AvailabilitySetProperties; props != nil { d.Set("platform_update_domain_count", props.PlatformUpdateDomainCount) d.Set("platform_fault_domain_count", props.PlatformFaultDomainCount) - } - flattenAndSetTags(d, resp.Tags) + if proximityPlacementGroup := props.ProximityPlacementGroup; proximityPlacementGroup != nil { + d.Set("proximity_placement_group_id", proximityPlacementGroup.ID) + } + } - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmAvailabilitySetDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).availSetClient + client := meta.(*ArmClient).compute.AvailabilitySetsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_availability_set_test.go b/azurerm/resource_arm_availability_set_test.go index 470c7899071a..c2abff1d1757 100644 --- a/azurerm/resource_arm_availability_set_test.go +++ b/azurerm/resource_arm_availability_set_test.go @@ -2,12 +2,14 @@ package azurerm import ( "fmt" + "net/http" "testing" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -39,7 +41,7 @@ func TestAccAzureRMAvailabilitySet_basic(t *testing.T) { } func TestAccAzureRMAvailabilitySet_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -131,6 +133,32 @@ func TestAccAzureRMAvailabilitySet_withTags(t *testing.T) { }) } +func TestAccAzureRMAvailabilitySet_withPPG(t *testing.T) { + resourceName := "azurerm_availability_set.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMAvailabilitySet_withPPG(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAvailabilitySetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAvailabilitySetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "proximity_placement_group_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMAvailabilitySet_withDomainCounts(t *testing.T) { resourceName := "azurerm_availability_set.test" ri := tf.AccRandTimeInt() @@ -192,21 +220,24 @@ func testCheckAzureRMAvailabilitySetExists(resourceName string) resource.TestChe return fmt.Errorf("Not found: %s", resourceName) } - availSetName := rs.Primary.Attributes["name"] + // Name of the actual scale set + 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 availability set: %s", availSetName) + return fmt.Errorf("Bad: no resource group found in state for availability set: %s", name) } - client := testAccProvider.Meta().(*ArmClient).availSetClient + client := testAccProvider.Meta().(*ArmClient).compute.AvailabilitySetsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext - resp, err := client.Get(ctx, resourceGroup, availSetName) + + vmss, err := client.Get(ctx, resourceGroup, name) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: Availability Set %q (resource group: %q) does not exist", availSetName, resourceGroup) - } + return fmt.Errorf("Bad: Get on vmScaleSetClient: %+v", err) + } - return fmt.Errorf("Bad: Get on availSetClient: %+v", err) + if vmss.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: VirtualMachineScaleSet %q (resource group: %q) does not exist", name, resourceGroup) } return nil @@ -227,7 +258,7 @@ func testCheckAzureRMAvailabilitySetDisappears(resourceName string) resource.Tes return fmt.Errorf("Bad: no resource group found in state for availability set: %s", availSetName) } - client := testAccProvider.Meta().(*ArmClient).availSetClient + client := testAccProvider.Meta().(*ArmClient).compute.AvailabilitySetsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Delete(ctx, resourceGroup, availSetName) if err != nil { @@ -249,7 +280,7 @@ func testCheckAzureRMAvailabilitySetDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).availSetClient + client := testAccProvider.Meta().(*ArmClient).compute.AvailabilitySetsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -333,6 +364,29 @@ resource "azurerm_availability_set" "test" { `, rInt, location, rInt) } +func testAccAzureRMAvailabilitySet_withPPG(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_proximity_placement_group" "test" { + name = "acctestPPG-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_availability_set" "test" { + name = "acctestavset-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + proximity_placement_group_id = "${azurerm_proximity_placement_group.test.id}" +} +`, rInt, location, rInt, rInt) +} + func testAccAzureRMAvailabilitySet_withDomainCounts(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_azuread_application.go b/azurerm/resource_arm_azuread_application.go index b6ce02826768..606cc0c3f6c5 100644 --- a/azurerm/resource_arm_azuread_application.go +++ b/azurerm/resource_arm_azuread_application.go @@ -81,7 +81,7 @@ As such the Azure Active Directory resources within the AzureRM Provider are now } func resourceArmActiveDirectoryApplicationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationsClient + client := meta.(*ArmClient).graph.ApplicationsClient ctx := meta.(*ArmClient).StopContext // NOTE: name isn't the Resource ID here, so we don't check it exists @@ -111,7 +111,7 @@ func resourceArmActiveDirectoryApplicationCreate(d *schema.ResourceData, meta in } func resourceArmActiveDirectoryApplicationUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationsClient + client := meta.(*ArmClient).graph.ApplicationsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -152,7 +152,7 @@ func resourceArmActiveDirectoryApplicationUpdate(d *schema.ResourceData, meta in } func resourceArmActiveDirectoryApplicationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationsClient + client := meta.(*ArmClient).graph.ApplicationsClient ctx := meta.(*ArmClient).StopContext resp, err := client.Get(ctx, d.Id()) @@ -192,7 +192,7 @@ func resourceArmActiveDirectoryApplicationRead(d *schema.ResourceData, meta inte } func resourceArmActiveDirectoryApplicationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).applicationsClient + client := meta.(*ArmClient).graph.ApplicationsClient ctx := meta.(*ArmClient).StopContext // in order to delete an application which is available to other tenants, we first have to disable this setting diff --git a/azurerm/resource_arm_azuread_application_test.go b/azurerm/resource_arm_azuread_application_test.go index 0031b274c5a3..7822ffde2e1b 100644 --- a/azurerm/resource_arm_azuread_application_test.go +++ b/azurerm/resource_arm_azuread_application_test.go @@ -138,7 +138,7 @@ func testCheckAzureRMActiveDirectoryApplicationExists(resourceName string) resou return fmt.Errorf("Not found: %q", resourceName) } - client := testAccProvider.Meta().(*ArmClient).applicationsClient + client := testAccProvider.Meta().(*ArmClient).graph.ApplicationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, rs.Primary.ID) @@ -159,7 +159,7 @@ func testCheckAzureRMActiveDirectoryApplicationDestroy(s *terraform.State) error continue } - client := testAccProvider.Meta().(*ArmClient).applicationsClient + client := testAccProvider.Meta().(*ArmClient).graph.ApplicationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, rs.Primary.ID) diff --git a/azurerm/resource_arm_azuread_service_principal.go b/azurerm/resource_arm_azuread_service_principal.go index b422b1a144ca..a40a07a5ad4a 100644 --- a/azurerm/resource_arm_azuread_service_principal.go +++ b/azurerm/resource_arm_azuread_service_principal.go @@ -43,7 +43,7 @@ As such the Azure Active Directory resources within the AzureRM Provider are now } func resourceArmActiveDirectoryServicePrincipalCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext applicationId := d.Get("application_id").(string) @@ -93,7 +93,7 @@ func resourceArmActiveDirectoryServicePrincipalCreate(d *schema.ResourceData, me } func resourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext objectId := d.Id() @@ -114,7 +114,7 @@ func resourceArmActiveDirectoryServicePrincipalRead(d *schema.ResourceData, meta } func resourceArmActiveDirectoryServicePrincipalDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext applicationId := d.Id() diff --git a/azurerm/resource_arm_azuread_service_principal_password.go b/azurerm/resource_arm_azuread_service_principal_password.go index 33a87042d21f..79b325a8d620 100644 --- a/azurerm/resource_arm_azuread_service_principal_password.go +++ b/azurerm/resource_arm_azuread_service_principal_password.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -72,7 +73,7 @@ As such the Azure Active Directory resources within the AzureRM Provider are now } func resourceArmActiveDirectoryServicePrincipalPasswordCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext objectId := d.Get("service_principal_id").(string) @@ -104,8 +105,8 @@ func resourceArmActiveDirectoryServicePrincipalPasswordCreate(d *schema.Resource credential.StartDate = &date.Time{Time: startDate} } - azureRMLockByName(objectId, servicePrincipalResourceName) - defer azureRMUnlockByName(objectId, servicePrincipalResourceName) + locks.ByName(objectId, servicePrincipalResourceName) + defer locks.UnlockByName(objectId, servicePrincipalResourceName) existingCredentials, err := client.ListPasswordCredentials(ctx, objectId) if err != nil { @@ -143,7 +144,7 @@ func resourceArmActiveDirectoryServicePrincipalPasswordCreate(d *schema.Resource } func resourceArmActiveDirectoryServicePrincipalPasswordRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext id := strings.Split(d.Id(), "/") @@ -205,7 +206,7 @@ func resourceArmActiveDirectoryServicePrincipalPasswordRead(d *schema.ResourceDa } func resourceArmActiveDirectoryServicePrincipalPasswordDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicePrincipalsClient + client := meta.(*ArmClient).graph.ServicePrincipalsClient ctx := meta.(*ArmClient).StopContext id := strings.Split(d.Id(), "/") @@ -216,8 +217,8 @@ func resourceArmActiveDirectoryServicePrincipalPasswordDelete(d *schema.Resource objectId := id[0] keyId := id[1] - azureRMLockByName(objectId, servicePrincipalResourceName) - defer azureRMUnlockByName(objectId, servicePrincipalResourceName) + locks.ByName(objectId, servicePrincipalResourceName) + defer locks.UnlockByName(objectId, servicePrincipalResourceName) // ensure the parent Service Principal exists servicePrincipal, err := client.Get(ctx, objectId) diff --git a/azurerm/resource_arm_azuread_service_principal_password_test.go b/azurerm/resource_arm_azuread_service_principal_password_test.go index 5c7e1a5f15b1..6332ace8cf40 100644 --- a/azurerm/resource_arm_azuread_service_principal_password_test.go +++ b/azurerm/resource_arm_azuread_service_principal_password_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,7 +45,7 @@ func TestAccAzureRMActiveDirectoryServicePrincipalPassword_basic(t *testing.T) { } func TestAccAzureRMActiveDirectoryServicePrincipalPassword_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -120,7 +121,7 @@ func testCheckAzureRMActiveDirectoryServicePrincipalPasswordExists(resourceName return fmt.Errorf("Not found: %q", resourceName) } - client := testAccProvider.Meta().(*ArmClient).servicePrincipalsClient + client := testAccProvider.Meta().(*ArmClient).graph.ServicePrincipalsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext id := strings.Split(rs.Primary.ID, "/") diff --git a/azurerm/resource_arm_azuread_service_principal_test.go b/azurerm/resource_arm_azuread_service_principal_test.go index 46ba1ae7ddd7..f0e670078f6b 100644 --- a/azurerm/resource_arm_azuread_service_principal_test.go +++ b/azurerm/resource_arm_azuread_service_principal_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMActiveDirectoryServicePrincipal_basic(t *testing.T) { } func TestAccAzureRMActiveDirectoryServicePrincipal_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -72,7 +73,7 @@ func testCheckAzureRMActiveDirectoryServicePrincipalExists(resourceName string) return fmt.Errorf("Not found: %q", resourceName) } - client := testAccProvider.Meta().(*ArmClient).servicePrincipalsClient + client := testAccProvider.Meta().(*ArmClient).graph.ServicePrincipalsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, rs.Primary.ID) @@ -93,7 +94,7 @@ func testCheckAzureRMActiveDirectoryServicePrincipalDestroy(s *terraform.State) continue } - client := testAccProvider.Meta().(*ArmClient).servicePrincipalsClient + client := testAccProvider.Meta().(*ArmClient).graph.ServicePrincipalsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, rs.Primary.ID) diff --git a/azurerm/resource_arm_batch_account.go b/azurerm/resource_arm_batch_account.go index bf733320e00b..45c6e93035ac 100644 --- a/azurerm/resource_arm_batch_account.go +++ b/azurerm/resource_arm_batch_account.go @@ -5,6 +5,10 @@ import ( "log" "regexp" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -52,6 +56,25 @@ func resourceArmBatchAccount() *schema.Resource { string(batch.UserSubscription), }, false), }, + "key_vault_reference": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.URLIsHTTPS, + }, + }, + }, + }, "primary_access_key": { Type: schema.TypeString, Sensitive: true, @@ -66,13 +89,13 @@ func resourceArmBatchAccount() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmBatchAccountCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchAccountClient + client := meta.(*ArmClient).batch.AccountClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Batch account creation.") @@ -82,9 +105,9 @@ func resourceArmBatchAccountCreate(d *schema.ResourceData, meta interface{}) err location := azure.NormalizeLocation(d.Get("location").(string)) storageAccountId := d.Get("storage_account_id").(string) poolAllocationMode := d.Get("pool_allocation_mode").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -102,7 +125,22 @@ func resourceArmBatchAccountCreate(d *schema.ResourceData, meta interface{}) err AccountCreateProperties: &batch.AccountCreateProperties{ PoolAllocationMode: batch.PoolAllocationMode(poolAllocationMode), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), + } + + // if pool allocation mode is UserSubscription, a key vault reference needs to be set + if poolAllocationMode == string(batch.UserSubscription) { + keyVaultReferenceSet := d.Get("key_vault_reference").([]interface{}) + keyVaultReference, err := azure.ExpandBatchAccountKeyVaultReference(keyVaultReferenceSet) + if err != nil { + return fmt.Errorf("Error creating Batch account %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if keyVaultReference == nil { + return fmt.Errorf("Error creating Batch account %q (Resource Group %q): When setting pool allocation mode to UserSubscription, a Key Vault reference needs to be set", name, resourceGroup) + } + + parameters.KeyVaultReference = keyVaultReference } if storageAccountId != "" { @@ -135,10 +173,10 @@ func resourceArmBatchAccountCreate(d *schema.ResourceData, meta interface{}) err } func resourceArmBatchAccountRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchAccountClient + client := meta.(*ArmClient).batch.AccountClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -181,18 +219,16 @@ func resourceArmBatchAccountRead(d *schema.ResourceData, meta interface{}) error d.Set("secondary_access_key", keys.Secondary) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmBatchAccountUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchAccountClient + client := meta.(*ArmClient).batch.AccountClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Batch account update.") - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -200,7 +236,7 @@ func resourceArmBatchAccountUpdate(d *schema.ResourceData, meta interface{}) err resourceGroup := id.ResourceGroup storageAccountId := d.Get("storage_account_id").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := batch.AccountUpdateParameters{ AccountUpdateProperties: &batch.AccountUpdateProperties{ @@ -208,7 +244,7 @@ func resourceArmBatchAccountUpdate(d *schema.ResourceData, meta interface{}) err StorageAccountID: &storageAccountId, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = client.Update(ctx, resourceGroup, name, parameters); err != nil { @@ -230,10 +266,10 @@ func resourceArmBatchAccountUpdate(d *schema.ResourceData, meta interface{}) err } func resourceArmBatchAccountDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchAccountClient + client := meta.(*ArmClient).batch.AccountClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_batch_account_test.go b/azurerm/resource_arm_batch_account_test.go index 2127542d06c0..504c782b5ef7 100644 --- a/azurerm/resource_arm_batch_account_test.go +++ b/azurerm/resource_arm_batch_account_test.go @@ -3,12 +3,14 @@ package azurerm import ( "fmt" "net/http" + "os" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -63,7 +65,7 @@ func TestAccAzureRMBatchAccount_basic(t *testing.T) { } func TestAccAzureRMBatchAccount_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -130,6 +132,32 @@ func TestAccAzureRMBatchAccount_complete(t *testing.T) { }) } +func TestAccAzureRMBatchAccount_userSubscription(t *testing.T) { + resourceName := "azurerm_batch_account.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + tenantID := os.Getenv("ARM_TENANT_ID") + + config := testAccAzureRMBatchAccount_userSubscription(ri, rs, location, tenantID) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBatchAccountDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBatchAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "pool_allocation_mode", "UserSubscription"), + ), + }, + }, + }) +} + func testCheckAzureRMBatchAccountExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -143,7 +171,7 @@ func testCheckAzureRMBatchAccountExists(resourceName string) resource.TestCheckF // Ensure resource group exists in API ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).batchAccountClient + conn := testAccProvider.Meta().(*ArmClient).batch.AccountClient resp, err := conn.Get(ctx, resourceGroup, batchAccount) if err != nil { @@ -159,7 +187,7 @@ func testCheckAzureRMBatchAccountExists(resourceName string) resource.TestCheckF } func testCheckAzureRMBatchAccountDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).batchAccountClient + conn := testAccProvider.Meta().(*ArmClient).batch.AccountClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -270,3 +298,56 @@ resource "azurerm_batch_account" "test" { } `, rInt, location, rString, rString) } + +func testAccAzureRMBatchAccount_userSubscription(rInt int, batchAccountSuffix string, location string, tenantID string) string { + return fmt.Sprintf(` +data "azuread_service_principal" "test" { + display_name = "Microsoft Azure Batch" +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-%d-batchaccount" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "batchkv%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + enabled_for_disk_encryption = true + enabled_for_deployment = true + enabled_for_template_deployment = true + tenant_id = "%s" + + sku { + name = "standard" + } + + access_policy { + tenant_id = "%s" + object_id = "${data.azuread_service_principal.test.object_id}" + + secret_permissions = [ + "get", + "list", + "set", + "delete" + ] + + } +} + +resource "azurerm_batch_account" "test" { + name = "testaccbatch%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + pool_allocation_mode = "UserSubscription" + + key_vault_reference { + id = "${azurerm_key_vault.test.id}" + url = "${azurerm_key_vault.test.vault_uri}" + } +} +`, rInt, location, batchAccountSuffix, tenantID, tenantID, batchAccountSuffix) +} diff --git a/azurerm/resource_arm_batch_application.go b/azurerm/resource_arm_batch_application.go new file mode 100644 index 000000000000..74bce9103076 --- /dev/null +++ b/azurerm/resource_arm_batch_application.go @@ -0,0 +1,240 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmBatchApplication() *schema.Resource { + return &schema.Resource{ + Create: resourceArmBatchApplicationCreate, + Read: resourceArmBatchApplicationRead, + Update: resourceArmBatchApplicationUpdate, + Delete: resourceArmBatchApplicationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMBatchApplicationName, + }, + + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMBatchAccountName, + }, + + "allow_updates": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "default_version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateAzureRMBatchApplicationVersion, + }, + + "display_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateAzureRMBatchApplicationDisplayName, + }, + }, + } +} + +func resourceArmBatchApplicationCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).batch.ApplicationClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + accountName := d.Get("account_name").(string) + + if features.ShouldResourcesBeImported() { + resp, err := client.Get(ctx, resourceGroup, accountName, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error checking for present of existing Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + } + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_batch_application", *resp.ID) + } + } + + allowUpdates := d.Get("allow_updates").(bool) + defaultVersion := d.Get("default_version").(string) + displayName := d.Get("display_name").(string) + + parameters := batch.Application{ + ApplicationProperties: &batch.ApplicationProperties{ + AllowUpdates: utils.Bool(allowUpdates), + DefaultVersion: utils.String(defaultVersion), + DisplayName: utils.String(displayName), + }, + } + + if _, err := client.Create(ctx, resourceGroup, accountName, name, ¶meters); err != nil { + return fmt.Errorf("Error creating Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, accountName, name) + if err != nil { + return fmt.Errorf("Error retrieving Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + if resp.ID == nil { + return fmt.Errorf("Cannot read Batch Application %q (Account Name %q / Resource Group %q) ID", name, accountName, resourceGroup) + } + d.SetId(*resp.ID) + + return resourceArmBatchApplicationRead(d, meta) +} + +func resourceArmBatchApplicationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).batch.ApplicationClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + accountName := id.Path["batchAccounts"] + name := id.Path["applications"] + + resp, err := client.Get(ctx, resourceGroup, accountName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Batch Application %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + d.Set("account_name", accountName) + if applicationProperties := resp.ApplicationProperties; applicationProperties != nil { + d.Set("allow_updates", applicationProperties.AllowUpdates) + d.Set("default_version", applicationProperties.DefaultVersion) + d.Set("display_name", applicationProperties.DisplayName) + } + + return nil +} + +func resourceArmBatchApplicationUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).batch.ApplicationClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + accountName := d.Get("account_name").(string) + allowUpdates := d.Get("allow_updates").(bool) + defaultVersion := d.Get("default_version").(string) + displayName := d.Get("display_name").(string) + + parameters := batch.Application{ + ApplicationProperties: &batch.ApplicationProperties{ + AllowUpdates: utils.Bool(allowUpdates), + DefaultVersion: utils.String(defaultVersion), + DisplayName: utils.String(displayName), + }, + } + + if _, err := client.Update(ctx, resourceGroup, accountName, name, parameters); err != nil { + return fmt.Errorf("Error updating Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + + return resourceArmBatchApplicationRead(d, meta) +} + +func resourceArmBatchApplicationDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).batch.ApplicationClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + accountName := id.Path["batchAccounts"] + name := id.Path["applications"] + + if _, err := client.Delete(ctx, resourceGroup, accountName, name); err != nil { + return fmt.Errorf("Error deleting Batch Application %q (Account Name %q / Resource Group %q): %+v", name, accountName, resourceGroup, err) + } + + return nil +} + +func validateAzureRMBatchApplicationName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^[-_\da-zA-Z]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%q can contain any combination of alphanumeric characters, hyphens, and underscores: %q", k, value)) + } + + if 1 > len(value) { + errors = append(errors, fmt.Errorf("%q cannot be less than 1 character: %q", k, value)) + } + + if len(value) > 64 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 64 characters: %q %d", k, value, len(value))) + } + + return warnings, errors +} + +func validateAzureRMBatchApplicationVersion(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^[-._\da-zA-Z]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%q can contain any combination of alphanumeric characters, hyphens, underscores, and periods: %q", k, value)) + } + + if 1 > len(value) { + errors = append(errors, fmt.Errorf("%q cannot be less than 1 character: %q", k, value)) + } + + if len(value) > 64 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 64 characters: %q %d", k, value, len(value))) + } + + return warnings, errors +} + +func validateAzureRMBatchApplicationDisplayName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if 1 > len(value) { + errors = append(errors, fmt.Errorf("%q cannot be less than 1 character: %q", k, value)) + } + + if len(value) > 1024 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 1024 characters: %q %d", k, value, len(value))) + } + + return warnings, errors +} diff --git a/azurerm/resource_arm_batch_application_test.go b/azurerm/resource_arm_batch_application_test.go new file mode 100644 index 000000000000..8c2fb855e45b --- /dev/null +++ b/azurerm/resource_arm_batch_application_test.go @@ -0,0 +1,150 @@ +package azurerm + +import ( + "fmt" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMBatchApplication_basic(t *testing.T) { + resourceName := "azurerm_batch_application.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBatchApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMBatchApplication_template(ri, rs, location, ""), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBatchApplicationExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMBatchApplication_update(t *testing.T) { + resourceName := "azurerm_batch_application.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + displayName := fmt.Sprintf("TestAccDisplayName-%d", ri) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBatchApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMBatchApplication_template(ri, rs, location, ""), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBatchApplicationExists(resourceName), + ), + }, + { + Config: testAccAzureRMBatchApplication_template(ri, rs, location, fmt.Sprintf(`display_name = "%s"`, displayName)), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBatchApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "display_name", displayName), + ), + }, + }, + }) +} + +func testCheckAzureRMBatchApplicationExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Batch Application not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + accountName := rs.Primary.Attributes["account_name"] + + client := testAccProvider.Meta().(*ArmClient).batch.ApplicationClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + if resp, err := client.Get(ctx, resourceGroup, accountName, name); err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Batch Application %q (Account Name %q / Resource Group %q) does not exist", name, accountName, resourceGroup) + } + return fmt.Errorf("Bad: Get on batchApplicationClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMBatchApplicationDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).batch.ApplicationClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_batch_application" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + accountName := rs.Primary.Attributes["account_name"] + + if resp, err := client.Get(ctx, resourceGroup, accountName, name); err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Get on batchApplicationClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testAccAzureRMBatchApplication_template(rInt int, rString string, location string, displayName string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_batch_account" "test" { + name = "acctestba%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + pool_allocation_mode = "BatchService" + storage_account_id = "${azurerm_storage_account.test.id}" +} + +resource "azurerm_batch_application" "test" { + name = "acctestbatchapp-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + %s +} +`, rInt, location, rString, rString, rInt, displayName) +} diff --git a/azurerm/resource_arm_batch_certificate.go b/azurerm/resource_arm_batch_certificate.go index 2f9fc5a003cc..506b9fc4e479 100644 --- a/azurerm/resource_arm_batch_certificate.go +++ b/azurerm/resource_arm_batch_certificate.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -71,10 +72,11 @@ func resourceArmBatchCertificate() *schema.Resource { }, "thumbprint_algorithm": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"SHA1"}, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"SHA1"}, false), + DiffSuppressFunc: suppress.CaseDifference, }, "public_data": { @@ -86,7 +88,7 @@ func resourceArmBatchCertificate() *schema.Resource { } func resourceArmBatchCertificateCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchCertificateClient + client := meta.(*ArmClient).batch.CertificateClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Batch certificate creation.") @@ -104,7 +106,7 @@ func resourceArmBatchCertificateCreate(d *schema.ResourceData, meta interface{}) return err } - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroupName, accountName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -148,10 +150,10 @@ func resourceArmBatchCertificateCreate(d *schema.ResourceData, meta interface{}) } func resourceArmBatchCertificateRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchCertificateClient + client := meta.(*ArmClient).batch.CertificateClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -184,12 +186,12 @@ func resourceArmBatchCertificateRead(d *schema.ResourceData, meta interface{}) e } func resourceArmBatchCertificateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchCertificateClient + client := meta.(*ArmClient).batch.CertificateClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Batch certificate update.") - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -235,10 +237,10 @@ func resourceArmBatchCertificateUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmBatchCertificateDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchCertificateClient + client := meta.(*ArmClient).batch.CertificateClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_batch_certificate_test.go b/azurerm/resource_arm_batch_certificate_test.go index 1f67b5e455d9..daa42d594d40 100644 --- a/azurerm/resource_arm_batch_certificate_test.go +++ b/azurerm/resource_arm_batch_certificate_test.go @@ -20,7 +20,7 @@ func TestAccAzureRMBatchCertificate_Pfx(t *testing.T) { location := testLocation() subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") - certificateID := fmt.Sprintf("/subscriptions/%s/resourceGroups/testaccbatch%d/providers/Microsoft.Batch/batchAccounts/testaccbatch%s/certificates/SHA1-42C107874FD0E4A9583292A2F1098E8FE4B2EDDA", subscriptionID, ri, rs) + certificateID := fmt.Sprintf("/subscriptions/%s/resourceGroups/testaccbatch%d/providers/Microsoft.Batch/batchAccounts/testaccbatch%s/certificates/sha1-42c107874fd0e4a9583292a2f1098e8fe4b2edda", subscriptionID, ri, rs) config := testAccAzureRMBatchCertificatePfx(ri, rs, location) @@ -34,10 +34,16 @@ func TestAccAzureRMBatchCertificate_Pfx(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "id", certificateID), resource.TestCheckResourceAttr(resourceName, "format", "Pfx"), - resource.TestCheckResourceAttr(resourceName, "thumbprint", "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA"), - resource.TestCheckResourceAttr(resourceName, "thumbprint_algorithm", "SHA1"), + resource.TestCheckResourceAttr(resourceName, "thumbprint", "42c107874fd0e4a9583292a2f1098e8fe4b2edda"), + resource.TestCheckResourceAttr(resourceName, "thumbprint_algorithm", "sha1"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"certificate", "password"}, + }, }, }) } @@ -69,7 +75,7 @@ func TestAccAzureRMBatchCertificate_Cer(t *testing.T) { location := testLocation() subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") - certificateID := fmt.Sprintf("/subscriptions/%s/resourceGroups/testaccbatch%d/providers/Microsoft.Batch/batchAccounts/testaccbatch%s/certificates/SHA1-312D31A79FA0CEF49C00F769AFC2B73E9F4EDF34", subscriptionID, ri, rs) + certificateID := fmt.Sprintf("/subscriptions/%s/resourceGroups/testaccbatch%d/providers/Microsoft.Batch/batchAccounts/testaccbatch%s/certificates/sha1-312d31a79fa0cef49c00f769afc2b73e9f4edf34", subscriptionID, ri, rs) config := testAccAzureRMBatchCertificateCer(ri, rs, location) @@ -84,10 +90,16 @@ func TestAccAzureRMBatchCertificate_Cer(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "id", certificateID), resource.TestCheckResourceAttr(resourceName, "format", "Cer"), - resource.TestCheckResourceAttr(resourceName, "thumbprint", "312D31A79FA0CEF49C00F769AFC2B73E9F4EDF34"), - resource.TestCheckResourceAttr(resourceName, "thumbprint_algorithm", "SHA1"), + resource.TestCheckResourceAttr(resourceName, "thumbprint", "312d31a79fa0cef49c00f769afc2b73e9f4edf34"), + resource.TestCheckResourceAttr(resourceName, "thumbprint_algorithm", "sha1"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"certificate"}, + }, }, }) } @@ -127,13 +139,13 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.pfx")}" - format = "Pfx" - password = "terraform" - thumbprint = "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.pfx")}" + format = "Pfx" + password = "terraform" + thumbprint = "42c107874fd0e4a9583292a2f1098e8fe4b2edda" + thumbprint_algorithm = "SHA1" } `, rInt, location, batchAccountSuffix) } @@ -153,12 +165,12 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.pfx")}" - format = "Pfx" - thumbprint = "42C107874FD0E4A9583292A2F1098E8FE4B2EDDA" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.pfx")}" + format = "Pfx" + thumbprint = "42c107874fd0e4a9583292a2f1098e8fe4b2edda" + thumbprint_algorithm = "SHA1" } `, rInt, location, batchAccountSuffix) } @@ -177,12 +189,12 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.cer")}" - format = "Cer" - thumbprint = "312D31A79FA0CEF49C00F769AFC2B73E9F4EDF34" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.cer")}" + format = "Cer" + thumbprint = "312d31a79fa0cef49c00f769afc2b73e9f4edf34" + thumbprint_algorithm = "SHA1" } `, rInt, location, batchAccountSuffix) } @@ -201,19 +213,19 @@ resource "azurerm_batch_account" "test" { } resource "azurerm_batch_certificate" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - account_name = "${azurerm_batch_account.test.name}" - certificate = "${filebase64("testdata/batch_certificate.cer")}" - format = "Cer" - password = "should not have a password for Cer" - thumbprint = "312D31A79FA0CEF49C00F769AFC2B73E9F4EDF34" - thumbprint_algorithm = "SHA1" + resource_group_name = "${azurerm_resource_group.test.name}" + account_name = "${azurerm_batch_account.test.name}" + certificate = "${filebase64("testdata/batch_certificate.cer")}" + format = "Cer" + password = "should not have a password for Cer" + thumbprint = "312d31a79fa0cef49c00f769afc2b73e9f4edf34" + thumbprint_algorithm = "SHA1" } `, rInt, location, batchAccountSuffix) } func testCheckAzureRMBatchCertificateDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).batchCertificateClient + conn := testAccProvider.Meta().(*ArmClient).batch.CertificateClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_batch_pool.go b/azurerm/resource_arm_batch_pool.go index ed30759f6b56..cdba850928c7 100644 --- a/azurerm/resource_arm_batch_pool.go +++ b/azurerm/resource_arm_batch_pool.go @@ -8,16 +8,18 @@ import ( "strings" "time" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" - - "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "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/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" ) func resourceArmBatchPool() *schema.Resource { @@ -116,6 +118,7 @@ func resourceArmBatchPool() *schema.Resource { "container_configuration": { Type: schema.TypeList, Optional: true, + MinItems: 1, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -124,6 +127,35 @@ func resourceArmBatchPool() *schema.Resource { Optional: true, ValidateFunc: validate.NoEmptyStrings, }, + "container_registries": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "registry_server": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "user_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "password": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, }, }, }, @@ -137,26 +169,27 @@ func resourceArmBatchPool() *schema.Resource { "id": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: azure.ValidateResourceID, }, "publisher": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validate.NoEmptyStrings, }, "offer": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validate.NoEmptyStrings, }, "sku": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validate.NoEmptyStrings, @@ -164,7 +197,7 @@ func resourceArmBatchPool() *schema.Resource { "version": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validate.NoEmptyStrings, }, @@ -251,6 +284,9 @@ func resourceArmBatchPool() *schema.Resource { "environment": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "user_identity": { @@ -334,7 +370,7 @@ func resourceArmBatchPool() *schema.Resource { } func resourceArmBatchPoolCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).batchPoolClient + client := meta.(*ArmClient).batch.PoolClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Batch pool creation.") @@ -346,7 +382,7 @@ func resourceArmBatchPoolCreate(d *schema.ResourceData, meta interface{}) error vmSize := d.Get("vm_size").(string) maxTasksPerNode := int32(d.Get("max_tasks_per_node").(int)) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, accountName, poolName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -382,6 +418,17 @@ func resourceArmBatchPoolCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error creating Batch pool %q (Resource Group %q): %+v", poolName, resourceGroup, err) } + if imageReference != nil { + // if an image reference ID is specified, the user wants use a custom image. This property is mutually exclusive with other properties. + if imageReference.ID != nil && (imageReference.Offer != nil || imageReference.Publisher != nil || imageReference.Sku != nil || imageReference.Version != nil) { + return fmt.Errorf("Error creating Batch pool %q (Resource Group %q): Properties version, offer, publish cannot be defined when using a custom image id", poolName, resourceGroup) + } else if imageReference.ID == nil && (imageReference.Offer == nil || imageReference.Publisher == nil || imageReference.Sku == nil || imageReference.Version == nil) { + return fmt.Errorf("Error creating Batch pool %q (Resource Group %q): Properties version, offer, publish and sku are mandatory when not using a custom image", poolName, resourceGroup) + } + } else { + return fmt.Errorf("Error creating Batch pool %q (Resource Group %q): image reference property can not be empty", poolName, resourceGroup) + } + if startTaskValue, startTaskOk := d.GetOk("start_task"); startTaskOk { startTaskList := startTaskValue.([]interface{}) startTask, startTaskErr := azure.ExpandBatchPoolStartTask(startTaskList) @@ -456,9 +503,9 @@ func resourceArmBatchPoolCreate(d *schema.ResourceData, meta interface{}) error func resourceArmBatchPoolUpdate(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).batchPoolClient + client := meta.(*ArmClient).batch.PoolClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -544,9 +591,9 @@ func resourceArmBatchPoolUpdate(d *schema.ResourceData, meta interface{}) error func resourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).batchPoolClient + client := meta.(*ArmClient).batch.PoolClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -581,7 +628,6 @@ func resourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { if props.DeploymentConfiguration != nil && props.DeploymentConfiguration.VirtualMachineConfiguration != nil && props.DeploymentConfiguration.VirtualMachineConfiguration.ImageReference != nil { - imageReference := props.DeploymentConfiguration.VirtualMachineConfiguration.ImageReference d.Set("storage_image_reference", azure.FlattenBatchPoolImageReference(imageReference)) @@ -590,7 +636,7 @@ func resourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { if dcfg := props.DeploymentConfiguration; dcfg != nil { if vmcfg := dcfg.VirtualMachineConfiguration; vmcfg != nil { - d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(vmcfg.ContainerConfiguration)) + d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(d, vmcfg.ContainerConfiguration)) } } @@ -606,9 +652,9 @@ func resourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { func resourceArmBatchPoolDelete(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).batchPoolClient + client := meta.(*ArmClient).batch.PoolClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -680,7 +726,7 @@ func expandBatchPoolScaleSettings(d *schema.ResourceData) (*batch.ScaleSettings, return scaleSettings, nil } -func waitForBatchPoolPendingResizeOperation(ctx context.Context, client batch.PoolClient, resourceGroup string, accountName string, poolName string) error { +func waitForBatchPoolPendingResizeOperation(ctx context.Context, client *batch.PoolClient, resourceGroup string, accountName string, poolName string) error { // waiting for the pool to be in steady state log.Printf("[INFO] waiting for the pending resize operation on this pool to be stopped...") isSteady := false diff --git a/azurerm/resource_arm_batch_pool_test.go b/azurerm/resource_arm_batch_pool_test.go index 6b7ed79fee92..9a41ca3ebed9 100644 --- a/azurerm/resource_arm_batch_pool_test.go +++ b/azurerm/resource_arm_batch_pool_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -46,7 +47,7 @@ func TestAccAzureRMBatchPool_basic(t *testing.T) { } func TestAccAzureRMBatchPool_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -311,6 +312,10 @@ func TestAccAzureRMBatchPool_container(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureRMBatchPoolExists(resourceName), resource.TestCheckResourceAttr(resourceName, "container_configuration.0.type", "DockerCompatible"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.registry_server", "myContainerRegistry.azurecr.io"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.user_name", "myUserName"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.password", "myPassword"), ), }, }, @@ -368,6 +373,36 @@ func TestAccAzureRMBatchPool_validateResourceFileHttpURLWithoutFilePath(t *testi }) } +func TestAccAzureRMBatchPool_customImage(t *testing.T) { + resourceName := "azurerm_batch_pool.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBatchPoolDestroy, + Steps: []resource.TestStep{ + { + Config: testaccAzureRMBatchPoolCustomImageConfiguration(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBatchPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "vm_size", "STANDARD_A1"), + resource.TestCheckResourceAttr(resourceName, "max_tasks_per_node", "2"), + resource.TestCheckResourceAttr(resourceName, "node_agent_sku_id", "batch.node.ubuntu 16.04"), + resource.TestCheckResourceAttr(resourceName, "account_name", fmt.Sprintf("testaccbatch%s", rs)), + resource.TestCheckResourceAttr(resourceName, "auto_scale.#", "0"), + resource.TestCheckResourceAttr(resourceName, "fixed_scale.#", "1"), + resource.TestCheckResourceAttr(resourceName, "fixed_scale.0.target_dedicated_nodes", "2"), + resource.TestCheckResourceAttr(resourceName, "fixed_scale.0.resize_timeout", "PT15M"), + resource.TestCheckResourceAttr(resourceName, "fixed_scale.0.target_low_priority_nodes", "0"), + resource.TestCheckResourceAttr(resourceName, "start_task.#", "0"), + ), + }, + }, + }) +} + func testCheckAzureRMBatchPoolExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -381,7 +416,7 @@ func testCheckAzureRMBatchPoolExists(name string) resource.TestCheckFunc { accountName := rs.Primary.Attributes["account_name"] ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).batchPoolClient + conn := testAccProvider.Meta().(*ArmClient).batch.PoolClient resp, err := conn.Get(ctx, resourceGroup, accountName, poolName) if err != nil { @@ -407,7 +442,7 @@ func testCheckAzureRMBatchPoolDestroy(s *terraform.State) error { accountName := rs.Primary.Attributes["account_name"] ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).batchPoolClient + conn := testAccProvider.Meta().(*ArmClient).batch.PoolClient resp, err := conn.Get(ctx, resourceGroup, accountName, poolName) if err != nil { @@ -457,11 +492,11 @@ resource "azurerm_batch_pool" "test" { vm_size = "Standard_A1" max_tasks_per_node = 2 node_agent_sku_id = "batch.node.ubuntu 16.04" - + fixed_scale { target_dedicated_nodes = 2 } - + storage_image_reference { publisher = "Canonical" offer = "UbuntuServer" @@ -510,7 +545,8 @@ resource "azurerm_batch_pool" "test" { auto_scale { evaluation_interval = "PT15M" - formula = < 0 { - for _, httpget := range httpRaw { x := httpget.(map[string]interface{}) @@ -900,7 +1023,7 @@ func expandContainerProbe(input interface{}) *containerinstance.ContainerProbe { return &probe } -func flattenContainerGroupIdentity(d *schema.ResourceData, identity *containerinstance.ContainerGroupIdentity) []interface{} { +func flattenContainerGroupIdentity(identity *containerinstance.ContainerGroupIdentity) []interface{} { if identity == nil { return make([]interface{}, 0) } @@ -962,7 +1085,6 @@ func flattenContainerImageRegistryCredentials(d *schema.ResourceData, input *[]c } func flattenContainerGroupContainers(d *schema.ResourceData, containers *[]containerinstance.Container, containerGroupPorts *[]containerinstance.Port, containerGroupVolumes *[]containerinstance.Volume) []interface{} { - //map old container names to index so we can look up things up nameIndexMap := map[string]int{} for i, c := range d.Get("container").([]interface{}) { @@ -972,7 +1094,6 @@ func flattenContainerGroupContainers(d *schema.ResourceData, containers *[]conta containerCfg := make([]interface{}, 0, len(*containers)) for _, container := range *containers { - //TODO fix this crash point name := *container.Name @@ -1093,7 +1214,6 @@ func flattenContainerEnvironmentVariables(input *[]containerinstance.Environment if isSecure { for _, envVar := range *input { - if envVar.Name != nil && envVar.Value == nil { if v, ok := d.GetOk(fmt.Sprintf("container.%d.secure_environment_variables.%s", oldContainerIndex, *envVar.Name)); ok { log.Printf("[DEBUG] SECURE : Name: %s - Value: %s", *envVar.Name, v.(string)) @@ -1241,23 +1361,26 @@ func expandContainerGroupDiagnostics(input []interface{}) *containerinstance.Con workspaceId := analyticsV["workspace_id"].(string) workspaceKey := analyticsV["workspace_key"].(string) - logType := containerinstance.LogAnalyticsLogType(analyticsV["log_type"].(string)) - metadataMap := analyticsV["metadata"].(map[string]interface{}) - metadata := make(map[string]*string) - for k, v := range metadataMap { - strValue := v.(string) - metadata[k] = &strValue + logAnalytics := containerinstance.LogAnalytics{ + WorkspaceID: utils.String(workspaceId), + WorkspaceKey: utils.String(workspaceKey), } - return &containerinstance.ContainerGroupDiagnostics{ - LogAnalytics: &containerinstance.LogAnalytics{ - WorkspaceID: utils.String(workspaceId), - WorkspaceKey: utils.String(workspaceKey), - LogType: logType, - Metadata: metadata, - }, + if logType := analyticsV["log_type"].(string); logType != "" { + logAnalytics.LogType = containerinstance.LogAnalyticsLogType(logType) + + metadataMap := analyticsV["metadata"].(map[string]interface{}) + metadata := make(map[string]*string) + for k, v := range metadataMap { + strValue := v.(string) + metadata[k] = &strValue + } + + logAnalytics.Metadata = metadata } + + return &containerinstance.ContainerGroupDiagnostics{LogAnalytics: &logAnalytics} } func flattenContainerGroupDiagnostics(d *schema.ResourceData, input *containerinstance.ContainerGroupDiagnostics) []interface{} { diff --git a/azurerm/resource_arm_container_group_test.go b/azurerm/resource_arm_container_group_test.go old mode 100755 new mode 100644 index 8955e49fc81a..434f47738f65 --- a/azurerm/resource_arm_container_group_test.go +++ b/azurerm/resource_arm_container_group_test.go @@ -2,11 +2,11 @@ package azurerm import ( "fmt" - "net/http" "testing" "github.com/hashicorp/terraform/helper/acctest" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" @@ -182,6 +182,38 @@ func TestAccAzureRMContainerGroup_imageRegistryCredentialsUpdate(t *testing.T) { }) } +func TestAccAzureRMContainerGroup_logTypeUnset(t *testing.T) { + resourceName := "azurerm_container_group.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMContainerGroup_logTypeUnset(ri, testLocation()) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "diagnostics.0.log_analytics.#", "1"), + resource.TestCheckResourceAttr(resourceName, "diagnostics.0.log_analytics.0.log_type", ""), + resource.TestCheckResourceAttr(resourceName, "diagnostics.0.log_analytics.0.metadata.%", "0"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_id"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_key"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "diagnostics.0.log_analytics.0.workspace_key", + }, + }, + }, + }) +} + func TestAccAzureRMContainerGroup_linuxBasic(t *testing.T) { resourceName := "azurerm_container_group.test" ri := tf.AccRandTimeInt() @@ -216,7 +248,7 @@ func TestAccAzureRMContainerGroup_linuxBasic(t *testing.T) { } func TestAccAzureRMContainerGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -355,6 +387,33 @@ func TestAccAzureRMContainerGroup_linuxComplete(t *testing.T) { }) } +func TestAccAzureRMContainerGroup_virtualNetwork(t *testing.T) { + resourceName := "azurerm_container_group.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMContainerGroup_virtualNetwork(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerGroupExists(resourceName), + resource.TestCheckNoResourceAttr(resourceName, "dns_label_name"), + resource.TestCheckNoResourceAttr(resourceName, "identity"), + resource.TestCheckResourceAttr(resourceName, "container.#", "1"), + resource.TestCheckResourceAttr(resourceName, "os_type", "Linux"), + resource.TestCheckResourceAttr(resourceName, "container.0.port", "80"), + resource.TestCheckResourceAttr(resourceName, "ip_address_type", "Private"), + resource.TestCheckResourceAttrSet(resourceName, "network_profile_id"), + ), + }, + }, + }) +} + func TestAccAzureRMContainerGroup_windowsBasic(t *testing.T) { resourceName := "azurerm_container_group.test" ri := tf.AccRandTimeInt() @@ -478,9 +537,9 @@ resource "azurerm_container_group" "test" { port = 80 } - identity { - type = "SystemAssigned" - } + identity { + type = "SystemAssigned" + } tags = { environment = "Testing" @@ -522,7 +581,7 @@ resource "azurerm_container_group" "test" { type = "UserAssigned" identity_ids = ["${azurerm_user_assigned_identity.test.id}"] } - + tags = { environment = "Testing" } @@ -563,7 +622,7 @@ resource "azurerm_container_group" "test" { type = "SystemAssigned, UserAssigned" identity_ids = ["${azurerm_user_assigned_identity.test.id}"] } - + tags = { environment = "Testing" } @@ -695,7 +754,8 @@ resource "azurerm_container_group" "test" { image = "microsoft/aci-helloworld:latest" cpu = "0.5" memory = "0.5" - ports { + + ports { port = 80 } } @@ -720,6 +780,49 @@ resource "azurerm_container_group" "test" { `, ri, location, ri) } +func testAccAzureRMContainerGroup_logTypeUnset(ri int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_log_analytics_workspace" "test" { + name = "acctestLAW-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "PerGB2018" +} + +resource "azurerm_container_group" "test" { + name = "acctestcontainergroup-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + ip_address_type = "public" + os_type = "Linux" + + container { + name = "hw" + image = "microsoft/aci-helloworld:latest" + cpu = "0.5" + memory = "0.5" + port = 80 + } + + diagnostics { + log_analytics { + workspace_id = "${azurerm_log_analytics_workspace.test.workspace_id}" + workspace_key = "${azurerm_log_analytics_workspace.test.primary_shared_key}" + } + } + + tags = { + environment = "Testing" + } +} +`, ri, location, ri, ri) +} + func testAccAzureRMContainerGroup_linuxBasicUpdated(ri int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -739,9 +842,11 @@ resource "azurerm_container_group" "test" { image = "microsoft/aci-helloworld:latest" cpu = "0.5" memory = "0.5" - ports { - port = 80 + + ports { + port = 80 } + ports { port = 5443 protocol = "UDP" @@ -762,6 +867,74 @@ resource "azurerm_container_group" "test" { `, ri, location, ri) } +func testAccAzureRMContainerGroup_virtualNetwork(ri int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "testvnet" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = ["10.1.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "testsubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.1.0.0/24" + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.ContainerInstance/containerGroups" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_network_profile" "test" { + name = "testnetprofile" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + container_network_interface { + name = "testcnic" + + ip_configuration { + name = "testipconfig" + subnet_id = "${azurerm_subnet.test.id}" + } + } +} + +resource "azurerm_container_group" "test" { + name = "acctestcontainergroup-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + ip_address_type = "Private" + network_profile_id = "${azurerm_network_profile.test.id}" + os_type = "Linux" + + container { + name = "hw" + image = "microsoft/aci-helloworld:latest" + cpu = "0.5" + memory = "0.5" + port = 80 + } + + tags = { + environment = "Testing" + } +} +`, ri, location, ri) +} + func testAccAzureRMContainerGroup_windowsBasic(ri int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -781,10 +954,12 @@ resource "azurerm_container_group" "test" { image = "microsoft/iis:windowsservercore" cpu = "2.0" memory = "3.5" + ports { port = 80 protocol = "TCP" } + ports { port = 443 protocol = "TCP" @@ -839,6 +1014,7 @@ resource "azurerm_container_group" "test" { image = "microsoft/iis:windowsservercore" cpu = "2.0" memory = "3.5" + ports { port = 80 protocol = "TCP" @@ -855,12 +1031,12 @@ resource "azurerm_container_group" "test" { } readiness_probe { - exec = ["cat","/tmp/healthy"] + exec = ["cat", "/tmp/healthy"] initial_delay_seconds = 1 - period_seconds = 1 - failure_threshold = 1 - success_threshold = 1 - timeout_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 } liveness_probe { @@ -869,11 +1045,12 @@ resource "azurerm_container_group" "test" { port = 443 scheme = "Http" } + initial_delay_seconds = 1 - period_seconds = 1 - failure_threshold = 1 - success_threshold = 1 - timeout_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 } commands = ["cmd.exe", "echo", "hi"] @@ -884,6 +1061,7 @@ resource "azurerm_container_group" "test" { workspace_id = "${azurerm_log_analytics_workspace.test.workspace_id}" workspace_key = "${azurerm_log_analytics_workspace.test.primary_shared_key}" log_type = "ContainerInsights" + metadata = { node-name = "acctestContainerGroup" } @@ -963,7 +1141,7 @@ resource "azurerm_container_group" "test" { gpu { count = 1 - sku = "K80" + sku = "K80" } volume { @@ -973,7 +1151,7 @@ resource "azurerm_container_group" "test" { share_name = "${azurerm_storage_share.test.name}" storage_account_name = "${azurerm_storage_account.test.name}" - storage_account_key = "${azurerm_storage_account.test.primary_access_key}" + storage_account_key = "${azurerm_storage_account.test.primary_access_key}" } environment_variables = { @@ -987,12 +1165,12 @@ resource "azurerm_container_group" "test" { } readiness_probe { - exec = ["cat","/tmp/healthy"] + exec = ["cat", "/tmp/healthy"] initial_delay_seconds = 1 - period_seconds = 1 - failure_threshold = 1 - success_threshold = 1 - timeout_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 } liveness_probe { @@ -1001,21 +1179,23 @@ resource "azurerm_container_group" "test" { port = 443 scheme = "Http" } + initial_delay_seconds = 1 - period_seconds = 1 - failure_threshold = 1 - success_threshold = 1 - timeout_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 } commands = ["/bin/bash", "-c", "ls"] } diagnostics { - log_analytics { + log_analytics { workspace_id = "${azurerm_log_analytics_workspace.test.workspace_id}" workspace_key = "${azurerm_log_analytics_workspace.test.primary_shared_key}" log_type = "ContainerInsights" + metadata = { node-name = "acctestContainerGroup" } @@ -1072,13 +1252,12 @@ func testCheckAzureRMContainerGroupDestroy(s *terraform.State) error { resp, err := conn.Get(ctx, resourceGroup, name) if err != nil { - if resp.StatusCode != http.StatusNotFound { + if !utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Container Group still exists:\n%#v", resp) } return nil } - } return nil diff --git a/azurerm/resource_arm_container_registry.go b/azurerm/resource_arm_container_registry.go index 294676e9c073..cc058e8f3ea5 100644 --- a/azurerm/resource_arm_container_registry.go +++ b/azurerm/resource_arm_container_registry.go @@ -7,13 +7,16 @@ import ( "strings" - "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2017-10-01/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry" "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -45,7 +48,7 @@ func resourceArmContainerRegistry() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(containerregistry.Classic), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(containerregistry.Classic), string(containerregistry.Basic), @@ -113,7 +116,72 @@ func resourceArmContainerRegistry() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "network_rule_set": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ConfigMode: schema.SchemaConfigModeAttr, // make sure we can set this to an empty array for Premium -> Basic + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_action": { + Type: schema.TypeString, + Optional: true, + Default: containerregistry.DefaultActionAllow, + ValidateFunc: validation.StringInSlice([]string{ + string(containerregistry.DefaultActionAllow), + string(containerregistry.DefaultActionDeny), + }, false), + }, + + "ip_rule": { + Type: schema.TypeSet, + Optional: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(containerregistry.Allow), + }, false), + }, + "ip_range": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.CIDR, + }, + }, + }, + }, + + "virtual_network": { + Type: schema.TypeSet, + Optional: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(containerregistry.Allow), + }, false), + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + }, + }, + }, + + "tags": tags.Schema(), }, CustomizeDiff: func(d *schema.ResourceDiff, v interface{}) error { @@ -130,14 +198,14 @@ func resourceArmContainerRegistry() *schema.Resource { } func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).containers.RegistryClient + client := meta.(*ArmClient).containers.RegistriesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Container Registry creation.") resourceGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -150,12 +218,30 @@ func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{} } } + availabilityRequest := containerregistry.RegistryNameCheckRequest{ + Name: utils.String(name), + Type: utils.String("Microsoft.ContainerRegistry/registries"), + } + available, err := client.CheckNameAvailability(ctx, availabilityRequest) + if err != nil { + return fmt.Errorf("Error checking if the name %q was available: %+v", name, err) + } + + if !*available.NameAvailable { + return fmt.Errorf("The name %q used for the Container Registry needs to be globally unique and isn't available: %s", name, *available.Message) + } + location := azure.NormalizeLocation(d.Get("location").(string)) sku := d.Get("sku").(string) adminUserEnabled := d.Get("admin_enabled").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) geoReplicationLocations := d.Get("georeplication_locations").(*schema.Set) + networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{})) + if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) { + return fmt.Errorf("`network_rule_set_set` can only be specified for a Premium Sku. If you are reverting from a Premium to Basic SKU plese set network_rule_set = []") + } + parameters := containerregistry.Registry{ Location: &location, Sku: &containerregistry.Sku{ @@ -164,8 +250,10 @@ func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{} }, RegistryProperties: &containerregistry.RegistryProperties{ AdminUserEnabled: utils.Bool(adminUserEnabled), + NetworkRuleSet: networkRuleSet, }, - Tags: expandTags(tags), + + Tags: tags.Expand(t), } if v, ok := d.GetOk("storage_account_id"); ok { @@ -216,7 +304,7 @@ func resourceArmContainerRegistryCreate(d *schema.ResourceData, meta interface{} } func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).containers.RegistryClient + client := meta.(*ArmClient).containers.RegistriesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Container Registry update.") @@ -225,22 +313,28 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{} sku := d.Get("sku").(string) adminUserEnabled := d.Get("admin_enabled").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) old, new := d.GetChange("georeplication_locations") hasGeoReplicationChanges := d.HasChange("georeplication_locations") oldGeoReplicationLocations := old.(*schema.Set) newGeoReplicationLocations := new.(*schema.Set) + networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{})) + if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) { + return fmt.Errorf("`network_rule_set_set` can only be specified for a Premium Sku. If you are reverting from a Premium to Basic SKU plese set network_rule_set = []") + } + parameters := containerregistry.RegistryUpdateParameters{ RegistryPropertiesUpdateParameters: &containerregistry.RegistryPropertiesUpdateParameters{ AdminUserEnabled: utils.Bool(adminUserEnabled), + NetworkRuleSet: networkRuleSet, }, Sku: &containerregistry.Sku{ Name: containerregistry.SkuName(sku), Tier: containerregistry.SkuTier(sku), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if v, ok := d.GetOk("storage_account_id"); ok { @@ -301,7 +395,7 @@ func resourceArmContainerRegistryUpdate(d *schema.ResourceData, meta interface{} } func applyGeoReplicationLocations(meta interface{}, resourceGroup string, name string, oldGeoReplicationLocations []interface{}, newGeoReplicationLocations []interface{}) error { - replicationClient := meta.(*ArmClient).containers.RegistryReplicationsClient + replicationClient := meta.(*ArmClient).containers.ReplicationsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing to apply geo-replications for AzureRM Container Registry.") @@ -370,11 +464,11 @@ func applyGeoReplicationLocations(meta interface{}, resourceGroup string, name s } func resourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).containers.RegistryClient - replicationClient := meta.(*ArmClient).containers.RegistryReplicationsClient + client := meta.(*ArmClient).containers.RegistriesClient + replicationClient := meta.(*ArmClient).containers.ReplicationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -402,6 +496,11 @@ func resourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{}) d.Set("admin_enabled", resp.AdminUserEnabled) d.Set("login_server", resp.LoginServer) + networkRuleSet := flattenNetworkRuleSet(resp.NetworkRuleSet) + if err := d.Set("network_rule_set", networkRuleSet); err != nil { + return fmt.Errorf("Error setting `network_rule_set`: %+v", err) + } + if sku := resp.Sku; sku != nil { d.Set("sku", string(sku.Tier)) } @@ -426,8 +525,6 @@ func resourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{}) d.Set("admin_password", "") } - flattenAndSetTags(d, resp.Tags) - replications, err := replicationClient.List(ctx, resourceGroup, name) if err != nil { return fmt.Errorf("Error making Read request on Azure Container Registry %s for replications: %s", name, err) @@ -451,14 +548,14 @@ func resourceArmContainerRegistryRead(d *schema.ResourceData, meta interface{}) d.Set("georeplication_locations", georeplication_locations) } - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmContainerRegistryDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).containers.RegistryClient + client := meta.(*ArmClient).containers.RegistriesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -500,3 +597,82 @@ func validateAzureRMContainerRegistryName(v interface{}, k string) (warnings []s return warnings, errors } + +func expandNetworkRuleSet(profiles []interface{}) *containerregistry.NetworkRuleSet { + if len(profiles) == 0 { + return nil + } + + profile := profiles[0].(map[string]interface{}) + + ipRuleConfigs := profile["ip_rule"].(*schema.Set).List() + ipRules := make([]containerregistry.IPRule, 0) + for _, ipRuleInterface := range ipRuleConfigs { + config := ipRuleInterface.(map[string]interface{}) + newIpRule := containerregistry.IPRule{ + Action: containerregistry.Action(config["action"].(string)), + IPAddressOrRange: utils.String(config["ip_range"].(string)), + } + ipRules = append(ipRules, newIpRule) + } + + networkRuleConfigs := profile["virtual_network"].(*schema.Set).List() + virtualNetworkRules := make([]containerregistry.VirtualNetworkRule, 0) + for _, networkRuleInterface := range networkRuleConfigs { + config := networkRuleInterface.(map[string]interface{}) + newVirtualNetworkRule := containerregistry.VirtualNetworkRule{ + Action: containerregistry.Action(config["action"].(string)), + VirtualNetworkResourceID: utils.String(config["subnet_id"].(string)), + } + virtualNetworkRules = append(virtualNetworkRules, newVirtualNetworkRule) + } + + networkRuleSet := containerregistry.NetworkRuleSet{ + DefaultAction: containerregistry.DefaultAction(profile["default_action"].(string)), + IPRules: &ipRules, + VirtualNetworkRules: &virtualNetworkRules, + } + return &networkRuleSet +} + +func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []interface{} { + if networkRuleSet == nil { + return []interface{}{} + } + + values := make(map[string]interface{}) + + values["default_action"] = string(networkRuleSet.DefaultAction) + + ipRules := make([]interface{}, 0) + for _, ipRule := range *networkRuleSet.IPRules { + value := make(map[string]interface{}) + value["action"] = string(ipRule.Action) + + //When a /32 CIDR is passed as an ip rule, Azure will drop the /32 leading to the resource wanting to be re-created next run + if !strings.Contains(*ipRule.IPAddressOrRange, "/") { + *ipRule.IPAddressOrRange += "/32" + } + + value["ip_range"] = ipRule.IPAddressOrRange + ipRules = append(ipRules, value) + } + + values["ip_rule"] = ipRules + + virtualNetworkRules := make([]interface{}, 0) + + if networkRuleSet.VirtualNetworkRules != nil { + for _, virtualNetworkRule := range *networkRuleSet.VirtualNetworkRules { + value := make(map[string]interface{}) + value["action"] = string(virtualNetworkRule.Action) + + value["subnet_id"] = virtualNetworkRule.VirtualNetworkResourceID + virtualNetworkRules = append(virtualNetworkRules, value) + } + } + + values["virtual_network"] = virtualNetworkRules + + return []interface{}{values} +} diff --git a/azurerm/resource_arm_container_registry_migrate.go b/azurerm/resource_arm_container_registry_migrate.go index 392588a247eb..87f58ba04626 100644 --- a/azurerm/resource_arm_container_registry_migrate.go +++ b/azurerm/resource_arm_container_registry_migrate.go @@ -92,7 +92,7 @@ func updateV1ToV2StorageAccountName(is *terraform.InstanceState, meta interface{ func findAzureStorageAccountIdFromName(name string, meta interface{}) (string, error) { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + client := meta.(*ArmClient).Storage.AccountsClient accounts, err := client.List(ctx) if err != nil { return "", err diff --git a/azurerm/resource_arm_container_registry_migrate_test.go b/azurerm/resource_arm_container_registry_migrate_test.go index 68f8cfe9f7ea..a354eac1cfca 100644 --- a/azurerm/resource_arm_container_registry_migrate_test.go +++ b/azurerm/resource_arm_container_registry_migrate_test.go @@ -21,7 +21,7 @@ func TestAccAzureRMContainerRegistryMigrateState(t *testing.T) { return } - client, err := getArmClient(config, false, "") + client, err := getArmClient(config, false, "0.0.0", "", true) if err != nil { t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) return @@ -105,14 +105,14 @@ func createResourceGroup(ctx context.Context, client *ArmClient, resourceGroupNa Location: &location, } - if _, err := client.resourceGroupsClient.CreateOrUpdate(ctx, resourceGroupName, group); err != nil { + if _, err := client.resource.GroupsClient.CreateOrUpdate(ctx, resourceGroupName, group); err != nil { return fmt.Errorf("Error creating Resource Group %q: %+v", resourceGroupName, err) } return nil } func createStorageAccount(client *ArmClient, resourceGroupName, storageAccountName, location string) (*storage.Account, error) { - storageClient := client.storageServiceClient + storageClient := client.Storage.AccountsClient createParams := storage.AccountCreateParameters{ Location: &location, Kind: storage.Storage, @@ -141,10 +141,10 @@ func createStorageAccount(client *ArmClient, resourceGroupName, storageAccountNa func destroyStorageAccountAndResourceGroup(client *ArmClient, resourceGroupName, storageAccountName string) { ctx := client.StopContext - if _, err := client.storageServiceClient.Delete(ctx, resourceGroupName, storageAccountName); err != nil { + if _, err := client.Storage.AccountsClient.Delete(ctx, resourceGroupName, storageAccountName); err != nil { log.Printf("[DEBUG] Error deleting Storage Account %q (Resource Group %q): %v", storageAccountName, resourceGroupName, err) } - if _, err := client.resourceGroupsClient.Delete(ctx, resourceGroupName); err != nil { + if _, err := client.resource.GroupsClient.Delete(ctx, resourceGroupName); err != nil { log.Printf("[DEBUG] Error deleting Resource Group %q): %v", resourceGroupName, err) } } diff --git a/azurerm/resource_arm_container_registry_test.go b/azurerm/resource_arm_container_registry_test.go index 056e68daec64..8ee190b4623d 100644 --- a/azurerm/resource_arm_container_registry_test.go +++ b/azurerm/resource_arm_container_registry_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" - "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2017-10-01/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -69,10 +70,10 @@ func TestAccAzureRMContainerRegistryName_validation(t *testing.T) { } } -func TestAccAzureRMContainerRegistry_basicBasic(t *testing.T) { - resourceName := "azurerm_container_registry.test" +func TestAccAzureRMContainerRegistry_basic_basic(t *testing.T) { + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - location := testLocation() + l := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -80,13 +81,13 @@ func TestAccAzureRMContainerRegistry_basicBasic(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMContainerRegistry_basicManaged(ri, location, "Basic"), + Config: testAccAzureRMContainerRegistry_basic_basic(ri, l), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -95,14 +96,14 @@ func TestAccAzureRMContainerRegistry_basicBasic(t *testing.T) { } func TestAccAzureRMContainerRegistry_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } - resourceName := "azurerm_container_registry.test" + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - location := testLocation() + l := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -110,23 +111,22 @@ func TestAccAzureRMContainerRegistry_requiresImport(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMContainerRegistry_basicManaged(ri, location, "Basic"), + Config: testAccAzureRMContainerRegistry_basicManaged(ri, l, "Basic"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - Config: testAccAzureRMContainerRegistry_requiresImport(ri, location, "Basic"), + Config: testAccAzureRMContainerRegistry_requiresImport(ri, l, "Basic"), ExpectError: testRequiresImportError("azurerm_container_registry"), }, }, }) } -func TestAccAzureRMContainerRegistry_basicStandard(t *testing.T) { - resourceName := "azurerm_container_registry.test" +func TestAccAzureRMContainerRegistry_basic_standard(t *testing.T) { + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - config := testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Standard") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -134,13 +134,13 @@ func TestAccAzureRMContainerRegistry_basicStandard(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Standard"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -148,10 +148,9 @@ func TestAccAzureRMContainerRegistry_basicStandard(t *testing.T) { }) } -func TestAccAzureRMContainerRegistry_basicPremium(t *testing.T) { - resourceName := "azurerm_container_registry.test" +func TestAccAzureRMContainerRegistry_basic_premium(t *testing.T) { + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - config := testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Premium") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -159,13 +158,13 @@ func TestAccAzureRMContainerRegistry_basicPremium(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Premium"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -173,11 +172,9 @@ func TestAccAzureRMContainerRegistry_basicPremium(t *testing.T) { }) } -func TestAccAzureRMContainerRegistry_basicBasicUpgradePremium(t *testing.T) { - resourceName := "azurerm_container_registry.test" +func TestAccAzureRMContainerRegistry_basic_basic2Premium2basic(t *testing.T) { + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - config := testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Basic") - configUpdated := testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Premium") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -185,17 +182,24 @@ func TestAccAzureRMContainerRegistry_basicBasicUpgradePremium(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMContainerRegistry_basic_basic(ri, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "sku", "Basic"), + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "sku", "Basic"), ), }, { - Config: configUpdated, + Config: testAccAzureRMContainerRegistry_basicManaged(ri, testLocation(), "Premium"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "sku", "Premium"), + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "sku", "Premium"), + ), + }, + { + Config: testAccAzureRMContainerRegistry_basic_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "sku", "Basic"), ), }, }, @@ -203,9 +207,8 @@ func TestAccAzureRMContainerRegistry_basicBasicUpgradePremium(t *testing.T) { } func TestAccAzureRMContainerRegistry_complete(t *testing.T) { - resourceName := "azurerm_container_registry.test" + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - config := testAccAzureRMContainerRegistry_complete(ri, testLocation()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -213,13 +216,13 @@ func TestAccAzureRMContainerRegistry_complete(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMContainerRegistry_complete(ri, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -228,11 +231,9 @@ func TestAccAzureRMContainerRegistry_complete(t *testing.T) { } func TestAccAzureRMContainerRegistry_update(t *testing.T) { - resourceName := "azurerm_container_registry.test" + rn := "azurerm_container_registry.test" ri := tf.AccRandTimeInt() - location := testLocation() - config := testAccAzureRMContainerRegistry_complete(ri, location) - updatedConfig := testAccAzureRMContainerRegistry_completeUpdated(ri, location) + l := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -240,15 +241,15 @@ func TestAccAzureRMContainerRegistry_update(t *testing.T) { CheckDestroy: testCheckAzureRMContainerRegistryDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMContainerRegistry_complete(ri, l), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, { - Config: updatedConfig, + Config: testAccAzureRMContainerRegistry_completeUpdated(ri, l), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMContainerRegistryExists(resourceName), + testCheckAzureRMContainerRegistryExists(rn), ), }, }, @@ -256,16 +257,11 @@ func TestAccAzureRMContainerRegistry_update(t *testing.T) { } func TestAccAzureRMContainerRegistry_geoReplication(t *testing.T) { - dataSourceName := "azurerm_container_registry.test" + dsn := "azurerm_container_registry.test" + ri := tf.AccRandTimeInt() + skuPremium := "Premium" skuBasic := "Basic" - ri := tf.AccRandTimeInt() - containerRegistryName := fmt.Sprintf("testacccr%d", ri) - resourceGroupName := fmt.Sprintf("testAccRg-%d", ri) - config := testAccAzureRMContainerRegistry_geoReplication(ri, testLocation(), skuPremium, `eastus", "westus`) - updatedConfig := testAccAzureRMContainerRegistry_geoReplication(ri, testLocation(), skuPremium, `centralus", "eastus`) - updatedConfigWithNoLocation := testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation(ri, testLocation(), skuPremium) - updatedConfigBasicSku := testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation(ri, testLocation(), skuBasic) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -274,68 +270,152 @@ func TestAccAzureRMContainerRegistry_geoReplication(t *testing.T) { Steps: []resource.TestStep{ // first config creates an ACR with locations { - Config: config, + // TODO: fix this to use dynamic locations + Config: testAccAzureRMContainerRegistry_geoReplication(ri, testLocation(), skuPremium, `eastus", "westus`), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", containerRegistryName), - resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resourceGroupName), - resource.TestCheckResourceAttr(dataSourceName, "sku", skuPremium), - resource.TestCheckResourceAttr(dataSourceName, "georeplication_locations.#", "2"), - testCheckAzureRMContainerRegistryExists(dataSourceName), - testCheckAzureRMContainerRegistryGeoreplications(dataSourceName, skuPremium, []string{`"eastus"`, `"westus"`}), + resource.TestCheckResourceAttr(dsn, "sku", skuPremium), + resource.TestCheckResourceAttr(dsn, "georeplication_locations.#", "2"), + testCheckAzureRMContainerRegistryExists(dsn), + testCheckAzureRMContainerRegistryGeoreplications(dsn, skuPremium, []string{`"eastus"`, `"westus"`}), ), }, // second config udpates the ACR with updated locations { - Config: updatedConfig, + Config: testAccAzureRMContainerRegistry_geoReplication(ri, testLocation(), skuPremium, `centralus", "eastus`), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", containerRegistryName), - resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resourceGroupName), - resource.TestCheckResourceAttr(dataSourceName, "sku", skuPremium), - resource.TestCheckResourceAttr(dataSourceName, "georeplication_locations.#", "2"), - testCheckAzureRMContainerRegistryExists(dataSourceName), - testCheckAzureRMContainerRegistryGeoreplications(dataSourceName, skuPremium, []string{`"eastus"`, `"centralus"`}), + resource.TestCheckResourceAttr(dsn, "sku", skuPremium), + resource.TestCheckResourceAttr(dsn, "georeplication_locations.#", "2"), + testCheckAzureRMContainerRegistryExists(dsn), + testCheckAzureRMContainerRegistryGeoreplications(dsn, skuPremium, []string{`"eastus"`, `"centralus"`}), ), }, // third config udpates the ACR with no location { - Config: updatedConfigWithNoLocation, + Config: testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation(ri, testLocation(), skuPremium), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", containerRegistryName), - resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resourceGroupName), - resource.TestCheckResourceAttr(dataSourceName, "sku", skuPremium), - testCheckAzureRMContainerRegistryExists(dataSourceName), - testCheckAzureRMContainerRegistryGeoreplications(dataSourceName, skuPremium, nil), + resource.TestCheckResourceAttr(dsn, "sku", skuPremium), + testCheckAzureRMContainerRegistryExists(dsn), + testCheckAzureRMContainerRegistryGeoreplications(dsn, skuPremium, nil), ), }, // fourth config updates an ACR with replicas { - Config: config, + Config: testAccAzureRMContainerRegistry_geoReplication(ri, testLocation(), skuPremium, `eastus", "westus`), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", containerRegistryName), - resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resourceGroupName), - resource.TestCheckResourceAttr(dataSourceName, "sku", skuPremium), - resource.TestCheckResourceAttr(dataSourceName, "georeplication_locations.#", "2"), - testCheckAzureRMContainerRegistryExists(dataSourceName), - testCheckAzureRMContainerRegistryGeoreplications(dataSourceName, skuPremium, []string{`"eastus"`, `"westus"`}), + resource.TestCheckResourceAttr(dsn, "sku", skuPremium), + resource.TestCheckResourceAttr(dsn, "georeplication_locations.#", "2"), + testCheckAzureRMContainerRegistryExists(dsn), + testCheckAzureRMContainerRegistryGeoreplications(dsn, skuPremium, []string{`"eastus"`, `"westus"`}), ), }, // fifth config updates the SKU to basic and no replicas (should remove the existing replicas if any) { - Config: updatedConfigBasicSku, + Config: testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation_basic(ri, testLocation()), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(dataSourceName, "name", containerRegistryName), - resource.TestCheckResourceAttr(dataSourceName, "resource_group_name", resourceGroupName), - resource.TestCheckResourceAttr(dataSourceName, "sku", skuBasic), - testCheckAzureRMContainerRegistryExists(dataSourceName), - testCheckAzureRMContainerRegistryGeoreplications(dataSourceName, skuBasic, nil), + resource.TestCheckResourceAttr(dsn, "sku", skuBasic), + testCheckAzureRMContainerRegistryExists(dsn), + testCheckAzureRMContainerRegistryGeoreplications(dsn, skuBasic, nil), ), }, }, }) } +func TestAccAzureRMContainerRegistry_networkAccessProfileIp(t *testing.T) { + rn := "azurerm_container_registry.test" + ri := tf.AccRandTimeInt() + l := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMContainerRegistry_networkAccessProfile_ip(ri, l, "Premium"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.default_action", "Allow"), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.ip_rule.#", "1"), + ), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMContainerRegistry_networkAccessProfileIp_update(t *testing.T) { + rn := "azurerm_container_registry.test" + ri := tf.AccRandTimeInt() + l := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMContainerRegistry_basicManaged(ri, l, "Basic"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + ), + }, + { + Config: testAccAzureRMContainerRegistry_networkAccessProfile_ip(ri, l, "Premium"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.default_action", "Allow"), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.ip_rule.#", "1"), + ), + }, + { + Config: testAccAzureRMContainerRegistry_basic_basic(ri, l), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + ), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMContainerRegistry_networkAccessProfileVnet(t *testing.T) { + rn := "azurerm_container_registry.test" + ri := tf.AccRandTimeInt() + l := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMContainerRegistry_networkAccessProfile_vnet(ri, l), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryExists(rn), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.default_action", "Deny"), + resource.TestCheckResourceAttr(rn, "network_rule_set.0.virtual_network.#", "1"), + ), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMContainerRegistryDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).containers.RegistryClient + conn := testAccProvider.Meta().(*ArmClient).containers.RegistriesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -373,7 +453,7 @@ func testCheckAzureRMContainerRegistryExists(resourceName string) resource.TestC return fmt.Errorf("Bad: no resource group found in state for Container Registry: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).containers.RegistryClient + conn := testAccProvider.Meta().(*ArmClient).containers.RegistriesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) @@ -403,7 +483,7 @@ func testCheckAzureRMContainerRegistryGeoreplications(resourceName string, sku s return fmt.Errorf("Bad: no resource group found in state for Container Registry: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).containers.RegistryReplicationsClient + conn := testAccProvider.Meta().(*ArmClient).containers.ReplicationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.List(ctx, resourceGroup, name) @@ -429,10 +509,30 @@ func testCheckAzureRMContainerRegistryGeoreplications(resourceName string, sku s } } +func testAccAzureRMContainerRegistry_basic_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku = "Basic" + + # make sure network_rule_set is empty for basic SKU + # premiuim SKU will automaticcally populate network_rule_set.default_action to allow + network_rule_set = [] +} +`, rInt, location, rInt) +} + func testAccAzureRMContainerRegistry_basicManaged(rInt int, location string, sku string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRg-%d" + name = "acctestrg-%d" location = "%s" } @@ -455,6 +555,7 @@ resource "azurerm_container_registry" "import" { resource_group_name = "${azurerm_container_registry.test.resource_group_name}" location = "${azurerm_container_registry.test.location}" sku = "${azurerm_container_registry.test.sku}" + } `, template) } @@ -462,7 +563,7 @@ resource "azurerm_container_registry" "import" { func testAccAzureRMContainerRegistry_complete(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRg-%d" + name = "acctestrg-%d" location = "%s" } @@ -483,7 +584,7 @@ resource "azurerm_container_registry" "test" { func testAccAzureRMContainerRegistry_completeUpdated(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRg-%d" + name = "acctestrg-%d" location = "%s" } @@ -504,7 +605,7 @@ resource "azurerm_container_registry" "test" { func testAccAzureRMContainerRegistry_geoReplication(rInt int, location string, sku string, georeplicationLocations string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "testAccRg-%d" + name = "acctestrg-%d" location = "%s" } @@ -521,7 +622,7 @@ resource "azurerm_container_registry" "test" { func testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation(rInt int, location string, sku string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "testAccRg-%d" + name = "acctestrg-%d" location = "%s" } @@ -533,3 +634,91 @@ resource "azurerm_container_registry" "test" { } `, rInt, location, rInt, sku) } + +func testAccAzureRMContainerRegistry_geoReplicationUpdateWithNoLocation_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_container_registry" "test" { + name = "testacccr%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku = "Basic" + + # make sure network_rule_set is empty for basic SKU + # premiuim SKU will automaticcally populate network_rule_set.default_action to allow + network_rule_set = [] +} +`, rInt, location, rInt) +} + +func testAccAzureRMContainerRegistry_networkAccessProfile_ip(rInt int, location string, sku string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%[1]d" + location = "%[2]s" +} + +resource "azurerm_container_registry" "test" { + name = "testAccCr%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku = "%[3]s" + admin_enabled = false + + network_rule_set { + default_action = "Allow" + + ip_rule { + action = "Allow" + ip_range = "8.8.8.8/32" + } + } +} +`, rInt, location, sku) +} + +func testAccAzureRMContainerRegistry_networkAccessProfile_vnet(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 = "virtualNetwork1" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "testsubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" + + service_endpoints = ["Microsoft.ContainerRegistry"] +} + +resource "azurerm_container_registry" "test" { + name = "testAccCr%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku = "Premium" + admin_enabled = false + + network_rule_set { + default_action = "Deny" + + virtual_network { + action = "Allow" + subnet_id = "${azurerm_subnet.test.id}" + } + } +} +`, rInt, location) +} diff --git a/azurerm/resource_arm_container_registry_webhook.go b/azurerm/resource_arm_container_registry_webhook.go new file mode 100644 index 000000000000..c3e53d92dd71 --- /dev/null +++ b/azurerm/resource_arm_container_registry_webhook.go @@ -0,0 +1,353 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmContainerRegistryWebhook() *schema.Resource { + return &schema.Resource{ + Create: resourceArmContainerRegistryWebhookCreate, + Read: resourceArmContainerRegistryWebhookRead, + Update: resourceArmContainerRegistryWebhookUpdate, + Delete: resourceArmContainerRegistryWebhookDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMContainerRegistryWebhookName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "registry_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMContainerRegistryName, + }, + + "service_uri": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAzureRMContainerRegistryWebhookServiceUri, + }, + + "custom_headers": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "status": { + Type: schema.TypeString, + Optional: true, + Default: containerregistry.WebhookStatusEnabled, + ValidateFunc: validation.StringInSlice([]string{ + string(containerregistry.WebhookStatusDisabled), + string(containerregistry.WebhookStatusEnabled), + }, false), + }, + + "scope": { + Type: schema.TypeString, + Optional: true, + Default: "", + }, + + "actions": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(containerregistry.ChartDelete), + string(containerregistry.ChartPush), + string(containerregistry.Delete), + string(containerregistry.Push), + string(containerregistry.Quarantine), + }, false), + }, + }, + + "location": azure.SchemaLocation(), + + "tags": tags.Schema(), + }, + } +} + +func resourceArmContainerRegistryWebhookCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).containers.WebhooksClient + ctx := meta.(*ArmClient).StopContext + log.Printf("[INFO] preparing arguments for AzureRM Container Registry Webhook creation.") + + resourceGroup := d.Get("resource_group_name").(string) + registryName := d.Get("registry_name").(string) + name := d.Get("name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, registryName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Container Registry Webhook %q (Resource Group %q, Registry %q): %s", name, resourceGroup, registryName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_container_registry_webhook", *existing.ID) + } + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + t := d.Get("tags").(map[string]interface{}) + + webhook := containerregistry.WebhookCreateParameters{ + Location: &location, + WebhookPropertiesCreateParameters: expandWebhookPropertiesCreateParameters(d), + Tags: tags.Expand(t), + } + + future, err := client.Create(ctx, resourceGroup, registryName, name, webhook) + if err != nil { + return fmt.Errorf("Error creating Container Registry Webhook %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for creation of Container Registry %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + read, err := client.Get(ctx, resourceGroup, registryName, name) + if err != nil { + return fmt.Errorf("Error retrieving Container Registry %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + if read.ID == nil { + return fmt.Errorf("Cannot read Container Registry %q (resource group %q, Registry %q) ID", name, resourceGroup, registryName) + } + + d.SetId(*read.ID) + + return resourceArmContainerRegistryWebhookRead(d, meta) +} + +func resourceArmContainerRegistryWebhookUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).containers.WebhooksClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM Container Registry Webhook update.") + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + registryName := id.Path["registries"] + name := id.Path["webhooks"] + + t := d.Get("tags").(map[string]interface{}) + + webhook := containerregistry.WebhookUpdateParameters{ + WebhookPropertiesUpdateParameters: expandWebhookPropertiesUpdateParameters(d), + Tags: tags.Expand(t), + } + + future, err := client.Update(ctx, resourceGroup, registryName, name, webhook) + if err != nil { + return fmt.Errorf("Error updating Container Registry Webhook %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Container Registry Webhook %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + return resourceArmContainerRegistryWebhookRead(d, meta) +} + +func resourceArmContainerRegistryWebhookRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).containers.WebhooksClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + registryName := id.Path["registries"] + name := id.Path["webhooks"] + + resp, err := client.Get(ctx, resourceGroup, registryName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Container Registry Webhook %q was not found in Resource Group %q for Registry %q", name, resourceGroup, registryName) + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Azure Container Registry Webhook %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + callbackConfig, err := client.GetCallbackConfig(ctx, resourceGroup, registryName, name) + if err != nil { + return fmt.Errorf("Error making Read request on Azure Container Registry Webhook Callback Config %q (Resource Group %q, Registry %q): %+v", name, resourceGroup, registryName, err) + } + + d.Set("resource_group_name", resourceGroup) + d.Set("registry_name", registryName) + d.Set("name", name) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + d.Set("service_uri", callbackConfig.ServiceURI) + + if callbackConfig.CustomHeaders != nil { + customHeaders := make(map[string]string) + for k, v := range callbackConfig.CustomHeaders { + customHeaders[k] = *v + } + d.Set("custom_headers", customHeaders) + } + + if webhookProps := resp.WebhookProperties; webhookProps != nil { + if webhookProps.Status != "" { + d.Set("status", string(webhookProps.Status)) + } + + if webhookProps.Scope != nil { + d.Set("scope", webhookProps.Scope) + } + + webhookActions := make([]string, len(*webhookProps.Actions)) + for i, action := range *webhookProps.Actions { + webhookActions[i] = string(action) + } + d.Set("actions", webhookActions) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmContainerRegistryWebhookDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).containers.WebhooksClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + registryName := id.Path["registries"] + name := id.Path["webhooks"] + + future, err := client.Delete(ctx, resourceGroup, registryName, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error issuing Azure ARM delete request of Container Registry Webhook '%s': %+v", name, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error issuing Azure ARM delete request of Container Registry Webhook '%s': %+v", name, err) + } + + return nil +} + +func validateAzureRMContainerRegistryWebhookName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[a-zA-Z0-9]{5,50}$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "alpha numeric characters only are allowed and between 5 and 50 characters in %q: %q", k, value)) + } + + return warnings, errors +} + +func validateAzureRMContainerRegistryWebhookServiceUri(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^https?://[^\s]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must start with http:// or https:// and must not contain whitespaces: %q", k, value)) + } + + return warnings, errors +} + +func expandWebhookPropertiesCreateParameters(d *schema.ResourceData) *containerregistry.WebhookPropertiesCreateParameters { + serviceUri := d.Get("service_uri").(string) + scope := d.Get("scope").(string) + + customHeaders := make(map[string]*string) + for k, v := range d.Get("custom_headers").(map[string]interface{}) { + customHeaders[k] = utils.String(v.(string)) + } + + actions := expandWebhookActions(d) + + webhookProperties := containerregistry.WebhookPropertiesCreateParameters{ + ServiceURI: &serviceUri, + CustomHeaders: customHeaders, + Actions: actions, + Scope: &scope, + } + + webhookProperties.Status = containerregistry.WebhookStatus(d.Get("status").(string)) + + return &webhookProperties +} + +func expandWebhookPropertiesUpdateParameters(d *schema.ResourceData) *containerregistry.WebhookPropertiesUpdateParameters { + serviceUri := d.Get("service_uri").(string) + scope := d.Get("scope").(string) + + customHeaders := make(map[string]*string) + for k, v := range d.Get("custom_headers").(map[string]interface{}) { + customHeaders[k] = utils.String(v.(string)) + } + + webhookProperties := containerregistry.WebhookPropertiesUpdateParameters{ + ServiceURI: &serviceUri, + CustomHeaders: customHeaders, + Actions: expandWebhookActions(d), + Scope: &scope, + Status: containerregistry.WebhookStatus(d.Get("status").(string)), + } + + return &webhookProperties +} + +func expandWebhookActions(d *schema.ResourceData) *[]containerregistry.WebhookAction { + actions := make([]containerregistry.WebhookAction, 0) + for _, action := range d.Get("actions").(*schema.Set).List() { + actions = append(actions, containerregistry.WebhookAction(action.(string))) + } + + return &actions +} diff --git a/azurerm/resource_arm_container_registry_webhook_test.go b/azurerm/resource_arm_container_registry_webhook_test.go new file mode 100644 index 000000000000..65454369f0d9 --- /dev/null +++ b/azurerm/resource_arm_container_registry_webhook_test.go @@ -0,0 +1,685 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMContainerRegistryWebhook_basic(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMContainerRegistryWebhook_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_withTags(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_withTags(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_withTagsUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test1"), + resource.TestCheckResourceAttr(resourceName, "tags.ENV", "prod"), + ), + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_actions(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_actions(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_actionsUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_status(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_status(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_statusUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "status", "enabled"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "status", "disabled"), + ), + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_serviceUri(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_serviceUri(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_serviceUriUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "service_uri", "https://mywebhookreceiver.example/mytag"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "service_uri", "https://my.webhookreceiver.example/mytag/2"), + ), + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_scope(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_scope(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_scopeUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "scope", "mytag:*"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "scope", "mytag:4"), + ), + }, + }, + }) +} + +func TestAccAzureRMContainerRegistryWebhook_customHeaders(t *testing.T) { + resourceName := "azurerm_container_registry_webhook.test" + ri := tf.AccRandTimeInt() + preConfig := testAccAzureRMContainerRegistryWebhook_customHeaders(ri, testLocation()) + postConfig := testAccAzureRMContainerRegistryWebhook_customHeadersUpdate(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerRegistryWebhookDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "custom_headers.%", "1"), + resource.TestCheckResourceAttr(resourceName, "custom_headers.Content-Type", "application/json"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerRegistryWebhookExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "custom_headers.%", "2"), + resource.TestCheckResourceAttr(resourceName, "custom_headers.Content-Type", "application/xml"), + resource.TestCheckResourceAttr(resourceName, "custom_headers.Accept-Charset", "utf-8"), + ), + }, + }, + }) +} + +func testAccAzureRMContainerRegistryWebhook_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push" + ] + + tags = { + label = "test" + } +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push" + ] + + tags = { + label = "test1" + ENV = "prod" + } +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_actions(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_actionsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push", + "delete" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_status(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + status = "enabled" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_statusUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + status = "disabled" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_serviceUri(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_serviceUriUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://my.webhookreceiver.example/mytag/2" + + status = "disabled" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_scope(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + scope = "mytag:*" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_scopeUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + scope = "mytag:4" + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_customHeaders(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + custom_headers = { + "Content-Type" = "application/json" + } + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testAccAzureRMContainerRegistryWebhook_customHeadersUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_container_registry" "acr" { + name = "acrwebhooktest%d" + resource_group_name = azurerm_resource_group.rg.name + location = "%s" + sku = "Standard" +} + +resource "azurerm_container_registry_webhook" "test" { + name = "testwebhook%d" + resource_group_name = azurerm_resource_group.rg.name + registry_name = azurerm_container_registry.acr.name + location = "%s" + + service_uri = "https://mywebhookreceiver.example/mytag" + + custom_headers = { + "Content-Type" = "application/xml" + "Accept-Charset" = "utf-8" + } + + actions = [ + "push" + ] +} +`, rInt, location, rInt, location, rInt, location) +} + +func testCheckAzureRMContainerRegistryWebhookDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).containers.WebhooksClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_container_registry_webhook" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + registryName := rs.Primary.Attributes["registry_name"] + name := rs.Primary.Attributes["name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, registryName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return nil + } + + return nil +} + +func testCheckAzureRMContainerRegistryWebhookExists(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) + } + + webhookName := 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 Container Registry Webhook: %s", webhookName) + } + + registryName, hasRegistryName := rs.Primary.Attributes["registry_name"] + if !hasRegistryName { + return fmt.Errorf("Bad: no registry name found in state for Container Registry Webhook: %s", webhookName) + } + + client := testAccProvider.Meta().(*ArmClient).containers.WebhooksClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, registryName, webhookName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Container Registry Webhook %q (resource group: %q) does not exist", webhookName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on WebhooksClient: %+v", err) + } + + return nil + } +} diff --git a/azurerm/resource_arm_container_service.go b/azurerm/resource_arm_container_service.go index f7a407e3d035..7a708769ce08 100644 --- a/azurerm/resource_arm_container_service.go +++ b/azurerm/resource_arm_container_service.go @@ -8,12 +8,15 @@ import ( "bytes" - "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-02-01/containerservice" + "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-06-01/containerservice" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -51,6 +54,7 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az ValidateFunc: validateArmContainerServiceOrchestrationPlatform, }, + //lintignore:S018 "master_profile": { Type: schema.TypeSet, Required: true, @@ -78,6 +82,7 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az Set: resourceAzureRMContainerServiceMasterProfileHash, }, + //lintignore:S018 "linux_profile": { Type: schema.TypeSet, Required: true, @@ -106,6 +111,7 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az Set: resourceAzureRMContainerServiceLinuxProfilesHash, }, + //lintignore:S018 "agent_pool_profile": { Type: schema.TypeSet, Required: true, @@ -139,13 +145,14 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az "vm_size": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, }, Set: resourceAzureRMContainerServiceAgentPoolProfilesHash, }, + //lintignore:S018 "service_principal": { Type: schema.TypeSet, Optional: true, @@ -167,6 +174,7 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az Set: resourceAzureRMContainerServiceServicePrincipalProfileHash, }, + //lintignore:S018 "diagnostics_profile": { Type: schema.TypeSet, Required: true, @@ -187,7 +195,7 @@ More information can be found here: https://azure.microsoft.com/en-us/updates/az Set: resourceAzureRMContainerServiceDiagnosticProfilesHash, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -202,7 +210,7 @@ func resourceArmContainerServiceCreateUpdate(d *schema.ResourceData, meta interf resGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := containerServiceClient.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -224,7 +232,7 @@ func resourceArmContainerServiceCreateUpdate(d *schema.ResourceData, meta interf agentProfiles := expandAzureRmContainerServiceAgentProfiles(d) diagnosticsProfile := expandAzureRmContainerServiceDiagnostics(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := containerservice.ContainerService{ Name: &name, @@ -238,7 +246,7 @@ func resourceArmContainerServiceCreateUpdate(d *schema.ResourceData, meta interf AgentPoolProfiles: &agentProfiles, DiagnosticsProfile: &diagnosticsProfile, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } servicePrincipalProfile := expandAzureRmContainerServiceServicePrincipal(d) @@ -279,7 +287,7 @@ func resourceArmContainerServiceCreateUpdate(d *schema.ResourceData, meta interf func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) error { containerServiceClient := meta.(*ArmClient).containers.ServicesClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -324,16 +332,14 @@ func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) e d.Set("diagnostics_profile", diagnosticProfile) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmContainerServiceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient) containerServiceClient := client.containers.ServicesClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -408,7 +414,6 @@ func flattenAzureRmContainerServiceAgentPoolProfiles(profiles *[]containerservic } func flattenAzureRmContainerServiceServicePrincipalProfile(profile *containerservice.ServicePrincipalProfile) *schema.Set { - if profile == nil { return nil } @@ -503,7 +508,6 @@ func expandAzureRmContainerServiceMasterProfile(d *schema.ResourceData) containe } func expandAzureRmContainerServiceServicePrincipal(d *schema.ResourceData) *containerservice.ServicePrincipalProfile { - value, exists := d.GetOk("service_principal") if !exists { return nil diff --git a/azurerm/resource_arm_container_service_test.go b/azurerm/resource_arm_container_service_test.go index 9dd987178ff9..814cb8011e23 100644 --- a/azurerm/resource_arm_container_service_test.go +++ b/azurerm/resource_arm_container_service_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMContainerService_orchestrationPlatformValidation(t *testing.T) { @@ -96,7 +97,7 @@ func TestAccAzureRMContainerService_dcosBasic(t *testing.T) { } func TestAccAzureRMContainerService_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_cosmosdb_account.go b/azurerm/resource_arm_cosmosdb_account.go index cd82d2a204b4..092261304a07 100644 --- a/azurerm/resource_arm_cosmosdb_account.go +++ b/azurerm/resource_arm_cosmosdb_account.go @@ -10,7 +10,10 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" @@ -47,13 +50,13 @@ func resourceArmCosmosDbAccount() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), - "tags": tagsSchema(), + "tags": tags.Schema(), //resource fields "offer_type": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(documentdb.Standard), }, true), @@ -64,7 +67,7 @@ func resourceArmCosmosDbAccount() *schema.Resource { Optional: true, ForceNew: true, Default: string(documentdb.GlobalDocumentDB), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(documentdb.GlobalDocumentDB), string(documentdb.MongoDB), @@ -95,7 +98,7 @@ func resourceArmCosmosDbAccount() *schema.Resource { "consistency_level": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(documentdb.BoundedStaleness), string(documentdb.ConsistentPrefix), @@ -109,14 +112,14 @@ func resourceArmCosmosDbAccount() *schema.Resource { Type: schema.TypeInt, Optional: true, Default: 5, - ValidateFunc: validation.IntBetween(5, 86400), + ValidateFunc: validation.IntBetween(5, 86400), // single region values }, "max_staleness_prefix": { Type: schema.TypeInt, Optional: true, Default: 100, - ValidateFunc: validation.IntBetween(10, 2147483647), + ValidateFunc: validation.IntBetween(10, 1000000), // single region values }, }, }, @@ -196,7 +199,7 @@ func resourceArmCosmosDbAccount() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ "EnableTable", "EnableGremlin", @@ -297,14 +300,14 @@ func resourceArmCosmosDbAccount() *schema.Resource { } func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Cosmos DB Account creation.") name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -318,7 +321,7 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) kind := d.Get("kind").(string) offerType := d.Get("offer_type").(string) ipRangeFilter := d.Get("ip_range_filter").(string) @@ -330,6 +333,7 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) if err != nil { return fmt.Errorf("Error checking if CosmosDB Account %q already exists (Resource Group %q): %+v", name, resourceGroup, err) } + if !utils.ResponseWasNotFound(r) { return fmt.Errorf("CosmosDB Account %s already exists, please import the resource via terraform import", name) } @@ -365,7 +369,19 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) VirtualNetworkRules: expandAzureRmCosmosDBAccountVirtualNetworkRules(d), EnableMultipleWriteLocations: utils.Bool(enableMultipleWriteLocations), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), + } + + // additional validation on MaxStalenessPrefix as it varies depending on if the DB is multi region or not + + cp := account.DatabaseAccountCreateUpdateProperties.ConsistencyPolicy + if len(geoLocations) > 1 && cp != nil && cp.DefaultConsistencyLevel == documentdb.BoundedStaleness { + if msp := cp.MaxStalenessPrefix; msp != nil && *msp < 100000 { + return fmt.Errorf("Error max_staleness_prefix (%d) must be greater then 100000 when more then one geo_location is used", *msp) + } + if mis := cp.MaxIntervalInSeconds; mis != nil && *mis < 300 { + return fmt.Errorf("Error max_interval_in_seconds (%d) must be greater then 300 (5min) when more then one geo_location is used", *mis) + } } resp, err := resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account) @@ -399,7 +415,7 @@ func resourceArmCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) } func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Cosmos DB Account update.") @@ -407,7 +423,7 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) name := d.Get("name").(string) location := d.Get("location").(string) resourceGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) kind := d.Get("kind").(string) offerType := d.Get("offer_type").(string) @@ -469,7 +485,7 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) VirtualNetworkRules: expandAzureRmCosmosDBAccountVirtualNetworkRules(d), EnableMultipleWriteLocations: utils.Bool(enableMultipleWriteLocations), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = resourceArmCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account); err != nil { @@ -499,7 +515,6 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) removedOne = true } } - } if removedOne { @@ -547,10 +562,10 @@ func resourceArmCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -573,7 +588,6 @@ func resourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) er d.Set("location", azure.NormalizeLocation(*location)) } d.Set("resource_group_name", resourceGroup) - flattenAndSetTags(d, resp.Tags) d.Set("kind", string(resp.Kind)) d.Set("offer_type", string(resp.DatabaseAccountOfferType)) @@ -614,20 +628,32 @@ func resourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error setting `virtual_network_rule`: %+v", err) } + readEndpoints := make([]string, 0) if p := resp.ReadLocations; p != nil { - readEndpoints := make([]string, 0) for _, l := range *p { + if l.DocumentEndpoint == nil { + continue + } + readEndpoints = append(readEndpoints, *l.DocumentEndpoint) } - d.Set("read_endpoints", readEndpoints) + } + if err := d.Set("read_endpoints", readEndpoints); err != nil { + return fmt.Errorf("Error setting `read_endpoints`: %s", err) } + writeEndpoints := make([]string, 0) if p := resp.WriteLocations; p != nil { - writeEndpoints := make([]string, 0) for _, l := range *p { + if l.DocumentEndpoint == nil { + continue + } + writeEndpoints = append(writeEndpoints, *l.DocumentEndpoint) } - d.Set("write_endpoints", writeEndpoints) + } + if err := d.Set("write_endpoints", writeEndpoints); err != nil { + return fmt.Errorf("Error setting `write_endpoints`: %s", err) } // ListKeys returns a data structure containing a DatabaseAccountListReadOnlyKeysResult pointer @@ -675,18 +701,17 @@ func resourceArmCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) er for i, v := range *connStringResp.ConnectionStrings { connStrings[i] = *v.ConnectionString } - } d.Set("connection_strings", connStrings) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -706,13 +731,11 @@ func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) stateConf := &resource.StateChangeConf{ Pending: []string{"Deleting"}, Target: []string{"NotFound"}, - Timeout: 60 * time.Minute, + Timeout: 180 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, resourceGroup, name) if err2 != nil { - if utils.ResponseWasNotFound(resp.Response) { return resp, "NotFound", nil } @@ -729,7 +752,7 @@ func resourceArmCosmosDbAccountDelete(d *schema.ResourceData, meta interface{}) return nil } -func resourceArmCosmosDbAccountApiUpsert(client documentdb.DatabaseAccountsClient, ctx context.Context, resourceGroup string, name string, account documentdb.DatabaseAccountCreateUpdateParameters) (*documentdb.DatabaseAccount, error) { +func resourceArmCosmosDbAccountApiUpsert(client *documentdb.DatabaseAccountsClient, ctx context.Context, resourceGroup string, name string, account documentdb.DatabaseAccountCreateUpdateParameters) (*documentdb.DatabaseAccount, error) { future, err := client.CreateOrUpdate(ctx, resourceGroup, name, account) if err != nil { return nil, fmt.Errorf("Error creating/updating CosmosDB Account %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -743,11 +766,10 @@ func resourceArmCosmosDbAccountApiUpsert(client documentdb.DatabaseAccountsClien stateConf := &resource.StateChangeConf{ Pending: []string{"Creating", "Updating", "Deleting"}, Target: []string{"Succeeded"}, - Timeout: 60 * time.Minute, + Timeout: 180 * time.Minute, MinTimeout: 30 * time.Second, Delay: 30 * time.Second, // required because it takes some time before the 'creating' location shows up Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, resourceGroup, name) if err2 != nil { return nil, "", fmt.Errorf("Error reading CosmosDB Account %q after create/update (Resource Group %q): %+v", name, resourceGroup, err2) @@ -800,7 +822,6 @@ func resourceArmCosmosDbAccountGenerateDefaultId(databaseName string, location s } func expandAzureRmCosmosDBAccountGeoLocations(databaseName string, d *schema.ResourceData) ([]documentdb.Location, error) { - locations := make([]documentdb.Location, 0) for _, l := range d.Get("geo_location").(*schema.Set).List() { data := l.(map[string]interface{}) @@ -825,7 +846,6 @@ func expandAzureRmCosmosDBAccountGeoLocations(databaseName string, d *schema.Res byPriorities := make(map[int]interface{}, len(locations)) byName := make(map[string]interface{}, len(locations)) for _, location := range locations { - priority := int(*location.FailoverPriority) name := *location.LocationName @@ -851,7 +871,6 @@ func expandAzureRmCosmosDBAccountGeoLocations(databaseName string, d *schema.Res //todo remove when deprecated field `failover_policy` is func expandAzureRmCosmosDBAccountFailoverPolicy(databaseName string, d *schema.ResourceData) ([]documentdb.Location, error) { - input := d.Get("failover_policy").(*schema.Set).List() locations := make([]documentdb.Location, 0, len(input)) @@ -899,7 +918,6 @@ func expandAzureRmCosmosDBAccountFailoverPolicy(databaseName string, d *schema.R } func expandAzureRmCosmosDBAccountCapabilities(d *schema.ResourceData) *[]documentdb.Capability { - capabilities := d.Get("capabilities").(*schema.Set).List() s := make([]documentdb.Capability, 0) @@ -923,7 +941,6 @@ func expandAzureRmCosmosDBAccountVirtualNetworkRules(d *schema.ResourceData) *[] } func flattenAzureRmCosmosDBAccountConsistencyPolicy(policy *documentdb.ConsistencyPolicy) []interface{} { - result := map[string]interface{}{} result["consistency_level"] = string(policy.DefaultConsistencyLevel) if policy.MaxIntervalInSeconds != nil { diff --git a/azurerm/resource_arm_cosmosdb_account_failover_test.go b/azurerm/resource_arm_cosmosdb_account_failover_test.go index 0a4288d75f2d..081fff4ac7a1 100644 --- a/azurerm/resource_arm_cosmosdb_account_failover_test.go +++ b/azurerm/resource_arm_cosmosdb_account_failover_test.go @@ -30,7 +30,6 @@ func TestAccAzureRMCosmosDBAccount_failover_boundedStaleness(t *testing.T) { } func TestAccAzureRMCosmosDBAccount_failover_boundedStalenessComplete(t *testing.T) { - ri := tf.AccRandTimeInt() config := testAccAzureRMCosmosDBAccount_failover_boundedStalenessComplete(ri, testLocation()) @@ -128,7 +127,6 @@ func TestAccAzureRMCosmosDBAccount_failover_strong(t *testing.T) { } func TestAccAzureRMCosmosDBAccount_failover_geoReplicated(t *testing.T) { - ri := tf.AccRandTimeInt() config := testAccAzureRMCosmosDBAccount_failover_geoReplicated(ri, testLocation(), testAltLocation()) @@ -315,8 +313,8 @@ resource "azurerm_cosmosdb_account" "test" { consistency_policy { consistency_level = "BoundedStaleness" - max_interval_in_seconds = 10 - max_staleness_prefix = 200 + max_interval_in_seconds = 333 + max_staleness_prefix = 101101 } failover_policy { diff --git a/azurerm/resource_arm_cosmosdb_account_test.go b/azurerm/resource_arm_cosmosdb_account_test.go index b24186b58d68..ae3fbac9a37d 100644 --- a/azurerm/resource_arm_cosmosdb_account_test.go +++ b/azurerm/resource_arm_cosmosdb_account_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) // TODO: refactor the test configs @@ -40,7 +41,7 @@ func TestAccAzureRMCosmosDBAccount_eventualConsistency(t *testing.T) { }) } func TestAccAzureRMCosmosDBAccount_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -476,6 +477,30 @@ func TestAccAzureRMCosmosDBAccount_geoReplicated_customId(t *testing.T) { }) } +func TestAccAzureRMCosmosDBAccount_geoReplicated_non_boundedStaleness_cp(t *testing.T) { + ri := tf.AccRandTimeInt() + resourceName := "azurerm_cosmosdb_account.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCosmosDBAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMCosmosDBAccount_geoReplicated_customConsistencyLevel(ri, testLocation(), testAltLocation(), documentdb.Session), + Check: resource.ComposeAggregateTestCheckFunc( + checkAccAzureRMCosmosDBAccount_basic(resourceName, testLocation(), string(documentdb.Session), 2), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMCosmosDBAccount_geoReplicated_add_remove(t *testing.T) { ri := tf.AccRandTimeInt() resourceName := "azurerm_cosmosdb_account.test" @@ -644,7 +669,7 @@ func TestAccAzureRMCosmosDBAccount_multiMaster(t *testing.T) { } func testCheckAzureRMCosmosDBAccountDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + conn := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -670,7 +695,7 @@ func testCheckAzureRMCosmosDBAccountDestroy(s *terraform.State) error { func testCheckAzureRMCosmosDBAccountExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + conn := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -825,7 +850,12 @@ func testAccAzureRMCosmosDBAccount_capabilityDocLevelTTL(rInt int, location stri } func testAccAzureRMCosmosDBAccount_geoReplicated(rInt int, location string, altLocation string) string { - return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), "", fmt.Sprintf(` + co := ` + max_interval_in_seconds = 373 + max_staleness_prefix = 100001 +` + + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), co, fmt.Sprintf(` geo_location { location = "%s" failover_priority = 1 @@ -835,7 +865,12 @@ func testAccAzureRMCosmosDBAccount_geoReplicated(rInt int, location string, altL } func testAccAzureRMCosmosDBAccount_multiMaster(rInt int, location string, altLocation string) string { - return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), "", fmt.Sprintf(` + co := ` + max_interval_in_seconds = 373 + max_staleness_prefix = 100001 +` + + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), co, fmt.Sprintf(` enable_multiple_write_locations = true geo_location { @@ -847,7 +882,23 @@ func testAccAzureRMCosmosDBAccount_multiMaster(rInt int, location string, altLoc } func testAccAzureRMCosmosDBAccount_geoReplicated_customId(rInt int, location string, altLocation string) string { - return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), "", fmt.Sprintf(` + co := ` + max_interval_in_seconds = 373 + max_staleness_prefix = 100001 +` + + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), co, fmt.Sprintf(` + geo_location { + prefix = "acctest-%d-custom-id" + location = "%s" + failover_priority = 1 + } + + `, rInt, altLocation)) +} + +func testAccAzureRMCosmosDBAccount_geoReplicated_customConsistencyLevel(rInt int, location string, altLocation string, cLevel documentdb.DefaultConsistencyLevel) string { + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(cLevel), "", fmt.Sprintf(` geo_location { prefix = "acctest-%d-custom-id" location = "%s" @@ -858,7 +909,11 @@ func testAccAzureRMCosmosDBAccount_geoReplicated_customId(rInt int, location str } func testAccAzureRMCosmosDBAccount_complete(rInt int, location string, altLocation string) string { - return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), "", fmt.Sprintf(` + co := ` + max_interval_in_seconds = 373 + max_staleness_prefix = 100001 +` + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), co, fmt.Sprintf(` ip_range_filter = "104.42.195.92,40.76.54.131,52.176.6.30,52.169.50.45/32,52.187.184.26,10.20.0.0/16" enable_automatic_failover = true @@ -871,7 +926,12 @@ func testAccAzureRMCosmosDBAccount_complete(rInt int, location string, altLocati } func testAccAzureRMCosmosDBAccount_emptyIpFilter(rInt int, location string, altLocation string) string { - return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), "", fmt.Sprintf(` + co := ` + max_interval_in_seconds = 373 + max_staleness_prefix = 100001 +` + + return testAccAzureRMCosmosDBAccount_basic(rInt, location, string(documentdb.BoundedStaleness), co, fmt.Sprintf(` ip_range_filter = "" enable_automatic_failover = true diff --git a/azurerm/resource_arm_cosmosdb_cassandra_keyspace.go b/azurerm/resource_arm_cosmosdb_cassandra_keyspace.go index 2c301873da7b..8793bceb3386 100644 --- a/azurerm/resource_arm_cosmosdb_cassandra_keyspace.go +++ b/azurerm/resource_arm_cosmosdb_cassandra_keyspace.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,14 +45,14 @@ func resourceArmCosmosDbCassandraKeyspace() *schema.Resource { } func resourceArmCosmosDbCassandraKeyspaceCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) account := d.Get("account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetCassandraKeyspace(ctx, resourceGroup, account, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -100,7 +101,7 @@ func resourceArmCosmosDbCassandraKeyspaceCreate(d *schema.ResourceData, meta int } func resourceArmCosmosDbCassandraKeyspaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosKeyspaceID(d.Id()) @@ -129,7 +130,7 @@ func resourceArmCosmosDbCassandraKeyspaceRead(d *schema.ResourceData, meta inter } func resourceArmCosmosDbCassandraKeyspaceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosKeyspaceID(d.Id()) diff --git a/azurerm/resource_arm_cosmosdb_cassandra_keyspace_test.go b/azurerm/resource_arm_cosmosdb_cassandra_keyspace_test.go index 83c151a92226..c40a6033ed5c 100644 --- a/azurerm/resource_arm_cosmosdb_cassandra_keyspace_test.go +++ b/azurerm/resource_arm_cosmosdb_cassandra_keyspace_test.go @@ -36,7 +36,7 @@ func TestAccAzureRMCosmosDbCassandraKeyspace_basic(t *testing.T) { } func testCheckAzureRMCosmosDbCassandraKeyspaceDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -65,7 +65,7 @@ func testCheckAzureRMCosmosDbCassandraKeyspaceDestroy(s *terraform.State) error func testCheckAzureRMCosmosDbCassandraKeyspaceExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_cosmosdb_mongo_collection.go b/azurerm/resource_arm_cosmosdb_mongo_collection.go index 9727cbf481b0..dea152d3d808 100644 --- a/azurerm/resource_arm_cosmosdb_mongo_collection.go +++ b/azurerm/resource_arm_cosmosdb_mongo_collection.go @@ -12,6 +12,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -92,7 +93,7 @@ func resourceArmCosmosDbMongoCollection() *schema.Resource { } func resourceArmCosmosDbMongoCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -100,7 +101,7 @@ func resourceArmCosmosDbMongoCollectionCreateUpdate(d *schema.ResourceData, meta account := d.Get("account_name").(string) database := d.Get("database_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetMongoDBCollection(ctx, resourceGroup, account, database, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -161,7 +162,7 @@ func resourceArmCosmosDbMongoCollectionCreateUpdate(d *schema.ResourceData, meta } func resourceArmCosmosDbMongoCollectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseCollectionID(d.Id()) @@ -195,19 +196,20 @@ func resourceArmCosmosDbMongoCollectionRead(d *schema.ResourceData, meta interfa d.Set("shard_key", k) } - indexes, ttl := flattenCosmosMongoCollectionIndexes(props.Indexes) - d.Set("default_ttl_seconds", ttl) - if err := d.Set("indexes", indexes); err != nil { - return fmt.Errorf("Error setting `indexes`: %+v", err) + if props.Indexes != nil { + indexes, ttl := flattenCosmosMongoCollectionIndexes(props.Indexes) + d.Set("default_ttl_seconds", ttl) + if err := d.Set("indexes", indexes); err != nil { + return fmt.Errorf("Error setting `indexes`: %+v", err) + } } - } return nil } func resourceArmCosmosDbMongoCollectionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseCollectionID(d.Id()) diff --git a/azurerm/resource_arm_cosmosdb_mongo_collection_test.go b/azurerm/resource_arm_cosmosdb_mongo_collection_test.go index 8313d50fa7b2..c67b65e9b284 100644 --- a/azurerm/resource_arm_cosmosdb_mongo_collection_test.go +++ b/azurerm/resource_arm_cosmosdb_mongo_collection_test.go @@ -109,7 +109,7 @@ func TestAccAzureRMCosmosDbMongoCollection_update(t *testing.T) { } func testCheckAzureRMCosmosDbMongoCollectionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -139,7 +139,7 @@ func testCheckAzureRMCosmosDbMongoCollectionDestroy(s *terraform.State) error { func testCheckAzureRMCosmosDbMongoCollectionExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_cosmosdb_mongo_database.go b/azurerm/resource_arm_cosmosdb_mongo_database.go index d4ccc8289514..93f82a4d35f2 100644 --- a/azurerm/resource_arm_cosmosdb_mongo_database.go +++ b/azurerm/resource_arm_cosmosdb_mongo_database.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,14 +45,14 @@ func resourceArmCosmosDbMongoDatabase() *schema.Resource { } func resourceArmCosmosDbMongoDatabaseCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) account := d.Get("account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetMongoDBDatabase(ctx, resourceGroup, account, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -100,7 +101,7 @@ func resourceArmCosmosDbMongoDatabaseCreate(d *schema.ResourceData, meta interfa } func resourceArmCosmosDbMongoDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseID(d.Id()) @@ -129,7 +130,7 @@ func resourceArmCosmosDbMongoDatabaseRead(d *schema.ResourceData, meta interface } func resourceArmCosmosDbMongoDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseID(d.Id()) diff --git a/azurerm/resource_arm_cosmosdb_mongo_database_test.go b/azurerm/resource_arm_cosmosdb_mongo_database_test.go index f5cbcc4b5d4d..8fdacdb2e43e 100644 --- a/azurerm/resource_arm_cosmosdb_mongo_database_test.go +++ b/azurerm/resource_arm_cosmosdb_mongo_database_test.go @@ -36,7 +36,7 @@ func TestAccAzureRMCosmosDbMongoDatabase_basic(t *testing.T) { } func testCheckAzureRMCosmosDbMongoDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -65,7 +65,7 @@ func testCheckAzureRMCosmosDbMongoDatabaseDestroy(s *terraform.State) error { func testCheckAzureRMCosmosDbMongoDatabaseExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_cosmosdb_sql_container.go b/azurerm/resource_arm_cosmosdb_sql_container.go new file mode 100644 index 000000000000..f515aa17d2d5 --- /dev/null +++ b/azurerm/resource_arm_cosmosdb_sql_container.go @@ -0,0 +1,261 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2015-04-08/documentdb" + "github.com/hashicorp/terraform/helper/schema" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmCosmosDbSQLContainer() *schema.Resource { + return &schema.Resource{ + Create: resourceArmCosmosDbSQLContainerCreate, + Read: resourceArmCosmosDbSQLContainerRead, + Delete: resourceArmCosmosDbSQLContainerDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.CosmosEntityName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.CosmosAccountName, + }, + + "database_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.CosmosEntityName, + }, + + "partition_key_path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "unique_key": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paths": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + }, + } +} + +func resourceArmCosmosDbSQLContainerCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).cosmos.DatabaseClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + database := d.Get("database_name").(string) + account := d.Get("account_name").(string) + partitionkeypaths := d.Get("partition_key_path").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.GetSQLContainer(ctx, resourceGroup, account, database, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of creating Cosmos SQL Container %s (Account: %s, Database:%s): %+v", name, account, database, err) + } + } else { + id, err := azure.CosmosGetIDFromResponse(existing.Response) + if err != nil { + return fmt.Errorf("Error generating import ID for Cosmos SQL Container '%s' (Account: %s, Database:%s)", name, account, database) + } + + return tf.ImportAsExistsError("azurerm_cosmosdb_sql_container", id) + } + } + + db := documentdb.SQLContainerCreateUpdateParameters{ + SQLContainerCreateUpdateProperties: &documentdb.SQLContainerCreateUpdateProperties{ + Resource: &documentdb.SQLContainerResource{ + ID: &name, + }, + Options: map[string]*string{}, + }, + } + + if partitionkeypaths != "" { + db.SQLContainerCreateUpdateProperties.Resource.PartitionKey = &documentdb.ContainerPartitionKey{ + Paths: &[]string{partitionkeypaths}, + Kind: documentdb.PartitionKindHash, + } + } + + if keys := expandCosmosSQLContainerUniqueKeys(d.Get("unique_key").(*schema.Set)); keys != nil { + db.SQLContainerCreateUpdateProperties.Resource.UniqueKeyPolicy = &documentdb.UniqueKeyPolicy{ + UniqueKeys: keys, + } + } + + future, err := client.CreateUpdateSQLContainer(ctx, resourceGroup, account, database, name, db) + if err != nil { + return fmt.Errorf("Error issuing create/update request for Cosmos SQL Container %s (Account: %s, Database:%s): %+v", name, account, database, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting on create/update future for Cosmos SQL Container %s (Account: %s, Database:%s): %+v", name, account, database, err) + } + + resp, err := client.GetSQLContainer(ctx, resourceGroup, account, database, name) + if err != nil { + return fmt.Errorf("Error making get request for Cosmos SQL Container %s (Account: %s, Database:%s): %+v", name, account, database, err) + } + + id, err := azure.CosmosGetIDFromResponse(resp.Response) + if err != nil { + return fmt.Errorf("Error retrieving the ID for Cosmos SQL Container '%s' (Account: %s, Database:%s) ID: %v", name, account, database, err) + } + d.SetId(id) + + return resourceArmCosmosDbSQLContainerRead(d, meta) +} + +func resourceArmCosmosDbSQLContainerRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).cosmos.DatabaseClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseCosmosDatabaseContainerID(d.Id()) + if err != nil { + return err + } + + resp, err := client.GetSQLContainer(ctx, id.ResourceGroup, id.Account, id.Database, id.Container) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading Cosmos SQL Container %s (Account %s) - removing from state", id.Database, id.Container) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Cosmos SQL Container %s (Account %s): %+v", id.Database, id.Container, err) + } + + d.Set("name", id.Container) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("account_name", id.Account) + d.Set("database_name", id.Database) + + if props := resp.SQLContainerProperties; props != nil { + if pk := props.PartitionKey; pk != nil { + if paths := pk.Paths; paths != nil { + if len(*paths) > 1 { + return fmt.Errorf("Error reading PartitionKey Paths, more then 1 returned") + } else if len(*paths) == 1 { + d.Set("partition_key_path", (*paths)[0]) + } + } + } + + if ukp := props.UniqueKeyPolicy; ukp != nil { + if err := d.Set("unique_key", flattenCosmosSQLContainerUniqueKeys(ukp.UniqueKeys)); err != nil { + return fmt.Errorf("Error setting `unique_key`: %+v", err) + } + } + } + + return nil +} + +func resourceArmCosmosDbSQLContainerDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).cosmos.DatabaseClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseCosmosDatabaseContainerID(d.Id()) + if err != nil { + return err + } + + future, err := client.DeleteSQLContainer(ctx, id.ResourceGroup, id.Account, id.Database, id.Container) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error deleting Cosmos SQL Container %s (Account %s): %+v", id.Database, id.Container, err) + } + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting on delete future for Cosmos SQL Container %s (Account %s): %+v", id.Database, id.Account, err) + } + + return nil +} + +func expandCosmosSQLContainerUniqueKeys(s *schema.Set) *[]documentdb.UniqueKey { + i := s.List() + if len(i) <= 0 || i[0] == nil { + return nil + } + + keys := make([]documentdb.UniqueKey, 0) + for _, k := range i { + key := k.(map[string]interface{}) + + paths := key["paths"].(*schema.Set).List() + if len(paths) == 0 { + continue + } + + keys = append(keys, documentdb.UniqueKey{ + Paths: utils.ExpandStringSlice(paths), + }) + } + + return &keys +} + +func flattenCosmosSQLContainerUniqueKeys(keys *[]documentdb.UniqueKey) *[]map[string]interface{} { + if keys == nil { + return nil + } + + slice := make([]map[string]interface{}, 0) + for _, k := range *keys { + if k.Paths == nil { + continue + } + + slice = append(slice, map[string]interface{}{ + "paths": *k.Paths, + }) + } + + return &slice +} diff --git a/azurerm/resource_arm_cosmosdb_sql_container_test.go b/azurerm/resource_arm_cosmosdb_sql_container_test.go new file mode 100644 index 000000000000..c4e9d8e6c741 --- /dev/null +++ b/azurerm/resource_arm_cosmosdb_sql_container_test.go @@ -0,0 +1,185 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMCosmosDbSqlContainer_basic(t *testing.T) { + ri := tf.AccRandTimeInt() + resourceName := "azurerm_cosmosdb_sql_container.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCosmosDbSqlContainerDestroy, + Steps: []resource.TestStep{ + { + + Config: testAccAzureRMCosmosDbSqlContainer_basic(ri, testLocation()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckAzureRMCosmosDbSqlContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMCosmosDbSqlContainer_complete(t *testing.T) { + ri := tf.AccRandTimeInt() + resourceName := "azurerm_cosmosdb_sql_container.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCosmosDbSqlContainerDestroy, + Steps: []resource.TestStep{ + { + + Config: testAccAzureRMCosmosDbSqlContainer_complete(ri, testLocation()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckAzureRMCosmosDbSqlContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMCosmosDbSqlContainer_update(t *testing.T) { + ri := tf.AccRandTimeInt() + resourceName := "azurerm_cosmosdb_sql_container.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCosmosDbSqlContainerDestroy, + Steps: []resource.TestStep{ + { + + Config: testAccAzureRMCosmosDbSqlContainer_basic(ri, testLocation()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckAzureRMCosmosDbSqlContainerExists(resourceName), + ), + }, + { + + Config: testAccAzureRMCosmosDbSqlContainer_complete(ri, testLocation()), + Check: resource.ComposeAggregateTestCheckFunc( + testCheckAzureRMCosmosDbSqlContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMCosmosDbSqlContainerDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_cosmosdb_sql_container" { + continue + } + + name := rs.Primary.Attributes["name"] + account := rs.Primary.Attributes["account_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + database := rs.Primary.Attributes["database_name"] + + resp, err := client.GetSQLContainer(ctx, resourceGroup, account, database, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Error checking destroy for Cosmos SQL Container %s (account %s) still exists:\n%v", name, account, err) + } + } + + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Cosmos SQL Container %s (account %s) still exists:\n%#v", name, account, resp) + } + } + + return nil +} + +func testCheckAzureRMCosmosDbSqlContainerExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + // 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"] + account := rs.Primary.Attributes["account_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + database := rs.Primary.Attributes["database_name"] + + resp, err := client.GetSQLContainer(ctx, resourceGroup, database, account, name) + if err != nil { + return fmt.Errorf("Bad: Get on cosmosAccountsClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Cosmos Container '%s' (account: '%s') does not exist", name, account) + } + + return nil + } +} + +func testAccAzureRMCosmosDbSqlContainer_basic(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_cosmosdb_sql_container" "test" { + name = "acctest-CSQLC-%[2]d" + resource_group_name = "${azurerm_cosmosdb_account.test.resource_group_name}" + account_name = "${azurerm_cosmosdb_account.test.name}" + database_name = "${azurerm_cosmosdb_sql_database.test.name}" +} + + +`, testAccAzureRMCosmosDbSqlDatabase_basic(rInt, location), rInt) +} + +func testAccAzureRMCosmosDbSqlContainer_complete(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_cosmosdb_sql_container" "test" { + name = "acctest-CSQLC-%[2]d" + resource_group_name = "${azurerm_cosmosdb_account.test.resource_group_name}" + account_name = "${azurerm_cosmosdb_account.test.name}" + database_name = "${azurerm_cosmosdb_sql_database.test.name}" + partition_key_path = "/definition/id" + unique_key { + paths = ["/definition/id1", "/definition/id2"] + } +} + +`, testAccAzureRMCosmosDbSqlDatabase_basic(rInt, location), rInt) +} diff --git a/azurerm/resource_arm_cosmosdb_sql_database.go b/azurerm/resource_arm_cosmosdb_sql_database.go index 3a3eb8b4c063..393d5253b5bc 100644 --- a/azurerm/resource_arm_cosmosdb_sql_database.go +++ b/azurerm/resource_arm_cosmosdb_sql_database.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,14 +45,14 @@ func resourceArmCosmosDbSQLDatabase() *schema.Resource { } func resourceArmCosmosDbSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) account := d.Get("account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetSQLDatabase(ctx, resourceGroup, account, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -100,7 +101,7 @@ func resourceArmCosmosDbSQLDatabaseCreate(d *schema.ResourceData, meta interface } func resourceArmCosmosDbSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseID(d.Id()) @@ -129,7 +130,7 @@ func resourceArmCosmosDbSQLDatabaseRead(d *schema.ResourceData, meta interface{} } func resourceArmCosmosDbSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosDatabaseID(d.Id()) diff --git a/azurerm/resource_arm_cosmosdb_sql_database_test.go b/azurerm/resource_arm_cosmosdb_sql_database_test.go index 2f1323473d8d..ec27ea549dea 100644 --- a/azurerm/resource_arm_cosmosdb_sql_database_test.go +++ b/azurerm/resource_arm_cosmosdb_sql_database_test.go @@ -37,7 +37,7 @@ func TestAccAzureRMCosmosDbSqlDatabase_basic(t *testing.T) { } func testCheckAzureRMCosmosDbSqlDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -66,7 +66,7 @@ func testCheckAzureRMCosmosDbSqlDatabaseDestroy(s *terraform.State) error { func testCheckAzureRMCosmosDbSqlDatabaseExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_cosmosdb_table.go b/azurerm/resource_arm_cosmosdb_table.go index 13a63e7c90ca..4f0385315ed3 100644 --- a/azurerm/resource_arm_cosmosdb_table.go +++ b/azurerm/resource_arm_cosmosdb_table.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,14 +45,14 @@ func resourceArmCosmosDbTable() *schema.Resource { } func resourceArmCosmosDbTableCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) account := d.Get("account_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetTable(ctx, resourceGroup, account, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -100,7 +101,7 @@ func resourceArmCosmosDbTableCreate(d *schema.ResourceData, meta interface{}) er } func resourceArmCosmosDbTableRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosTableID(d.Id()) @@ -129,7 +130,7 @@ func resourceArmCosmosDbTableRead(d *schema.ResourceData, meta interface{}) erro } func resourceArmCosmosDbTableDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).cosmosAccountsClient + client := meta.(*ArmClient).cosmos.DatabaseClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseCosmosTableID(d.Id()) diff --git a/azurerm/resource_arm_cosmosdb_table_test.go b/azurerm/resource_arm_cosmosdb_table_test.go index bb3475f60f10..db908aefb753 100644 --- a/azurerm/resource_arm_cosmosdb_table_test.go +++ b/azurerm/resource_arm_cosmosdb_table_test.go @@ -36,7 +36,7 @@ func TestAccAzureRMCosmosDbTable_basic(t *testing.T) { } func testCheckAzureRMCosmosDbTableDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -65,7 +65,7 @@ func testCheckAzureRMCosmosDbTableDestroy(s *terraform.State) error { func testCheckAzureRMCosmosDbTableExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).cosmosAccountsClient + client := testAccProvider.Meta().(*ArmClient).cosmos.DatabaseClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_dashboard.go b/azurerm/resource_arm_dashboard.go new file mode 100644 index 000000000000..6eb40ee6659f --- /dev/null +++ b/azurerm/resource_arm_dashboard.go @@ -0,0 +1,153 @@ +package azurerm + +import ( + "encoding/json" + "fmt" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/preview/portal/mgmt/2019-01-01-preview/portal" + "github.com/hashicorp/terraform/helper/schema" + "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/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmDashboard() *schema.Resource { + return &schema.Resource{ + Create: resourceArmDashboardCreateUpdate, + Read: resourceArmDashboardRead, + Update: resourceArmDashboardCreateUpdate, + Delete: resourceArmDashboardDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateDashboardName, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + "location": azure.SchemaLocation(), + "tags": tags.Schema(), + "dashboard_properties": { + Type: schema.TypeString, + Optional: true, + Computed: true, + StateFunc: normalizeJson, + }, + }, + } +} + +func resourceArmDashboardCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).portal.DashboardsClient + ctx := meta.(*ArmClient).StopContext + + t := d.Get("tags").(map[string]interface{}) + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + location := azure.NormalizeLocation(d.Get("location").(string)) + dashboardProps := d.Get("dashboard_properties").(string) + + dashboard := portal.Dashboard{ + Location: &location, + Tags: tags.Expand(t), + } + + var dashboardProperties portal.DashboardProperties + + if err := json.Unmarshal([]byte(dashboardProps), &dashboardProperties); err != nil { + return fmt.Errorf("Error parsing JSON: %+v", err) + } + dashboard.DashboardProperties = &dashboardProperties + + _, err := client.CreateOrUpdate(ctx, resourceGroup, name, dashboard) + if err != nil { + return fmt.Errorf("Error creating/updating Dashboard %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + // get it back again to set the props + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error making Read request for Dashboard %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmDashboardRead(d, meta) +} + +func resourceArmDashboardRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).portal.DashboardsClient + ctx := meta.(*ArmClient).StopContext + + id, parseErr := azure.ParseAzureResourceID(d.Id()) + if parseErr != nil { + return parseErr + } + resourceGroup := id.ResourceGroup + name := id.Path["dashboards"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request for Dashboard %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*resp.Location)) + } + + props, jsonErr := json.Marshal(resp.DashboardProperties) + if jsonErr != nil { + return fmt.Errorf("Error parsing DashboardProperties JSON: %+v", jsonErr) + } + d.Set("dashboard_properties", string(props)) + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmDashboardDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).portal.DashboardsClient + ctx := meta.(*ArmClient).StopContext + + id, parseErr := azure.ParseAzureResourceID(d.Id()) + if parseErr != nil { + return parseErr + } + resourceGroup := id.ResourceGroup + name := id.Path["dashboards"] + + resp, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Error retrieving Key Vault %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} + +func validateDashboardName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if len(value) > 64 { + errors = append(errors, fmt.Errorf("%q may not exceed 64 characters in length", k)) + } + + // only alpanumeric and hyphens + if matched := regexp.MustCompile(`^[-\w]+$`).Match([]byte(value)); !matched { + errors = append(errors, fmt.Errorf("%q may only contain alphanumeric and hyphen characters", k)) + } + + return warnings, errors +} diff --git a/azurerm/resource_arm_dashboard_test.go b/azurerm/resource_arm_dashboard_test.go new file mode 100644 index 000000000000..86b1d2487a23 --- /dev/null +++ b/azurerm/resource_arm_dashboard_test.go @@ -0,0 +1,139 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccResourceArmDashboard_basic(t *testing.T) { + resourceName := "azurerm_dashboard.test" + ri := tf.AccRandTimeInt() + config := testResourceArmDashboard_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMDashboardDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMDashboardExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMDashboardExists(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) + } + + dashboardName := 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 Dashboard: %s", dashboardName) + } + + client := testAccProvider.Meta().(*ArmClient).portal.DashboardsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, dashboardName) + if err != nil { + return fmt.Errorf("Bad: Get on dashboardsClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Dashboard %q (resource group: %q) does not exist", dashboardName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMDashboardDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).portal.DashboardsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_dashboard" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Dashboard still exists:\n%+v", resp) + } + } + + return nil +} + +func testResourceArmDashboard_basic(rInt int, location string) string { + return fmt.Sprintf(` + resource "azurerm_resource_group" "test-group"{ + name = "acctestRG-%d" + location = "%s" + } + + resource "azurerm_dashboard" "test" { + name = "my-test-dashboard" + resource_group_name = azurerm_resource_group.test-group.name + location = azurerm_resource_group.test-group.location + dashboard_properties = < 0) || + (rulesets.VirtualNetworkRules != nil && len(*rulesets.VirtualNetworkRules) > 0) { + return fmt.Errorf("network_rulesets cannot be used when the SKU is basic") + } + } + } + return resourceArmEventHubNamespaceRead(d, meta) } @@ -182,7 +275,7 @@ func resourceArmEventHubNamespaceRead(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).eventhub.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -204,8 +297,25 @@ func resourceArmEventHubNamespaceRead(d *schema.ResourceData, meta interface{}) d.Set("location", azure.NormalizeLocation(*location)) } - d.Set("sku", string(resp.Sku.Name)) - d.Set("capacity", resp.Sku.Capacity) + if sku := resp.Sku; sku != nil { + d.Set("sku", string(sku.Name)) + d.Set("capacity", sku.Capacity) + } + + if props := resp.EHNamespaceProperties; props != nil { + d.Set("auto_inflate_enabled", props.IsAutoInflateEnabled) + d.Set("kafka_enabled", props.KafkaEnabled) + d.Set("maximum_throughput_units", int(*props.MaximumThroughputUnits)) + } + + ruleset, err := client.GetNetworkRuleSet(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error making Read request on EventHub Namespace %q Network Ruleset: %+v", name, err) + } + + if err := d.Set("network_rulesets", flattenEventHubNamespaceNetworkRuleset(ruleset)); err != nil { + return fmt.Errorf("Error setting `network_ruleset` for Evenhub Namespace %s: %v", name, err) + } keys, err := client.ListKeys(ctx, resGroup, name, eventHubNamespaceDefaultAuthorizationRule) if err != nil { @@ -217,22 +327,14 @@ func resourceArmEventHubNamespaceRead(d *schema.ResourceData, meta interface{}) d.Set("default_secondary_key", keys.SecondaryKey) } - if props := resp.EHNamespaceProperties; props != nil { - d.Set("auto_inflate_enabled", props.IsAutoInflateEnabled) - d.Set("kafka_enabled", props.KafkaEnabled) - d.Set("maximum_throughput_units", int(*props.MaximumThroughputUnits)) - } - - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmEventHubNamespaceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).eventhub.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -250,7 +352,7 @@ func resourceArmEventHubNamespaceDelete(d *schema.ResourceData, meta interface{} return waitForEventHubNamespaceToBeDeleted(ctx, client, resGroup, name) } -func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client eventhub.NamespacesClient, resourceGroup, name string) error { +func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client *eventhub.NamespacesClient, resourceGroup, name string) error { // we can't use the Waiter here since the API returns a 200 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for EventHub Namespace (%q in Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ @@ -266,7 +368,7 @@ func waitForEventHubNamespaceToBeDeleted(ctx context.Context, client eventhub.Na return nil } -func eventHubNamespaceStateStatusCodeRefreshFunc(ctx context.Context, client eventhub.NamespacesClient, resourceGroup, name string) resource.StateRefreshFunc { +func eventHubNamespaceStateStatusCodeRefreshFunc(ctx context.Context, client *eventhub.NamespacesClient, resourceGroup, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroup, name) @@ -282,3 +384,94 @@ func eventHubNamespaceStateStatusCodeRefreshFunc(ctx context.Context, client eve return res, strconv.Itoa(res.StatusCode), nil } } + +func expandEventHubNamespaceNetworkRuleset(input []interface{}) *eventhub.NetworkRuleSetProperties { + if len(input) == 0 { + return nil + } + + block := input[0].(map[string]interface{}) + + ruleset := eventhub.NetworkRuleSetProperties{ + DefaultAction: eventhub.DefaultAction(block["default_action"].(string)), + } + + if v, ok := block["virtual_network_rule"].([]interface{}); ok { + if len(v) > 0 { + var rules []eventhub.NWRuleSetVirtualNetworkRules + for _, r := range v { + rblock := r.(map[string]interface{}) + rules = append(rules, eventhub.NWRuleSetVirtualNetworkRules{ + Subnet: &eventhub.Subnet{ + ID: utils.String(rblock["subnet_id"].(string)), + }, + IgnoreMissingVnetServiceEndpoint: utils.Bool(rblock["ignore_missing_virtual_network_service_endpoint"].(bool)), + }) + } + + ruleset.VirtualNetworkRules = &rules + } + } + + if v, ok := block["ip_rule"].([]interface{}); ok { + if len(v) > 0 { + var rules []eventhub.NWRuleSetIPRules + for _, r := range v { + rblock := r.(map[string]interface{}) + rules = append(rules, eventhub.NWRuleSetIPRules{ + IPMask: utils.String(rblock["ip_mask"].(string)), + Action: eventhub.NetworkRuleIPAction(rblock["action"].(string)), + }) + } + + ruleset.IPRules = &rules + } + } + + return &ruleset +} + +func flattenEventHubNamespaceNetworkRuleset(ruleset eventhub.NetworkRuleSet) []interface{} { + if ruleset.NetworkRuleSetProperties == nil { + return nil + } + + vnetBlocks := make([]interface{}, 0) + if vnetRules := ruleset.NetworkRuleSetProperties.VirtualNetworkRules; vnetRules != nil { + for _, vnetRule := range *vnetRules { + block := make(map[string]interface{}) + + if s := vnetRule.Subnet; s != nil { + if v := s.ID; v != nil { + block["subnet_id"] = *v + } + } + + if v := vnetRule.IgnoreMissingVnetServiceEndpoint; v != nil { + block["ignore_missing_virtual_network_service_endpoint"] = *v + } + + vnetBlocks = append(vnetBlocks, block) + } + } + ipBlocks := make([]interface{}, 0) + if ipRules := ruleset.NetworkRuleSetProperties.IPRules; ipRules != nil { + for _, ipRule := range *ipRules { + block := make(map[string]interface{}) + + block["action"] = string(ipRule.Action) + + if v := ipRule.IPMask; v != nil { + block["ip_mask"] = *v + } + + ipBlocks = append(ipBlocks, block) + } + } + + return []interface{}{map[string]interface{}{ + "default_action": string(ruleset.DefaultAction), + "virtual_network_rule": vnetBlocks, + "ip_rule": ipBlocks, + }} +} diff --git a/azurerm/resource_arm_eventhub_namespace_authorization_rule.go b/azurerm/resource_arm_eventhub_namespace_authorization_rule.go index bdbf133ac793..0df33ef1c6d0 100644 --- a/azurerm/resource_arm_eventhub_namespace_authorization_rule.go +++ b/azurerm/resource_arm_eventhub_namespace_authorization_rule.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -57,7 +58,7 @@ func resourceArmEventHubNamespaceAuthorizationRuleCreateUpdate(d *schema.Resourc namespaceName := d.Get("namespace_name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -99,7 +100,7 @@ func resourceArmEventHubNamespaceAuthorizationRuleRead(d *schema.ResourceData, m client := meta.(*ArmClient).eventhub.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -145,7 +146,7 @@ func resourceArmEventHubNamespaceAuthorizationRuleDelete(d *schema.ResourceData, eventhubClient := meta.(*ArmClient).eventhub.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_eventhub_namespace_authorization_rule_test.go b/azurerm/resource_arm_eventhub_namespace_authorization_rule_test.go index 2d9ea2c23b05..140c6ab0588b 100644 --- a/azurerm/resource_arm_eventhub_namespace_authorization_rule_test.go +++ b/azurerm/resource_arm_eventhub_namespace_authorization_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -60,7 +61,7 @@ func testAccAzureRMEventHubNamespaceAuthorizationRule(t *testing.T, listen, send } func TestAccAzureRMEventHubNamespaceAuthorizationRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -131,7 +132,7 @@ func TestAccAzureRMEventHubNamespaceAuthorizationRule_rightsUpdate(t *testing.T) } func testCheckAzureRMEventHubNamespaceAuthorizationRuleDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).eventhub.NamespacesClient + client := testAccProvider.Meta().(*ArmClient).eventhub.NamespacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -143,7 +144,7 @@ func testCheckAzureRMEventHubNamespaceAuthorizationRuleDestroy(s *terraform.Stat namespaceName := rs.Primary.Attributes["namespace_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) + resp, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(resp.Response) { return err @@ -156,6 +157,9 @@ func testCheckAzureRMEventHubNamespaceAuthorizationRuleDestroy(s *terraform.Stat func testCheckAzureRMEventHubNamespaceAuthorizationRuleExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).eventhub.NamespacesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + // Ensure we have enough information in state to look up in API rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -169,9 +173,7 @@ func testCheckAzureRMEventHubNamespaceAuthorizationRuleExists(resourceName strin return fmt.Errorf("Bad: no resource group found in state for Event Hub: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).eventhub.NamespacesClient - ctx := testAccProvider.Meta().(*ArmClient).StopContext - resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) + resp, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { return fmt.Errorf("Bad: Event Hub Namespace Authorization Rule %q (namespace %q / resource group: %q) does not exist", name, namespaceName, resourceGroup) diff --git a/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go new file mode 100644 index 000000000000..360966478b78 --- /dev/null +++ b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config.go @@ -0,0 +1,299 @@ +package azurerm + +import ( + "context" + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmEventHubNamespaceDisasterRecoveryConfig() *schema.Resource { + return &schema.Resource{ + Create: resourceArmEventHubNamespaceDisasterRecoveryConfigCreate, + Read: resourceArmEventHubNamespaceDisasterRecoveryConfigRead, + Update: resourceArmEventHubNamespaceDisasterRecoveryConfigUpdate, + Delete: resourceArmEventHubNamespaceDisasterRecoveryConfigDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateEventHubAuthorizationRuleName(), + }, + + "namespace_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateEventHubNamespaceName(), + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "partner_namespace_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceIDOrEmpty, + }, + + "alternate_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: azure.ValidateEventHubNamespaceName(), + }, + }, + } +} + +func resourceArmEventHubNamespaceDisasterRecoveryConfigCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM EventHub Namespace Disaster Recovery Configs creation.") + + name := d.Get("name").(string) + namespaceName := d.Get("namespace_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_eventhub_namespace_disaster_recovery_config", *existing.ID) + } + } + + parameters := eventhub.ArmDisasterRecovery{ + ArmDisasterRecoveryProperties: &eventhub.ArmDisasterRecoveryProperties{ + PartnerNamespace: utils.String(d.Get("partner_namespace_id").(string)), + }, + } + + if v, ok := d.GetOk("alternate_name"); ok { + parameters.ArmDisasterRecoveryProperties.AlternateName = utils.String(v.(string)) + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, namespaceName, name, parameters); err != nil { + return fmt.Errorf("Error creating/updating EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + return fmt.Errorf("Error waiting for replication to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + return fmt.Errorf("Error reading EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + if read.ID == nil { + return fmt.Errorf("Got nil ID for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q)", name, namespaceName, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmEventHubNamespaceDisasterRecoveryConfigRead(d, meta) +} + +func resourceArmEventHubNamespaceDisasterRecoveryConfigUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Path["disasterRecoveryConfigs"] + resourceGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + + if d.HasChange("partner_namespace_id") { + // break pairing + breakPair, err := client.BreakPairing(ctx, resourceGroup, namespaceName, name) + if breakPair.StatusCode != http.StatusOK { + return fmt.Errorf("Error issuing break pairing request for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + return fmt.Errorf("Error waiting for break pairing request to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + } + + parameters := eventhub.ArmDisasterRecovery{ + ArmDisasterRecoveryProperties: &eventhub.ArmDisasterRecoveryProperties{ + PartnerNamespace: utils.String(d.Get("partner_namespace_id").(string)), + }, + } + + if v, ok := d.GetOk("alternate_name"); ok { + parameters.ArmDisasterRecoveryProperties.AlternateName = utils.String(v.(string)) + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, namespaceName, name, parameters); err != nil { + return fmt.Errorf("Error creating/updating EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + return fmt.Errorf("Error waiting for replication to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + return resourceArmEventHubNamespaceDisasterRecoveryConfigRead(d, meta) +} + +func resourceArmEventHubNamespaceDisasterRecoveryConfigRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Path["disasterRecoveryConfigs"] + resourceGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + + resp, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Azure EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("namespace_name", namespaceName) + d.Set("resource_group_name", resourceGroup) + + if properties := resp.ArmDisasterRecoveryProperties; properties != nil { + d.Set("partner_namespace_id", properties.PartnerNamespace) + d.Set("alternate_name", properties.AlternateName) + } + + return nil +} + +func resourceArmEventHubNamespaceDisasterRecoveryConfigDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Path["disasterRecoveryConfigs"] + resourceGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + + breakPair, err := client.BreakPairing(ctx, resourceGroup, namespaceName, name) + if err != nil { + return fmt.Errorf("Error issuing break pairing request for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + if breakPair.StatusCode != http.StatusOK { + return fmt.Errorf("Error breaking pairing for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + if err := resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx, client, resourceGroup, namespaceName, name); err != nil { + return fmt.Errorf("Error waiting for break pairing request to complete for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + if _, err := client.Delete(ctx, resourceGroup, namespaceName, name); err != nil { + return fmt.Errorf("Error issuing delete request for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %s", name, namespaceName, resourceGroup, err) + } + + // no future for deletion so wait for it to vanish + deleteWait := &resource.StateChangeConf{ + Pending: []string{"200"}, + Target: []string{"404"}, + Timeout: 30 * time.Minute, + MinTimeout: 30 * time.Second, + Refresh: func() (interface{}, string, error) { + resp, err := client.Get(ctx, resourceGroup, namespaceName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return resp, strconv.Itoa(resp.StatusCode), nil + } + return nil, "nil", fmt.Errorf("Error polling for the status of the EventHub Namespace Disaster Recovery Configs %q deletion (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + return resp, strconv.Itoa(resp.StatusCode), nil + }, + } + if _, err := deleteWait.WaitForState(); err != nil { + return fmt.Errorf("Error waiting the deletion of EventHub Namespace Disaster Recovery Configs %q deletion (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + // it can take some time for the name to become available again + // this is mainly here to enable updating the resource in place + nameFreeWait := &resource.StateChangeConf{ + Pending: []string{"NameInUse"}, + Target: []string{"None"}, + Timeout: 30 * time.Minute, + MinTimeout: 30 * time.Second, + Refresh: func() (interface{}, string, error) { + resp, err := client.CheckNameAvailability(ctx, resourceGroup, namespaceName, eventhub.CheckNameAvailabilityParameter{Name: utils.String(name)}) + if err != nil { + return resp, "Error", fmt.Errorf("Error checking if the EventHub Namespace Disaster Recovery Configs %q name has been freed (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + return resp, string(resp.Reason), nil + }, + } + if _, err := nameFreeWait.WaitForState(); err != nil { + return fmt.Errorf("Error waiting the the EventHub Namespace Disaster Recovery Configs %q name to be available (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + return nil +} + +func resourceArmEventHubNamespaceDisasterRecoveryConfigWaitForState(ctx context.Context, client *eventhub.DisasterRecoveryConfigsClient, resourceGroup, namespaceName, name string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{string(eventhub.Accepted)}, + Target: []string{string(eventhub.Succeeded)}, + // Delay: 15 * time.Second, + Timeout: 30 * time.Minute, + MinTimeout: 30 * time.Second, + Refresh: func() (interface{}, string, error) { + read, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + return nil, "error", fmt.Errorf("Wait read EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): %v", name, namespaceName, resourceGroup, err) + } + + if props := read.ArmDisasterRecoveryProperties; props != nil { + if props.ProvisioningState == eventhub.Failed { + return read, "failed", fmt.Errorf("Replication for EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q) failed!", name, namespaceName, resourceGroup) + } + return read, string(props.ProvisioningState), nil + } + + return read, "nil", fmt.Errorf("Waiting for replication error EventHub Namespace Disaster Recovery Configs %q (Namespace %q / Resource Group %q): provisioning state is nil", name, namespaceName, resourceGroup) + }, + } + + _, err := stateConf.WaitForState() + return err +} diff --git a/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config_test.go b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config_test.go new file mode 100644 index 000000000000..49984c866ada --- /dev/null +++ b/azurerm/resource_arm_eventhub_namespace_disaster_recovery_config_test.go @@ -0,0 +1,279 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMEventHubNamespaceDisasterRecoveryConfig_basic(t *testing.T) { + resourceName := "azurerm_eventhub_namespace_disaster_recovery_config.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMEventHubNamespaceDisasterRecoveryConfig_complete(t *testing.T) { + resourceName := "azurerm_eventhub_namespace_disaster_recovery_config.test" + ri := tf.AccRandTimeInt() + + // skipping due to there being no way to delete a DRC once an alternate name has been set + // sdk bug: https://github.com/Azure/azure-sdk-for-go/issues/5893 + t.Skip() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_complete(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMEventHubNamespaceDisasterRecoveryConfig_update(t *testing.T) { + resourceName := "azurerm_eventhub_namespace_disaster_recovery_config.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigExists(resourceName), + ), + }, + { + Config: testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_updated(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_updated_removed(ri, testLocation(), testAltLocation()), + }, + }, + }) +} + +func testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_eventhub_namespace_disaster_recovery_config" { + continue + } + + name := rs.Primary.Attributes["name"] + namespaceName := rs.Primary.Attributes["namespace_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: EventHub Namespace Disaster Recovery Configs %q (namespace %q / resource group: %q): %+v", name, namespaceName, resourceGroup, err) + } + } + } + + return nil +} + +func testCheckAzureRMEventHubNamespaceDisasterRecoveryConfigExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).eventhub.DisasterRecoveryConfigsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + // 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"] + namespaceName := rs.Primary.Attributes["namespace_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, namespaceName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: EventHub Namespace Disaster Recovery Configs %q (namespace %q / resource group: %q) does not exist", name, namespaceName, resourceGroup) + } + + return fmt.Errorf("Bad: EventHub Namespace Disaster Recovery Configs %q (namespace %q / resource group: %q): %+v", name, namespaceName, resourceGroup, err) + } + + return nil + } +} + +func testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_basic(rInt int, location string, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_eventhub_namespace" "testa" { + name = "acctest-EHN-%[1]d-a" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testb" { + name = "acctest-EHN-%[1]d-b" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace_disaster_recovery_config" "test" { + name = "acctest-EHN-DRC-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + namespace_name = "${azurerm_eventhub_namespace.testa.name}" + partner_namespace_id = "${azurerm_eventhub_namespace.testb.id}" +} + +`, rInt, location, altlocation) +} + +func testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_complete(rInt int, location string, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_eventhub_namespace" "testa" { + name = "acctest-EHN-%[1]d-a" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testb" { + name = "acctest-EHN-%[1]d-b" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace_disaster_recovery_config" "test" { + name = "${azurerm_eventhub_namespace.testa.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + namespace_name = "${azurerm_eventhub_namespace.testa.name}" + partner_namespace_id = "${azurerm_eventhub_namespace.testb.id}" + alternate_name = "acctest-EHN-DRC-%[1]d-alt" +} + +`, rInt, location, altlocation) +} + +func testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_updated(rInt int, location string, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_eventhub_namespace" "testa" { + name = "acctest-EHN-%[1]d-a" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testb" { + name = "acctest-EHN-%[1]d-b" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testc" { + name = "acctest-EHN-%[1]d-c" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace_disaster_recovery_config" "test" { + name = "acctest-EHN-DRC-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + namespace_name = "${azurerm_eventhub_namespace.testa.name}" + partner_namespace_id = "${azurerm_eventhub_namespace.testc.id}" +} + +`, rInt, location, altlocation) +} + +func testAccAzureRMEventHubNamespaceDisasterRecoveryConfig_updated_removed(rInt int, location string, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_eventhub_namespace" "testa" { + name = "acctest-EHN-%[1]d-a" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testb" { + name = "acctest-EHN-%[1]d-b" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_eventhub_namespace" "testc" { + name = "acctest-EHN-%[1]d-c" + location = "%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} +`, rInt, location, altlocation) +} diff --git a/azurerm/resource_arm_eventhub_namespace_test.go b/azurerm/resource_arm_eventhub_namespace_test.go index 1f20d2097215..4b1564eecce4 100644 --- a/azurerm/resource_arm_eventhub_namespace_test.go +++ b/azurerm/resource_arm_eventhub_namespace_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMEventHubNamespace_basic(t *testing.T) { } func TestAccAzureRMEventHubNamespace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -114,6 +115,54 @@ func TestAccAzureRMEventHubNamespace_standard(t *testing.T) { }) } +func TestAccAzureRMEventHubNamespace_networkrule_iprule(t *testing.T) { + resourceName := "azurerm_eventhub_namespace.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventHubNamespace_networkrule_iprule(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMEventHubNamespace_networkrule_vnet(t *testing.T) { + resourceName := "azurerm_eventhub_namespace.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventHubNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventHubNamespace_networkrule_vnet(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventHubNamespaceExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMEventHubNamespace_readDefaultKeys(t *testing.T) { resourceName := "azurerm_eventhub_namespace.test" ri := tf.AccRandTimeInt() @@ -162,7 +211,6 @@ func TestAccAzureRMEventHubNamespace_maximumThroughputUnits(t *testing.T) { } func TestAccAzureRMEventHubNamespace_NonStandardCasing(t *testing.T) { - ri := tf.AccRandTimeInt() config := testAccAzureRMEventHubNamespaceNonStandardCasing(ri, testLocation()) @@ -464,6 +512,70 @@ resource "azurerm_eventhub_namespace" "test" { `, rInt, location, rInt) } +func testAccAzureRMEventHubNamespace_networkrule_iprule(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + capacity = "2" + + network_rulesets { + default_action = "Deny" + ip_rule { + ip_mask = "10.0.0.0/16" + } + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMEventHubNamespace_networkrule_vnet(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_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + capacity = "2" + + network_rulesets { + default_action = "Deny" + virtual_network_rule { + subnet_id = "${azurerm_subnet.test.id}" + + ignore_missing_virtual_network_service_endpoint = true + } + } +} +`, rInt, location) +} + func testAccAzureRMEventHubNamespaceNonStandardCasing(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_eventhub_test.go b/azurerm/resource_arm_eventhub_test.go index 7d21866e5d38..230a58dd1896 100644 --- a/azurerm/resource_arm_eventhub_test.go +++ b/azurerm/resource_arm_eventhub_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMEventHubPartitionCount_validation(t *testing.T) { @@ -219,7 +220,7 @@ func TestAccAzureRMEventHub_basicOnePartition(t *testing.T) { } func TestAccAzureRMEventHub_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_express_route_circuit.go b/azurerm/resource_arm_express_route_circuit.go index 6a92f59a394c..e2600184f58d 100644 --- a/azurerm/resource_arm_express_route_circuit.go +++ b/azurerm/resource_arm_express_route_circuit.go @@ -4,11 +4,15 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -39,14 +43,14 @@ func resourceArmExpressRouteCircuit() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "peering_location": { Type: schema.TypeString, Required: true, ForceNew: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "bandwidth_in_mbps": { @@ -67,7 +71,7 @@ func resourceArmExpressRouteCircuit() *schema.Resource { string(network.ExpressRouteCircuitSkuTierStandard), string(network.ExpressRouteCircuitSkuTierPremium), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "family": { @@ -77,7 +81,7 @@ func resourceArmExpressRouteCircuit() *schema.Resource { string(network.MeteredData), string(network.UnlimitedData), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, }, @@ -100,13 +104,13 @@ func resourceArmExpressRouteCircuit() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmExpressRouteCircuitCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRouteCircuitClient + client := meta.(*ArmClient).network.ExpressRouteCircuitsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ARM ExpressRoute Circuit creation.") @@ -114,10 +118,10 @@ func resourceArmExpressRouteCircuitCreateUpdate(d *schema.ResourceData, meta int name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - azureRMLockByName(name, expressRouteCircuitResourceName) - defer azureRMUnlockByName(name, expressRouteCircuitResourceName) + locks.ByName(name, expressRouteCircuitResourceName) + defer locks.UnlockByName(name, expressRouteCircuitResourceName) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -136,8 +140,8 @@ func resourceArmExpressRouteCircuitCreateUpdate(d *schema.ResourceData, meta int bandwidthInMbps := int32(d.Get("bandwidth_in_mbps").(int)) sku := expandExpressRouteCircuitSku(d) allowRdfeOps := d.Get("allow_classic_operations").(bool) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) // There is the potential for the express route circuit to become out of sync when the service provider updates // the express route circuit. We'll get and update the resource in place as per https://aka.ms/erRefresh @@ -209,15 +213,26 @@ func resourceArmExpressRouteCircuitCreateUpdate(d *schema.ResourceData, meta int } func resourceArmExpressRouteCircuitRead(d *schema.ResourceData, meta interface{}) error { - resp, resourceGroup, err := retrieveErcByResourceId(d.Id(), meta) + ercClient := meta.(*ArmClient).network.ExpressRouteCircuitsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { - return err + return fmt.Errorf("Error Parsing Azure Resource ID -: %+v", err) } - if resp == nil { - log.Printf("[INFO] Express Route Circuit %q not found. Removing from state", d.Get("name").(string)) - d.SetId("") - return nil + resourceGroup := id.ResourceGroup + name := id.Path["expressRouteCircuits"] + + resp, err := ercClient.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Express Route Circuit %q (Resource Group %q) was not found - removing from state", name, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Express Route Circuit %q (Resource Group %q): %+v", name, resourceGroup, err) } d.Set("name", resp.Name) @@ -243,22 +258,23 @@ func resourceArmExpressRouteCircuitRead(d *schema.ResourceData, meta interface{} d.Set("service_key", resp.ServiceKey) d.Set("allow_classic_operations", resp.AllowClassicOperations) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmExpressRouteCircuitDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRouteCircuitClient + client := meta.(*ArmClient).network.ExpressRouteCircuitsClient ctx := meta.(*ArmClient).StopContext - resourceGroup, name, err := extractResourceGroupAndErcName(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { - return fmt.Errorf("Error Parsing Azure Resource ID: %+v", err) + return fmt.Errorf("Error Parsing Azure Resource ID -: %+v", err) } - azureRMLockByName(name, expressRouteCircuitResourceName) - defer azureRMUnlockByName(name, expressRouteCircuitResourceName) + resourceGroup := id.ResourceGroup + name := id.Path["expressRouteCircuits"] + + locks.ByName(name, expressRouteCircuitResourceName) + defer locks.UnlockByName(name, expressRouteCircuitResourceName) future, err := client.Delete(ctx, resourceGroup, name) if err != nil { diff --git a/azurerm/resource_arm_express_route_circuit_authorization.go b/azurerm/resource_arm_express_route_circuit_authorization.go index f2e310cb4e48..9367aea0bdf5 100644 --- a/azurerm/resource_arm_express_route_circuit_authorization.go +++ b/azurerm/resource_arm_express_route_circuit_authorization.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -50,17 +52,17 @@ func resourceArmExpressRouteCircuitAuthorization() *schema.Resource { } func resourceArmExpressRouteCircuitAuthorizationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRouteAuthsClient + client := meta.(*ArmClient).network.ExpressRouteAuthsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) circuitName := d.Get("express_route_circuit_name").(string) - azureRMLockByName(circuitName, expressRouteCircuitResourceName) - defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + locks.ByName(circuitName, expressRouteCircuitResourceName) + defer locks.UnlockByName(circuitName, expressRouteCircuitResourceName) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, circuitName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -97,10 +99,10 @@ func resourceArmExpressRouteCircuitAuthorizationCreate(d *schema.ResourceData, m } func resourceArmExpressRouteCircuitAuthorizationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRouteAuthsClient + client := meta.(*ArmClient).network.ExpressRouteAuthsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -131,10 +133,10 @@ func resourceArmExpressRouteCircuitAuthorizationRead(d *schema.ResourceData, met } func resourceArmExpressRouteCircuitAuthorizationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRouteAuthsClient + client := meta.(*ArmClient).network.ExpressRouteAuthsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -143,8 +145,8 @@ func resourceArmExpressRouteCircuitAuthorizationDelete(d *schema.ResourceData, m circuitName := id.Path["expressRouteCircuits"] name := id.Path["authorizations"] - azureRMLockByName(circuitName, expressRouteCircuitResourceName) - defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + locks.ByName(circuitName, expressRouteCircuitResourceName) + defer locks.UnlockByName(circuitName, expressRouteCircuitResourceName) future, err := client.Delete(ctx, resourceGroup, circuitName, name) if err != nil { diff --git a/azurerm/resource_arm_express_route_circuit_authorization_test.go b/azurerm/resource_arm_express_route_circuit_authorization_test.go index 2c5f24dac49f..1af4768e440c 100644 --- a/azurerm/resource_arm_express_route_circuit_authorization_test.go +++ b/azurerm/resource_arm_express_route_circuit_authorization_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func testAccAzureRMExpressRouteCircuitAuthorization_basic(t *testing.T) { } func testAccAzureRMExpressRouteCircuitAuthorization_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -104,7 +105,7 @@ func testCheckAzureRMExpressRouteCircuitAuthorizationExists(resourceName string) return fmt.Errorf("Bad: no resource group found in state for Express Route Circuit Authorization: %s", expressRouteCircuitName) } - client := testAccProvider.Meta().(*ArmClient).expressRouteAuthsClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRouteAuthsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, expressRouteCircuitName, authorizationName) @@ -121,7 +122,7 @@ func testCheckAzureRMExpressRouteCircuitAuthorizationExists(resourceName string) } func testCheckAzureRMExpressRouteCircuitAuthorizationDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).expressRouteAuthsClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRouteAuthsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_express_route_circuit_peering.go b/azurerm/resource_arm_express_route_circuit_peering.go index da4923fd16e3..e5773e8b44b1 100644 --- a/azurerm/resource_arm_express_route_circuit_peering.go +++ b/azurerm/resource_arm_express_route_circuit_peering.go @@ -5,12 +5,14 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -106,7 +108,7 @@ func resourceArmExpressRouteCircuitPeering() *schema.Resource { } func resourceArmExpressRouteCircuitPeeringCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRoutePeeringsClient + client := meta.(*ArmClient).network.ExpressRoutePeeringsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Express Route Peering create/update.") @@ -115,10 +117,10 @@ func resourceArmExpressRouteCircuitPeeringCreateUpdate(d *schema.ResourceData, m circuitName := d.Get("express_route_circuit_name").(string) resourceGroup := d.Get("resource_group_name").(string) - azureRMLockByName(circuitName, expressRouteCircuitResourceName) - defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + locks.ByName(circuitName, expressRouteCircuitResourceName) + defer locks.UnlockByName(circuitName, expressRouteCircuitResourceName) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, circuitName, peeringType) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -180,10 +182,10 @@ func resourceArmExpressRouteCircuitPeeringCreateUpdate(d *schema.ResourceData, m } func resourceArmExpressRouteCircuitPeeringRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRoutePeeringsClient + client := meta.(*ArmClient).network.ExpressRoutePeeringsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -223,10 +225,10 @@ func resourceArmExpressRouteCircuitPeeringRead(d *schema.ResourceData, meta inte } func resourceArmExpressRouteCircuitPeeringDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).expressRoutePeeringsClient + client := meta.(*ArmClient).network.ExpressRoutePeeringsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -235,8 +237,8 @@ func resourceArmExpressRouteCircuitPeeringDelete(d *schema.ResourceData, meta in circuitName := id.Path["expressRouteCircuits"] peeringType := id.Path["peerings"] - azureRMLockByName(circuitName, expressRouteCircuitResourceName) - defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + locks.ByName(circuitName, expressRouteCircuitResourceName) + defer locks.UnlockByName(circuitName, expressRouteCircuitResourceName) future, err := client.Delete(ctx, resourceGroup, circuitName, peeringType) if err != nil { diff --git a/azurerm/resource_arm_express_route_circuit_peering_test.go b/azurerm/resource_arm_express_route_circuit_peering_test.go index b18e00cee9db..73648259cff7 100644 --- a/azurerm/resource_arm_express_route_circuit_peering_test.go +++ b/azurerm/resource_arm_express_route_circuit_peering_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,7 +41,7 @@ func testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) } func testAccAzureRMExpressRouteCircuitPeering_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -146,7 +147,7 @@ func testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName string) resou return fmt.Errorf("Bad: no resource group found in state for Express Route Circuit Peering: %s", peeringType) } - client := testAccProvider.Meta().(*ArmClient).expressRoutePeeringsClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRoutePeeringsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, circuitName, peeringType) @@ -163,7 +164,7 @@ func testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName string) resou } func testCheckAzureRMExpressRouteCircuitPeeringDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).expressRoutePeeringsClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRoutePeeringsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_express_route_circuit_test.go b/azurerm/resource_arm_express_route_circuit_test.go index bc235d05e777..804405816a7a 100644 --- a/azurerm/resource_arm_express_route_circuit_test.go +++ b/azurerm/resource_arm_express_route_circuit_test.go @@ -5,10 +5,11 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -81,7 +82,7 @@ func testAccAzureRMExpressRouteCircuit_basicMetered(t *testing.T) { } func testAccAzureRMExpressRouteCircuit_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -287,7 +288,7 @@ func testCheckAzureRMExpressRouteCircuitExists(resourceName string, erc *network return fmt.Errorf("Bad: no resource group found in state for Express Route Circuit: %s", expressRouteCircuitName) } - client := testAccProvider.Meta().(*ArmClient).expressRouteCircuitClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRouteCircuitsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, expressRouteCircuitName) @@ -306,7 +307,7 @@ func testCheckAzureRMExpressRouteCircuitExists(resourceName string, erc *network } func testCheckAzureRMExpressRouteCircuitDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).expressRouteCircuitClient + client := testAccProvider.Meta().(*ArmClient).network.ExpressRouteCircuitsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_firewall.go b/azurerm/resource_arm_firewall.go index f20183a07dd3..43688b244677 100644 --- a/azurerm/resource_arm_firewall.go +++ b/azurerm/resource_arm_firewall.go @@ -5,11 +5,14 @@ import ( "log" "regexp" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -77,13 +80,13 @@ func resourceArmFirewall() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmFirewallCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Azure Firewall creation") @@ -91,7 +94,7 @@ func resourceArmFirewallCreateUpdate(d *schema.ResourceData, meta interface{}) e name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -105,24 +108,24 @@ func resourceArmFirewallCreateUpdate(d *schema.ResourceData, meta interface{}) e } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := 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) + locks.ByName(name, azureFirewallResourceName) + defer locks.UnlockByName(name, azureFirewallResourceName) - azureRMLockMultipleByName(subnetToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(subnetToLock, subnetResourceName) + locks.MultipleByName(subnetToLock, subnetResourceName) + defer locks.UnlockMultipleByName(subnetToLock, subnetResourceName) - azureRMLockMultipleByName(vnetToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnetToLock, virtualNetworkResourceName) + locks.MultipleByName(vnetToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnetToLock, virtualNetworkResourceName) parameters := network.AzureFirewall{ Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), AzureFirewallPropertiesFormat: &network.AzureFirewallPropertiesFormat{ IPConfigurations: ipConfigs, }, @@ -169,10 +172,10 @@ func resourceArmFirewallCreateUpdate(d *schema.ResourceData, meta interface{}) e } func resourceArmFirewallRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -203,16 +206,14 @@ func resourceArmFirewallRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, read.Tags) - - return nil + return tags.FlattenAndSet(d, read.Tags) } func resourceArmFirewallDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -239,7 +240,7 @@ func resourceArmFirewallDelete(d *schema.ResourceData, meta interface{}) error { continue } - parsedSubnetId, err2 := parseAzureResourceID(*config.Subnet.ID) + parsedSubnetId, err2 := azure.ParseAzureResourceID(*config.Subnet.ID) if err2 != nil { return err2 } @@ -257,14 +258,14 @@ func resourceArmFirewallDelete(d *schema.ResourceData, meta interface{}) error { } } - azureRMLockByName(name, azureFirewallResourceName) - defer azureRMUnlockByName(name, azureFirewallResourceName) + locks.ByName(name, azureFirewallResourceName) + defer locks.UnlockByName(name, azureFirewallResourceName) - azureRMLockMultipleByName(&subnetNamesToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(&subnetNamesToLock, subnetResourceName) + locks.MultipleByName(&subnetNamesToLock, subnetResourceName) + defer locks.UnlockMultipleByName(&subnetNamesToLock, subnetResourceName) - azureRMLockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + locks.MultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -298,7 +299,7 @@ func expandArmFirewallIPConfigurations(d *schema.ResourceData) (*[]network.Azure return nil, nil, nil, fmt.Errorf("one of `ip_configuration.0.internal_public_ip_address_id` or `ip_configuration.0.public_ip_address_id` must be set") } - subnetID, err := parseAzureResourceID(subnetId) + subnetID, err := azure.ParseAzureResourceID(subnetId) if err != nil { return nil, nil, nil, err } @@ -382,7 +383,7 @@ func validateAzureFirewallName(v interface{}, k string) (warnings []string, erro } func validateAzureFirewallSubnetName(v interface{}, k string) (warnings []string, errors []error) { - parsed, err := parseAzureResourceID(v.(string)) + parsed, err := azure.ParseAzureResourceID(v.(string)) if err != nil { errors = append(errors, fmt.Errorf("Error parsing Azure Resource ID %q", v.(string))) return warnings, errors @@ -390,7 +391,6 @@ func validateAzureFirewallSubnetName(v interface{}, k string) (warnings []string subnetName := parsed.Path["subnets"] if subnetName != "AzureFirewallSubnet" { errors = append(errors, fmt.Errorf("The name of the Subnet for %q must be exactly 'AzureFirewallSubnet' to be used for the Azure Firewall resource", k)) - } return warnings, errors diff --git a/azurerm/resource_arm_firewall_application_rule_collection.go b/azurerm/resource_arm_firewall_application_rule_collection.go index 4bf278a1996d..ca953a75817c 100644 --- a/azurerm/resource_arm_firewall_application_rule_collection.go +++ b/azurerm/resource_arm_firewall_application_rule_collection.go @@ -7,8 +7,10 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/set" @@ -120,7 +122,7 @@ func resourceArmFirewallApplicationRuleCollection() *schema.Resource { } func resourceArmFirewallApplicationRuleCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -131,8 +133,8 @@ func resourceArmFirewallApplicationRuleCollectionCreateUpdate(d *schema.Resource return fmt.Errorf("Error expanding Firewall Application Rules: %+v", err) } - azureRMLockByName(firewallName, azureFirewallResourceName) - defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -182,7 +184,7 @@ func resourceArmFirewallApplicationRuleCollectionCreateUpdate(d *schema.Resource ruleCollections[index] = newRuleCollection } else { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { if index != -1 { return tf.ImportAsExistsError("azurerm_firewall_application_rule_collection", id) } @@ -232,10 +234,10 @@ func resourceArmFirewallApplicationRuleCollectionCreateUpdate(d *schema.Resource } func resourceArmFirewallApplicationRuleCollectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -304,10 +306,10 @@ func resourceArmFirewallApplicationRuleCollectionRead(d *schema.ResourceData, me } func resourceArmFirewallApplicationRuleCollectionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -316,8 +318,8 @@ func resourceArmFirewallApplicationRuleCollectionDelete(d *schema.ResourceData, firewallName := id.Path["azureFirewalls"] name := id.Path["applicationRuleCollections"] - azureRMLockByName(firewallName, azureFirewallResourceName) - defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { diff --git a/azurerm/resource_arm_firewall_application_rule_collection_test.go b/azurerm/resource_arm_firewall_application_rule_collection_test.go index b243439b0329..4f3ccb80031f 100644 --- a/azurerm/resource_arm_firewall_application_rule_collection_test.go +++ b/azurerm/resource_arm_firewall_application_rule_collection_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -48,7 +49,7 @@ func TestAccAzureRMFirewallApplicationRuleCollection_basic(t *testing.T) { } func TestAccAzureRMFirewallApplicationRuleCollection_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -428,7 +429,7 @@ func testCheckAzureRMFirewallApplicationRuleCollectionExists(resourceName string firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -462,7 +463,7 @@ func testCheckAzureRMFirewallApplicationRuleCollectionDoesNotExist(resourceName firewallName := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -491,7 +492,7 @@ func testCheckAzureRMFirewallApplicationRuleCollectionDisappears(resourceName st firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -843,6 +844,7 @@ resource "azurerm_firewall_application_rule_collection" "test" { port = 9000 type = "Https" } + protocol { port = 9001 type = "Http" diff --git a/azurerm/resource_arm_firewall_nat_rule_collection.go b/azurerm/resource_arm_firewall_nat_rule_collection.go index bc5e27d9c445..a5dd31e211e8 100644 --- a/azurerm/resource_arm_firewall_nat_rule_collection.go +++ b/azurerm/resource_arm_firewall_nat_rule_collection.go @@ -4,13 +4,15 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/set" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -119,15 +121,15 @@ func resourceArmFirewallNatRuleCollection() *schema.Resource { } func resourceArmFirewallNatRuleCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.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) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -179,7 +181,7 @@ func resourceArmFirewallNatRuleCollectionCreateUpdate(d *schema.ResourceData, me ruleCollections[index] = newRuleCollection } else { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { if index != -1 { return tf.ImportAsExistsError("azurerm_firewall_nat_rule_collection", id) } @@ -229,10 +231,10 @@ func resourceArmFirewallNatRuleCollectionCreateUpdate(d *schema.ResourceData, me } func resourceArmFirewallNatRuleCollectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -301,10 +303,10 @@ func resourceArmFirewallNatRuleCollectionRead(d *schema.ResourceData, meta inter } func resourceArmFirewallNatRuleCollectionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -313,8 +315,8 @@ func resourceArmFirewallNatRuleCollectionDelete(d *schema.ResourceData, meta int firewallName := id.Path["azureFirewalls"] name := id.Path["natRuleCollections"] - azureRMLockByName(firewallName, azureFirewallResourceName) - defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { diff --git a/azurerm/resource_arm_firewall_nat_rule_collection_test.go b/azurerm/resource_arm_firewall_nat_rule_collection_test.go index 83f4a7287363..7a6ac2c285f6 100644 --- a/azurerm/resource_arm_firewall_nat_rule_collection_test.go +++ b/azurerm/resource_arm_firewall_nat_rule_collection_test.go @@ -4,10 +4,11 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMFirewallNatRuleCollection_basic(t *testing.T) { @@ -36,7 +37,7 @@ func TestAccAzureRMFirewallNatRuleCollection_basic(t *testing.T) { } func TestAccAzureRMFirewallNatRuleCollection_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -307,7 +308,7 @@ func testCheckAzureRMFirewallNatRuleCollectionExists(resourceName string) resour firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -341,7 +342,7 @@ func testCheckAzureRMFirewallNatRuleCollectionDoesNotExist(resourceName string, firewallName := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -370,7 +371,7 @@ func testCheckAzureRMFirewallNatRuleCollectionDisappears(resourceName string) re firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -429,10 +430,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "Any", - ] - - translated_port = 53 - translated_address = "8.8.8.8" + ] + + translated_port = 53 + translated_address = "8.8.8.8" } } `, template, rInt) @@ -467,10 +468,10 @@ resource "azurerm_firewall_nat_rule_collection" "import" { protocols = [ "Any", - ] - - translated_port = 53 - translated_address = "8.8.8.8" + ] + + translated_port = 53 + translated_address = "8.8.8.8" } } `, template) @@ -505,10 +506,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] + ] - translated_port = 53 - translated_address = "8.8.8.8" + translated_port = 53 + translated_address = "8.8.8.8" } } `, template, rInt) @@ -543,10 +544,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] - - translated_port = 53 - translated_address = "8.8.8.8" + ] + + translated_port = 53 + translated_address = "8.8.8.8" } } @@ -574,10 +575,10 @@ resource "azurerm_firewall_nat_rule_collection" "test_add" { protocols = [ "TCP", - ] - - translated_port = 8080 - translated_address = "8.8.4.4" + ] + + translated_port = 8080 + translated_address = "8.8.4.4" } } `, template, rInt, rInt) @@ -612,10 +613,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] - - translated_port = 53 - translated_address = "10.0.0.1" + ] + + translated_port = 53 + translated_address = "10.0.0.1" } } @@ -643,10 +644,10 @@ resource "azurerm_firewall_nat_rule_collection" "test_add" { protocols = [ "TCP", - ] - - translated_port = 8080 - translated_address = "10.0.0.1" + ] + + translated_port = 8080 + translated_address = "10.0.0.1" } } `, template, rInt, rInt) @@ -681,10 +682,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] - - translated_port = 53 - translated_address = "10.0.0.1" + ] + + translated_port = 53 + translated_address = "10.0.0.1" } rule { @@ -704,10 +705,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] - - translated_port = 8888 - translated_address = "192.168.0.1" + ] + + translated_port = 8888 + translated_address = "192.168.0.1" } } `, template, rInt) @@ -742,10 +743,10 @@ resource "azurerm_firewall_nat_rule_collection" "test" { protocols = [ "TCP", - ] - - translated_port = 53 - translated_address = "10.0.0.1" + ] + + translated_port = 53 + translated_address = "10.0.0.1" } } `, template, rInt) diff --git a/azurerm/resource_arm_firewall_network_rule_collection.go b/azurerm/resource_arm_firewall_network_rule_collection.go index d437237f3d63..175f7ddafd96 100644 --- a/azurerm/resource_arm_firewall_network_rule_collection.go +++ b/azurerm/resource_arm_firewall_network_rule_collection.go @@ -4,13 +4,15 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/set" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -111,15 +113,15 @@ func resourceArmFirewallNetworkRuleCollection() *schema.Resource { } func resourceArmFirewallNetworkRuleCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.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) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -171,7 +173,7 @@ func resourceArmFirewallNetworkRuleCollectionCreateUpdate(d *schema.ResourceData ruleCollections[index] = newRuleCollection } else { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { if index != -1 { return tf.ImportAsExistsError("azurerm_firewall_network_rule_collection", id) } @@ -222,10 +224,10 @@ func resourceArmFirewallNetworkRuleCollectionCreateUpdate(d *schema.ResourceData } func resourceArmFirewallNetworkRuleCollectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -294,10 +296,10 @@ func resourceArmFirewallNetworkRuleCollectionRead(d *schema.ResourceData, meta i } func resourceArmFirewallNetworkRuleCollectionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).azureFirewallsClient + client := meta.(*ArmClient).network.AzureFirewallsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -306,8 +308,8 @@ func resourceArmFirewallNetworkRuleCollectionDelete(d *schema.ResourceData, meta firewallName := id.Path["azureFirewalls"] name := id.Path["networkRuleCollections"] - azureRMLockByName(firewallName, azureFirewallResourceName) - defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + locks.ByName(firewallName, azureFirewallResourceName) + defer locks.UnlockByName(firewallName, azureFirewallResourceName) firewall, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { diff --git a/azurerm/resource_arm_firewall_network_rule_collection_test.go b/azurerm/resource_arm_firewall_network_rule_collection_test.go index 249732d65848..ca72db607934 100644 --- a/azurerm/resource_arm_firewall_network_rule_collection_test.go +++ b/azurerm/resource_arm_firewall_network_rule_collection_test.go @@ -4,10 +4,11 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMFirewallNetworkRuleCollection_basic(t *testing.T) { @@ -40,7 +41,7 @@ func TestAccAzureRMFirewallNetworkRuleCollection_basic(t *testing.T) { } func TestAccAzureRMFirewallNetworkRuleCollection_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -317,7 +318,7 @@ func testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName string) re firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -351,7 +352,7 @@ func testCheckAzureRMFirewallNetworkRuleCollectionDoesNotExist(resourceName stri firewallName := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { @@ -380,7 +381,7 @@ func testCheckAzureRMFirewallNetworkRuleCollectionDisappears(resourceName string firewallName := rs.Primary.Attributes["azure_firewall_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, firewallName) if err != nil { diff --git a/azurerm/resource_arm_firewall_test.go b/azurerm/resource_arm_firewall_test.go index 134cb607a1ec..67908dc910b0 100644 --- a/azurerm/resource_arm_firewall_test.go +++ b/azurerm/resource_arm_firewall_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -110,7 +111,7 @@ func TestAccAzureRMFirewall_basic(t *testing.T) { } func TestAccAzureRMFirewall_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -211,7 +212,7 @@ func testCheckAzureRMFirewallExists(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for Azure Firewall: %q", name) } - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -240,7 +241,7 @@ func testCheckAzureRMFirewallDisappears(resourceName string) resource.TestCheckF return fmt.Errorf("Bad: no resource group found in state for Azure Firewall: %q", name) } - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -255,7 +256,7 @@ func testCheckAzureRMFirewallDisappears(resourceName string) resource.TestCheckF } func testCheckAzureRMFirewallDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + client := testAccProvider.Meta().(*ArmClient).network.AzureFirewallsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_front_door.go b/azurerm/resource_arm_front_door.go new file mode 100644 index 000000000000..110709dc5262 --- /dev/null +++ b/azurerm/resource_arm_front_door.go @@ -0,0 +1,1455 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" + "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/tf" + afd "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmFrontDoor() *schema.Resource { + return &schema.Resource{ + Create: resourceArmFrontDoorCreateUpdate, + Read: resourceArmFrontDoorRead, + Update: resourceArmFrontDoorCreateUpdate, + Delete: resourceArmFrontDoorDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: afd.ValidateFrontDoorName, + }, + + "cname": { + Type: schema.TypeString, + Computed: true, + }, + + "friendly_name": { + Type: schema.TypeString, + Optional: true, + }, + + "load_balancer_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "enforce_backend_pools_certificate_name_check": { + Type: schema.TypeBool, + Required: true, + }, + + "location": azure.SchemaLocation(), + + // Product Backlog Item #: 4642226 Resource id should not be case sensitive + // + // Description: + // Resource Group currently is case sensitive in AFD RP, but it should not be. + // To make it case insentivie, we need to migrate and normalize the existing values in storage. + // Multiple steps are needed to perform this migration. + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "routing_rule": { + Type: schema.TypeList, + MaxItems: 100, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "accepted_protocols": { + Type: schema.TypeList, + Required: true, + MaxItems: 2, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.HTTP), + string(frontdoor.HTTPS), + }, false), + }, + }, + "patterns_to_match": { + Type: schema.TypeList, + Required: true, + MaxItems: 25, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "frontend_endpoints": { + Type: schema.TypeList, + Required: true, + MaxItems: 100, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "redirect_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_fragment": { + Type: schema.TypeString, + Optional: true, + }, + "custom_host": { + Type: schema.TypeString, + Required: true, + }, + "custom_path": { + Type: schema.TypeString, + Optional: true, + }, + "custom_query_string": { + Type: schema.TypeString, + Optional: true, + }, + "redirect_protocol": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.RedirectProtocolHTTPOnly), + string(frontdoor.RedirectProtocolHTTPSOnly), + string(frontdoor.RedirectProtocolMatchRequest), + }, false), + }, + "redirect_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Found), + string(frontdoor.Moved), + string(frontdoor.PermanentRedirect), + string(frontdoor.TemporaryRedirect), + }, false), + }, + }, + }, + }, + "forwarding_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend_pool_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "cache_use_dynamic_compression": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "cache_query_parameter_strip_directive": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.StripAll), + string(frontdoor.StripNone), + }, false), + Default: string(frontdoor.StripNone), + }, + "custom_forwarding_path": { + Type: schema.TypeString, + Optional: true, + }, + "forwarding_protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.HTTPOnly), + string(frontdoor.HTTPSOnly), + string(frontdoor.MatchRequest), + }, false), + Default: string(frontdoor.MatchRequest), + }, + }, + }, + }, + }, + }, + }, + + "backend_pool_load_balancing": { + Type: schema.TypeList, + MaxItems: 5000, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "sample_size": { + Type: schema.TypeInt, + Optional: true, + Default: 4, + }, + "successful_samples_required": { + Type: schema.TypeInt, + Optional: true, + Default: 2, + }, + "additional_latency_milliseconds": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + }, + }, + }, + }, + + "backend_pool_health_probe": { + Type: schema.TypeList, + MaxItems: 5000, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Default: "/", + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.HTTP), + string(frontdoor.HTTPS), + }, false), + Default: string(frontdoor.HTTP), + }, + "interval_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 120, + }, + }, + }, + }, + + "backend_pool": { + Type: schema.TypeList, + MaxItems: 50, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend": { + Type: schema.TypeList, + MaxItems: 100, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "address": { + Type: schema.TypeString, + Required: true, + }, + "http_port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "https_port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "weight": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 1000), + Default: 50, + }, + "priority": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 5), + Default: 1, + }, + "host_header": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "health_probe_name": { + Type: schema.TypeString, + Required: true, + }, + "load_balancing_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "frontend_endpoint": { + Type: schema.TypeList, + MaxItems: 100, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: afd.ValidateBackendPoolRoutingRuleName, + }, + "host_name": { + Type: schema.TypeString, + Required: true, + }, + "session_affinity_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "session_affinity_ttl_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + }, + "custom_https_provisioning_enabled": { + Type: schema.TypeBool, + Required: true, + }, + "web_application_firewall_policy_link_id": { + Type: schema.TypeString, + Optional: true, + }, + "custom_https_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_source": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.CertificateSourceAzureKeyVault), + string(frontdoor.CertificateSourceFrontDoor), + }, false), + Default: string(frontdoor.CertificateSourceFrontDoor), + }, + "provisioning_state": { + Type: schema.TypeString, + Computed: true, + }, + "provisioning_substate": { + Type: schema.TypeString, + Computed: true, + }, + // NOTE: None of these attributes are valid if + // certificate_source is set to FrontDoor + "azure_key_vault_certificate_secret_name": { + Type: schema.TypeString, + Optional: true, + }, + "azure_key_vault_certificate_secret_version": { + Type: schema.TypeString, + Optional: true, + }, + "azure_key_vault_certificate_vault_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + + CustomizeDiff: func(d *schema.ResourceDiff, v interface{}) error { + if err := afd.ValidateFrontdoorSettings(d); err != nil { + return fmt.Errorf("Error creating Front Door %q (Resource Group %q): %+v", d.Get("name").(string), d.Get("resource_group_name").(string), err) + } + + return nil + }, + } +} + +func resourceArmFrontDoorCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + subscriptionId := meta.(*ArmClient).subscriptionId + + if requireResourcesToBeImported { + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error checking for present of existing Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_front_door", *resp.ID) + } + } + + frontDoorPath := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.Network/Frontdoors/%s", subscriptionId, resourceGroup, name) + location := azure.NormalizeLocation(d.Get("location").(string)) + friendlyName := d.Get("friendly_name").(string) + routingRules := d.Get("routing_rule").([]interface{}) + loadBalancingSettings := d.Get("backend_pool_load_balancing").([]interface{}) + healthProbeSettings := d.Get("backend_pool_health_probe").([]interface{}) + backendPools := d.Get("backend_pool").([]interface{}) + frontendEndpoints := d.Get("frontend_endpoint").([]interface{}) + backendPoolsSettings := d.Get("enforce_backend_pools_certificate_name_check").(bool) + enabledState := d.Get("load_balancer_enabled").(bool) + tags := d.Get("tags").(map[string]interface{}) + + frontDoorParameters := frontdoor.FrontDoor{ + Location: utils.String(location), + Properties: &frontdoor.Properties{ + FriendlyName: utils.String(friendlyName), + RoutingRules: expandArmFrontDoorRoutingRule(routingRules, frontDoorPath), + BackendPools: expandArmFrontDoorBackendPools(backendPools, frontDoorPath), + BackendPoolsSettings: expandArmFrontDoorBackendPoolsSettings(backendPoolsSettings), + FrontendEndpoints: expandArmFrontDoorFrontendEndpoint(frontendEndpoints, frontDoorPath), + HealthProbeSettings: expandArmFrontDoorHealthProbeSettingsModel(healthProbeSettings, frontDoorPath), + LoadBalancingSettings: expandArmFrontDoorLoadBalancingSettingsModel(loadBalancingSettings, frontDoorPath), + EnabledState: expandArmFrontDoorEnabledState(enabledState), + }, + Tags: expandTags(tags), + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, frontDoorParameters) + if err != nil { + return fmt.Errorf("Error creating Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for creation of Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if resp.ID == nil { + return fmt.Errorf("Cannot read Front Door %q (Resource Group %q) ID", name, resourceGroup) + } + d.SetId(*resp.ID) + + // Now loop through the FrontendEndpoints and enable/disable Custom Domain HTTPS + // on each individual Frontend Endpoint if required + for _, v := range frontendEndpoints { + frontendEndpoint := v.(map[string]interface{}) + customHttpsProvisioningEnabled := frontendEndpoint["custom_https_provisioning_enabled"].(bool) + frontendEndpointName := frontendEndpoint["name"].(string) + + // Get current state of endpoint from Azure + client := meta.(*ArmClient).frontdoor.FrontDoorsFrontendClient + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, name, frontendEndpointName) + if err != nil { + return fmt.Errorf("Error retrieving Front Door Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err) + } + if resp.ID == nil { + return fmt.Errorf("Cannot read Front Door Frontend Endpoint %q (Resource Group %q) ID", frontendEndpointName, resourceGroup) + } + + if properties := resp.FrontendEndpointProperties; properties != nil { + if provisioningState := properties.CustomHTTPSProvisioningState; provisioningState != "" { + // Check to see if we are going to change the CustomHTTPSProvisioningState, if so check to + // see if its current state is configurable, if not return an error... + if customHttpsProvisioningEnabled != afd.NormalizeCustomHTTPSProvisioningStateToBool(provisioningState) { + if err := afd.IsFrontDoorFrontendEndpointConfigurable(provisioningState, customHttpsProvisioningEnabled, frontendEndpointName, resourceGroup); err != nil { + return err + } + } + + if customHttpsProvisioningEnabled && provisioningState == frontdoor.CustomHTTPSProvisioningStateDisabled { + // Build a custom Https configuration based off the config file to send to the enable call + // NOTE: I do not need to check to see if this exists since I already do that in the validation code + chc := frontendEndpoint["custom_https_configuration"].([]interface{}) + customHttpsConfiguration := chc[0].(map[string]interface{}) + customHTTPSConfigurationUpdate := makeCustomHttpsConfiguration(customHttpsConfiguration) + + // Enable Custom Domain HTTPS for the Frontend Endpoint + if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(true, name, frontendEndpointName, resourceGroup, customHTTPSConfigurationUpdate, meta); err != nil { + return fmt.Errorf("Unable enable Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err) + } + } else if !customHttpsProvisioningEnabled && provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled { + // Disable Custom Domain HTTPS for the Frontend Endpoint + if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(false, name, frontendEndpointName, resourceGroup, frontdoor.CustomHTTPSConfiguration{}, meta); err != nil { + return fmt.Errorf("Unable to disable Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err) + } + } + } + } + } + + return resourceArmFrontDoorRead(d, meta) +} + +func resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(enableCustomHttpsProvisioning bool, frontDoorName string, frontendEndpointName string, resourceGroup string, customHTTPSConfiguration frontdoor.CustomHTTPSConfiguration, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsFrontendClient + ctx := meta.(*ArmClient).StopContext + + if enableCustomHttpsProvisioning { + future, err := client.EnableHTTPS(ctx, resourceGroup, frontDoorName, frontendEndpointName, customHTTPSConfiguration) + + if err != nil { + return fmt.Errorf("Error enabling Custom Domain HTTPS for Frontend Endpoint: %+v", err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting to enable Custom Domain HTTPS for Frontend Endpoint: %+v", err) + } + } else { + future, err := client.DisableHTTPS(ctx, resourceGroup, frontDoorName, frontendEndpointName) + + if err != nil { + return fmt.Errorf("Error disabling Custom Domain HTTPS for Frontend Endpoint: %+v", err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting to disable Custom Domain HTTPS for Frontend Endpoint: %+v", err) + } + } + + return nil +} + +func resourceArmFrontDoorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["frontdoors"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Front Door %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if properties := resp.Properties; properties != nil { + if err := d.Set("backend_pool", flattenArmFrontDoorBackendPools(properties.BackendPools)); err != nil { + return fmt.Errorf("Error setting `backend_pool`: %+v", err) + } + + if err := d.Set("enforce_backend_pools_certificate_name_check", flattenArmFrontDoorBackendPoolsSettings(properties.BackendPoolsSettings)); err != nil { + return fmt.Errorf("Error setting `enforce_backend_pools_certificate_name_check`: %+v", err) + } + + d.Set("cname", properties.Cname) + d.Set("load_balancer_enabled", properties.EnabledState == frontdoor.EnabledStateEnabled) + d.Set("friendly_name", properties.FriendlyName) + + if frontendEndpoints := properties.FrontendEndpoints; frontendEndpoints != nil { + if resp.Name != nil { + if frontDoorFrontendEndpoints, err := flattenArmFrontDoorFrontendEndpoint(frontendEndpoints, resourceGroup, *resp.Name, meta); frontDoorFrontendEndpoints != nil { + if err := d.Set("frontend_endpoint", frontDoorFrontendEndpoints); err != nil { + return fmt.Errorf("Error setting `frontend_endpoint`: %+v", err) + } + } else { + return fmt.Errorf("Error flattening `frontend_endpoint`: %+v", err) + } + } else { + return fmt.Errorf("Error flattening `frontend_endpoint`: Unable to read Frontdoor Name") + } + } + + if err := d.Set("backend_pool_health_probe", flattenArmFrontDoorHealthProbeSettingsModel(properties.HealthProbeSettings)); err != nil { + return fmt.Errorf("Error setting `backend_pool_health_probe`: %+v", err) + } + + if err := d.Set("backend_pool_load_balancing", flattenArmFrontDoorLoadBalancingSettingsModel(properties.LoadBalancingSettings)); err != nil { + return fmt.Errorf("Error setting `backend_pool_load_balancing`: %+v", err) + } + + if err := d.Set("routing_rule", flattenArmFrontDoorRoutingRule(properties.RoutingRules)); err != nil { + return fmt.Errorf("Error setting `routing_rules`: %+v", err) + } + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmFrontDoorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["frontdoors"] + + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error deleting Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deleting Front Door %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} + +func expandArmFrontDoorBackendPools(input []interface{}, frontDoorPath string) *[]frontdoor.BackendPool { + if len(input) == 0 { + return &[]frontdoor.BackendPool{} + } + + output := make([]frontdoor.BackendPool, 0) + + for _, bp := range input { + backendPool := bp.(map[string]interface{}) + + backendPoolName := backendPool["name"].(string) + backendPoolLoadBalancingName := backendPool["load_balancing_name"].(string) + backendPoolHealthProbeName := backendPool["health_probe_name"].(string) + + backends := backendPool["backend"].([]interface{}) + + result := frontdoor.BackendPool{ + ID: utils.String(frontDoorPath + "/BackendPools/" + backendPoolName), + Name: utils.String(backendPoolName), + BackendPoolProperties: &frontdoor.BackendPoolProperties{ + Backends: expandArmFrontDoorBackend(backends), + LoadBalancingSettings: &frontdoor.SubResource{ + ID: utils.String(frontDoorPath + "/LoadBalancingSettings/" + backendPoolLoadBalancingName), + }, + HealthProbeSettings: &frontdoor.SubResource{ + ID: utils.String(frontDoorPath + "/HealthProbeSettings/" + backendPoolHealthProbeName), + }, + }, + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorBackend(input []interface{}) *[]frontdoor.Backend { + if len(input) == 0 { + return &[]frontdoor.Backend{} + } + + output := make([]frontdoor.Backend, 0) + + for _, be := range input { + backend := be.(map[string]interface{}) + + address := backend["address"].(string) + hostHeader := backend["host_header"].(string) + enabled := backend["enabled"].(bool) + httpPort := int32(backend["http_port"].(int)) + httpsPort := int32(backend["https_port"].(int)) + priority := int32(backend["priority"].(int)) + weight := int32(backend["weight"].(int)) + + result := frontdoor.Backend{ + Address: utils.String(address), + BackendHostHeader: utils.String(hostHeader), + EnabledState: expandArmFrontDoorBackendEnabledState(enabled), + HTTPPort: utils.Int32(httpPort), + HTTPSPort: utils.Int32(httpsPort), + Priority: utils.Int32(priority), + Weight: utils.Int32(weight), + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorBackendEnabledState(isEnabled bool) frontdoor.BackendEnabledState { + if isEnabled { + return frontdoor.Enabled + } + + return frontdoor.Disabled +} + +func expandArmFrontDoorBackendPoolsSettings(enforceCertificateNameCheck bool) *frontdoor.BackendPoolsSettings { + enforceCheck := frontdoor.EnforceCertificateNameCheckEnabledStateDisabled + + if enforceCertificateNameCheck { + enforceCheck = frontdoor.EnforceCertificateNameCheckEnabledStateEnabled + } + + result := frontdoor.BackendPoolsSettings{ + EnforceCertificateNameCheck: enforceCheck, + } + + return &result +} + +func expandArmFrontDoorFrontendEndpoint(input []interface{}, frontDoorPath string) *[]frontdoor.FrontendEndpoint { + if len(input) == 0 { + return &[]frontdoor.FrontendEndpoint{} + } + + output := make([]frontdoor.FrontendEndpoint, 0) + + for _, frontendEndpoints := range input { + frontendEndpoint := frontendEndpoints.(map[string]interface{}) + + hostName := frontendEndpoint["host_name"].(string) + isSessionAffinityEnabled := frontendEndpoint["session_affinity_enabled"].(bool) + sessionAffinityTtlSeconds := int32(frontendEndpoint["session_affinity_ttl_seconds"].(int)) + customHttpsConfiguration := frontendEndpoint["custom_https_configuration"].([]interface{}) + waf := frontendEndpoint["web_application_firewall_policy_link_id"].(string) + name := frontendEndpoint["name"].(string) + id := utils.String(frontDoorPath + "/FrontendEndpoints/" + name) + + sessionAffinityEnabled := frontdoor.SessionAffinityEnabledStateDisabled + if isSessionAffinityEnabled { + sessionAffinityEnabled = frontdoor.SessionAffinityEnabledStateEnabled + } + + result := frontdoor.FrontendEndpoint{ + ID: id, + Name: utils.String(name), + FrontendEndpointProperties: &frontdoor.FrontendEndpointProperties{ + CustomHTTPSConfiguration: expandArmFrontDoorCustomHTTPSConfiguration(customHttpsConfiguration), + HostName: utils.String(hostName), + SessionAffinityEnabledState: sessionAffinityEnabled, + SessionAffinityTTLSeconds: utils.Int32(sessionAffinityTtlSeconds), + }, + } + + if waf != "" { + result.FrontendEndpointProperties.WebApplicationFirewallPolicyLink = &frontdoor.FrontendEndpointUpdateParametersWebApplicationFirewallPolicyLink{ + ID: utils.String(waf), + } + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorCustomHTTPSConfiguration(input []interface{}) *frontdoor.CustomHTTPSConfiguration { + if len(input) == 0 { + defaultHttpsConfiguration := frontdoor.CustomHTTPSConfiguration{ + ProtocolType: frontdoor.ServerNameIndication, + CertificateSource: frontdoor.CertificateSourceFrontDoor, + CertificateSourceParameters: &frontdoor.CertificateSourceParameters{ + CertificateType: frontdoor.Dedicated, + }, + } + return &defaultHttpsConfiguration + } + + v := input[0].(map[string]interface{}) + customHttpsConfiguration := makeCustomHttpsConfiguration(v) + + return &customHttpsConfiguration +} + +func expandArmFrontDoorHealthProbeSettingsModel(input []interface{}, frontDoorPath string) *[]frontdoor.HealthProbeSettingsModel { + if len(input) == 0 { + return &[]frontdoor.HealthProbeSettingsModel{} + } + + output := make([]frontdoor.HealthProbeSettingsModel, 0) + + for _, hps := range input { + v := hps.(map[string]interface{}) + + path := v["path"].(string) + protocol := v["protocol"].(string) + intervalInSeconds := int32(v["interval_in_seconds"].(int)) + name := v["name"].(string) + + result := frontdoor.HealthProbeSettingsModel{ + ID: utils.String(frontDoorPath + "/HealthProbeSettings/" + name), + Name: utils.String(name), + HealthProbeSettingsProperties: &frontdoor.HealthProbeSettingsProperties{ + IntervalInSeconds: utils.Int32(intervalInSeconds), + Path: utils.String(path), + Protocol: frontdoor.Protocol(protocol), + }, + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorLoadBalancingSettingsModel(input []interface{}, frontDoorPath string) *[]frontdoor.LoadBalancingSettingsModel { + if len(input) == 0 { + return &[]frontdoor.LoadBalancingSettingsModel{} + } + + output := make([]frontdoor.LoadBalancingSettingsModel, 0) + + for _, lbs := range input { + loadBalanceSetting := lbs.(map[string]interface{}) + + name := loadBalanceSetting["name"].(string) + sampleSize := int32(loadBalanceSetting["sample_size"].(int)) + successfulSamplesRequired := int32(loadBalanceSetting["successful_samples_required"].(int)) + additionalLatencyMilliseconds := int32(loadBalanceSetting["additional_latency_milliseconds"].(int)) + id := utils.String(frontDoorPath + "/LoadBalancingSettings/" + name) + + result := frontdoor.LoadBalancingSettingsModel{ + ID: id, + Name: utils.String(name), + LoadBalancingSettingsProperties: &frontdoor.LoadBalancingSettingsProperties{ + SampleSize: utils.Int32(sampleSize), + SuccessfulSamplesRequired: utils.Int32(successfulSamplesRequired), + AdditionalLatencyMilliseconds: utils.Int32(additionalLatencyMilliseconds), + }, + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorRoutingRule(input []interface{}, frontDoorPath string) *[]frontdoor.RoutingRule { + if len(input) == 0 { + return nil + } + + output := make([]frontdoor.RoutingRule, 0) + + for _, rr := range input { + routingRule := rr.(map[string]interface{}) + + id := routingRule["id"].(string) + frontendEndpoints := routingRule["frontend_endpoints"].([]interface{}) + acceptedProtocols := routingRule["accepted_protocols"].([]interface{}) + ptm := routingRule["patterns_to_match"].([]interface{}) + enabled := routingRule["enabled"].(bool) + name := routingRule["name"].(string) + + patternsToMatch := make([]string, 0) + + for _, p := range ptm { + patternsToMatch = append(patternsToMatch, p.(string)) + } + + var routingConfiguration frontdoor.BasicRouteConfiguration + + if rc := routingRule["redirect_configuration"].([]interface{}); len(rc) != 0 { + routingConfiguration = expandArmFrontDoorRedirectConfiguration(rc) + } else if fc := routingRule["forwarding_configuration"].([]interface{}); len(fc) != 0 { + routingConfiguration = expandArmFrontDoorForwardingConfiguration(fc, frontDoorPath) + } + + currentRoutingRule := frontdoor.RoutingRule{ + ID: utils.String(id), + Name: utils.String(name), + RoutingRuleProperties: &frontdoor.RoutingRuleProperties{ + FrontendEndpoints: expandArmFrontDoorFrontEndEndpoints(frontendEndpoints, frontDoorPath), + AcceptedProtocols: expandArmFrontDoorAcceptedProtocols(acceptedProtocols), + PatternsToMatch: &patternsToMatch, + EnabledState: frontdoor.RoutingRuleEnabledState(expandArmFrontDoorEnabledState(enabled)), + RouteConfiguration: routingConfiguration, + }, + } + output = append(output, currentRoutingRule) + } + + return &output +} + +func expandArmFrontDoorAcceptedProtocols(input []interface{}) *[]frontdoor.Protocol { + if len(input) == 0 { + return &[]frontdoor.Protocol{} + } + + output := make([]frontdoor.Protocol, 0) + + for _, ap := range input { + result := frontdoor.HTTPS + + if ap.(string) == string(frontdoor.HTTP) { + result = frontdoor.HTTP + } + + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorFrontEndEndpoints(input []interface{}, frontDoorPath string) *[]frontdoor.SubResource { + if len(input) == 0 { + return &[]frontdoor.SubResource{} + } + + output := make([]frontdoor.SubResource, 0) + + for _, SubResource := range input { + result := frontdoor.SubResource{ + ID: utils.String(frontDoorPath + "/FrontendEndpoints/" + SubResource.(string)), + } + output = append(output, result) + } + + return &output +} + +func expandArmFrontDoorEnabledState(enabled bool) frontdoor.EnabledState { + if enabled { + return frontdoor.EnabledStateEnabled + } + + return frontdoor.EnabledStateDisabled +} + +func expandArmFrontDoorRedirectConfiguration(input []interface{}) frontdoor.RedirectConfiguration { + if len(input) == 0 { + return frontdoor.RedirectConfiguration{} + } + v := input[0].(map[string]interface{}) + + redirectType := v["redirect_type"].(string) + redirectProtocol := v["redirect_protocol"].(string) + customHost := v["custom_host"].(string) + customPath := v["custom_path"].(string) + customFragment := v["custom_fragment"].(string) + customQueryString := v["custom_query_string"].(string) + + redirectConfiguration := frontdoor.RedirectConfiguration{ + CustomHost: utils.String(customHost), + RedirectType: frontdoor.RedirectType(redirectType), + RedirectProtocol: frontdoor.RedirectProtocol(redirectProtocol), + OdataType: frontdoor.OdataTypeMicrosoftAzureFrontDoorModelsFrontdoorRedirectConfiguration, + } + + // The way the API works is if you don't include the attribute in the structure + // it is treated as Preserve instead of Replace... + if customPath != "" { + redirectConfiguration.CustomPath = utils.String(customPath) + } + if customFragment != "" { + redirectConfiguration.CustomFragment = utils.String(customFragment) + } + if customQueryString != "" { + redirectConfiguration.CustomQueryString = utils.String(customQueryString) + } + + return redirectConfiguration +} + +func expandArmFrontDoorForwardingConfiguration(input []interface{}, frontDoorPath string) frontdoor.ForwardingConfiguration { + if len(input) == 0 { + return frontdoor.ForwardingConfiguration{} + } + v := input[0].(map[string]interface{}) + + customForwardingPath := v["custom_forwarding_path"].(string) + forwardingProtocol := v["forwarding_protocol"].(string) + cacheUseDynamicCompression := v["cache_use_dynamic_compression"].(bool) + cacheQueryParameterStripDirective := v["cache_query_parameter_strip_directive"].(string) + backendPoolName := v["backend_pool_name"].(string) + + useDynamicCompression := frontdoor.DynamicCompressionEnabledDisabled + + if cacheUseDynamicCompression { + useDynamicCompression = frontdoor.DynamicCompressionEnabledEnabled + } + + cacheConfiguration := &frontdoor.CacheConfiguration{ + QueryParameterStripDirective: frontdoor.Query(cacheQueryParameterStripDirective), + DynamicCompression: useDynamicCompression, + } + + backend := &frontdoor.SubResource{ + ID: utils.String(frontDoorPath + "/BackendPools/" + backendPoolName), + } + + forwardingConfiguration := frontdoor.ForwardingConfiguration{ + ForwardingProtocol: frontdoor.ForwardingProtocol(forwardingProtocol), + CacheConfiguration: cacheConfiguration, + BackendPool: backend, + OdataType: frontdoor.OdataTypeMicrosoftAzureFrontDoorModelsFrontdoorForwardingConfiguration, + } + + if customForwardingPath != "" { + forwardingConfiguration.CustomForwardingPath = utils.String(customForwardingPath) + } + + return forwardingConfiguration +} + +func flattenArmFrontDoorBackendPools(input *[]frontdoor.BackendPool) []map[string]interface{} { + if input == nil { + return make([]map[string]interface{}, 0) + } + + output := make([]map[string]interface{}, 0) + + for _, v := range *input { + result := make(map[string]interface{}) + + if id := v.ID; id != nil { + result["id"] = *id + } + + if name := v.Name; name != nil { + result["name"] = *name + } + + if properties := v.BackendPoolProperties; properties != nil { + result["backend"] = flattenArmFrontDoorBackend(properties.Backends) + result["health_probe_name"] = flattenArmFrontDoorSubResource(properties.HealthProbeSettings, "HealthProbeSettings") + result["load_balancing_name"] = flattenArmFrontDoorSubResource(properties.LoadBalancingSettings, "LoadBalancingSettings") + } + output = append(output, result) + } + + return output +} + +func flattenArmFrontDoorBackendPoolsSettings(input *frontdoor.BackendPoolsSettings) bool { + if input == nil { + return true + } + + result := false + + if enforceCertificateNameCheck := input.EnforceCertificateNameCheck; enforceCertificateNameCheck != "" { + if enforceCertificateNameCheck == frontdoor.EnforceCertificateNameCheckEnabledStateEnabled { + result = true + } + } + + return result +} + +func flattenArmFrontDoorBackend(input *[]frontdoor.Backend) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + output := make([]interface{}, 0) + + for _, v := range *input { + result := make(map[string]interface{}) + + if address := v.Address; address != nil { + result["address"] = *address + } + if backendHostHeader := v.BackendHostHeader; backendHostHeader != nil { + result["host_header"] = *backendHostHeader + } + + result["enabled"] = v.EnabledState == frontdoor.Enabled + + if httpPort := v.HTTPPort; httpPort != nil { + result["http_port"] = int(*httpPort) + } + + if httpsPort := v.HTTPSPort; httpsPort != nil { + result["https_port"] = int(*httpsPort) + } + + if priority := v.Priority; priority != nil { + result["priority"] = int(*priority) + } + + if weight := v.Weight; weight != nil { + result["weight"] = int(*weight) + } + + output = append(output, result) + } + + return output +} + +func flattenArmFrontDoorFrontendEndpoint(input *[]frontdoor.FrontendEndpoint, resourceGroup string, frontDoorName string, meta interface{}) ([]interface{}, error) { + if input == nil { + return make([]interface{}, 0), fmt.Errorf("Cannot read Front Door Frontend Endpoint (Resource Group %q): slice is empty", resourceGroup) + } + + output := make([]interface{}, 0) + + for _, v := range *input { + result := make(map[string]interface{}) + customHttpsConfiguration := make([]interface{}, 0) + chc := make(map[string]interface{}) + + if name := v.Name; name != nil { + result["name"] = *name + + // Need to call frontEndEndpointClient here to get customConfiguration information from that client + // because the information is hidden from the main frontDoorClient "by design"... + client := meta.(*ArmClient).frontdoor.FrontDoorsFrontendClient + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, frontDoorName, *name) + if err != nil { + return make([]interface{}, 0), fmt.Errorf("Error retrieving Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q): %+v", *name, resourceGroup, err) + } + if resp.ID == nil { + return make([]interface{}, 0), fmt.Errorf("Cannot read Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q) ID", *name, resourceGroup) + } + + result["id"] = resp.ID + + if properties := resp.FrontendEndpointProperties; properties != nil { + if hostName := properties.HostName; hostName != nil { + result["host_name"] = *hostName + } + + if sessionAffinityEnabled := properties.SessionAffinityEnabledState; sessionAffinityEnabled != "" { + result["session_affinity_enabled"] = sessionAffinityEnabled == frontdoor.SessionAffinityEnabledStateEnabled + } + + if sessionAffinityTtlSeconds := properties.SessionAffinityTTLSeconds; sessionAffinityTtlSeconds != nil { + result["session_affinity_ttl_seconds"] = *sessionAffinityTtlSeconds + } + + if waf := properties.WebApplicationFirewallPolicyLink; waf != nil { + result["web_application_firewall_policy_link_id"] = *waf.ID + } + + if properties.CustomHTTPSConfiguration != nil { + customHTTPSConfiguration := properties.CustomHTTPSConfiguration + if customHTTPSConfiguration.CertificateSource == frontdoor.CertificateSourceAzureKeyVault { + if kvcsp := customHTTPSConfiguration.KeyVaultCertificateSourceParameters; kvcsp != nil { + chc["certificate_source"] = string(frontdoor.CertificateSourceAzureKeyVault) + chc["azure_key_vault_certificate_vault_id"] = *kvcsp.Vault.ID + chc["azure_key_vault_certificate_secret_name"] = *kvcsp.SecretName + chc["azure_key_vault_certificate_secret_version"] = *kvcsp.SecretVersion + } + } else { + chc["certificate_source"] = string(frontdoor.CertificateSourceFrontDoor) + } + + if provisioningState := properties.CustomHTTPSProvisioningState; provisioningState != "" { + chc["provisioning_state"] = provisioningState + if provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled || provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabling { + result["custom_https_provisioning_enabled"] = true + + if provisioningSubstate := properties.CustomHTTPSProvisioningSubstate; provisioningSubstate != "" { + chc["provisioning_substate"] = provisioningSubstate + } + } else { + result["custom_https_provisioning_enabled"] = false + } + + customHttpsConfiguration = append(customHttpsConfiguration, chc) + result["custom_https_configuration"] = customHttpsConfiguration + } + } + } + } + + output = append(output, result) + } + + return output, nil +} + +func flattenArmFrontDoorHealthProbeSettingsModel(input *[]frontdoor.HealthProbeSettingsModel) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + result := make(map[string]interface{}) + + for _, v := range *input { + if id := v.ID; id != nil { + result["id"] = *id + } + if name := v.Name; name != nil { + result["name"] = *name + } + if properties := v.HealthProbeSettingsProperties; properties != nil { + if intervalInSeconds := properties.IntervalInSeconds; intervalInSeconds != nil { + result["interval_in_seconds"] = *intervalInSeconds + } + if path := properties.Path; path != nil { + result["path"] = *path + } + result["protocol"] = string(properties.Protocol) + } + } + + return []interface{}{result} +} + +func flattenArmFrontDoorLoadBalancingSettingsModel(input *[]frontdoor.LoadBalancingSettingsModel) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + + for _, v := range *input { + if id := v.ID; id != nil { + result["id"] = *id + } + if name := v.Name; name != nil { + result["name"] = *name + } + if properties := v.LoadBalancingSettingsProperties; properties != nil { + if additionalLatencyMilliseconds := properties.AdditionalLatencyMilliseconds; additionalLatencyMilliseconds != nil { + result["additional_latency_milliseconds"] = *additionalLatencyMilliseconds + } + if sampleSize := properties.SampleSize; sampleSize != nil { + result["sample_size"] = *sampleSize + } + if successfulSamplesRequired := properties.SuccessfulSamplesRequired; successfulSamplesRequired != nil { + result["successful_samples_required"] = *successfulSamplesRequired + } + } + } + return []interface{}{result} +} + +func flattenArmFrontDoorRoutingRule(input *[]frontdoor.RoutingRule) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + output := make([]interface{}, 0) + + for _, v := range *input { + result := make(map[string]interface{}) + + if id := v.ID; id != nil { + result["id"] = *id + } + if name := v.Name; name != nil { + result["name"] = *name + } + + if properties := v.RoutingRuleProperties; properties != nil { + result["accepted_protocols"] = flattenArmFrontDoorAcceptedProtocol(properties.AcceptedProtocols) + result["enabled"] = properties.EnabledState == frontdoor.RoutingRuleEnabledStateEnabled + result["frontend_endpoints"] = flattenArmFrontDoorFrontendEndpointsSubResources(properties.FrontendEndpoints) + if patternsToMatch := properties.PatternsToMatch; patternsToMatch != nil { + result["patterns_to_match"] = *patternsToMatch + } + + brc := properties.RouteConfiguration + if routeConfigType := afd.GetFrontDoorBasicRouteConfigurationType(brc.(interface{})); routeConfigType != "" { + rc := make([]interface{}, 0) + c := make(map[string]interface{}) + + // there are only two types of Route Configuration + if routeConfigType == "ForwardingConfiguration" { + v := brc.(frontdoor.ForwardingConfiguration) + + c["backend_pool_name"] = flattenArmFrontDoorSubResource(v.BackendPool, "BackendPools") + c["custom_forwarding_path"] = v.CustomForwardingPath + c["forwarding_protocol"] = string(v.ForwardingProtocol) + + if cacheConfiguration := v.CacheConfiguration; cacheConfiguration != nil { + if queryParameter := cacheConfiguration.QueryParameterStripDirective; queryParameter != "" { + c["cache_query_parameter_strip_directive"] = string(queryParameter) + } else { + c["cache_query_parameter_strip_directive"] = string(frontdoor.StripNone) + } + + c["cache_use_dynamic_compression"] = false + + if dynamicCompression := cacheConfiguration.DynamicCompression; dynamicCompression != "" { + if dynamicCompression == frontdoor.DynamicCompressionEnabledEnabled { + c["cache_use_dynamic_compression"] = true + } + } + } else { + // Set Defaults + c["cache_query_parameter_strip_directive"] = string(frontdoor.StripNone) + c["cache_use_dynamic_compression"] = false + } + + rc = append(rc, c) + result["forwarding_configuration"] = rc + } else { + v := brc.(frontdoor.RedirectConfiguration) + c["custom_fragment"] = v.CustomFragment + c["custom_host"] = v.CustomHost + c["custom_path"] = v.CustomPath + c["custom_query_string"] = v.CustomQueryString + c["redirect_protocol"] = string(v.RedirectProtocol) + c["redirect_type"] = string(v.RedirectType) + + rc = append(rc, c) + result["redirect_configuration"] = rc + } + } + } + + output = append(output, result) + } + + return output +} + +func flattenArmFrontDoorAcceptedProtocol(input *[]frontdoor.Protocol) []string { + if input == nil { + return make([]string, 0) + } + + output := make([]string, 0) + for _, p := range *input { + output = append(output, string(p)) + } + + return output +} + +func flattenArmFrontDoorSubResource(input *frontdoor.SubResource, resourceType string) string { + if input == nil { + return "" + } + + name := "" + + if id := input.ID; id != nil { + aid, err := parseAzureResourceID(*id) + if err != nil { + return "" + } + name = aid.Path[resourceType] + } + + return name +} + +func flattenArmFrontDoorFrontendEndpointsSubResources(input *[]frontdoor.SubResource) []string { + if input == nil { + return make([]string, 0) + } + + output := make([]string, 0) + + for _, v := range *input { + name := flattenArmFrontDoorSubResource(&v, "FrontendEndpoints") + output = append(output, name) + } + + return output +} + +func makeCustomHttpsConfiguration(customHttpsConfiguration map[string]interface{}) frontdoor.CustomHTTPSConfiguration { + customHTTPSConfigurationUpdate := frontdoor.CustomHTTPSConfiguration{ + ProtocolType: frontdoor.ServerNameIndication, + } + + if customHttpsConfiguration["certificate_source"].(string) == "AzureKeyVault" { + vaultSecret := customHttpsConfiguration["azure_key_vault_certificate_secret_name"].(string) + vaultVersion := customHttpsConfiguration["azure_key_vault_certificate_secret_version"].(string) + vaultId := customHttpsConfiguration["azure_key_vault_certificate_vault_id"].(string) + + customHTTPSConfigurationUpdate.CertificateSource = frontdoor.CertificateSourceAzureKeyVault + customHTTPSConfigurationUpdate.KeyVaultCertificateSourceParameters = &frontdoor.KeyVaultCertificateSourceParameters{ + Vault: &frontdoor.KeyVaultCertificateSourceParametersVault{ + ID: utils.String(vaultId), + }, + SecretName: utils.String(vaultSecret), + SecretVersion: utils.String(vaultVersion), + } + } else { + customHTTPSConfigurationUpdate.CertificateSource = frontdoor.CertificateSourceFrontDoor + customHTTPSConfigurationUpdate.CertificateSourceParameters = &frontdoor.CertificateSourceParameters{ + CertificateType: frontdoor.Dedicated, + } + } + + return customHTTPSConfigurationUpdate +} diff --git a/azurerm/resource_arm_front_door_firewall_policy.go b/azurerm/resource_arm_front_door_firewall_policy.go new file mode 100644 index 000000000000..e16d82087913 --- /dev/null +++ b/azurerm/resource_arm_front_door_firewall_policy.go @@ -0,0 +1,770 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + afd "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmFrontDoorFirewallPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceArmFrontDoorFirewallPolicyCreateUpdate, + Read: resourceArmFrontDoorFirewallPolicyRead, + Update: resourceArmFrontDoorFirewallPolicyCreateUpdate, + Delete: resourceArmFrontDoorFirewallPolicyDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "location": azure.SchemaLocationForDataSource(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Detection), + string(frontdoor.Prevention), + }, false), + Default: string(frontdoor.Prevention), + }, + + "redirect_url": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.URLIsHTTPOrHTTPS, + }, + + "custom_block_response_status_code": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validate.IntInSlice([]int{ + 200, + 403, + 405, + 406, + 429, + }), + }, + + "custom_block_response_body": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: afd.ValidateCustomBlockResponseBody, + }, + + "custom_rule": { + Type: schema.TypeList, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "priority": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + }, + + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.MatchRule), + string(frontdoor.RateLimitRule), + }, false), + }, + + "rate_limit_duration_in_minutes": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + }, + + "rate_limit_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: 10, + }, + + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Allow), + string(frontdoor.Block), + string(frontdoor.Log), + string(frontdoor.Redirect), + }, false), + }, + + "match_condition": { + Type: schema.TypeList, + Optional: true, + MaxItems: 100, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_variable": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Cookies), + string(frontdoor.PostArgs), + string(frontdoor.QueryString), + string(frontdoor.RemoteAddr), + string(frontdoor.RequestBody), + string(frontdoor.RequestHeader), + string(frontdoor.RequestMethod), + string(frontdoor.RequestURI), + }, false), + }, + + "match_values": { + Type: schema.TypeList, + Required: true, + MaxItems: 100, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Any), + string(frontdoor.BeginsWith), + string(frontdoor.Contains), + string(frontdoor.EndsWith), + string(frontdoor.Equal), + string(frontdoor.GeoMatch), + string(frontdoor.GreaterThan), + string(frontdoor.GreaterThanOrEqual), + string(frontdoor.IPMatch), + string(frontdoor.LessThan), + string(frontdoor.LessThanOrEqual), + string(frontdoor.RegEx), + }, false), + }, + + "selector": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "negation_condition": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "transforms": { + Type: schema.TypeList, + Optional: true, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Lowercase), + string(frontdoor.RemoveNulls), + string(frontdoor.Trim), + string(frontdoor.Uppercase), + string(frontdoor.URLDecode), + string(frontdoor.URLEncode), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + + "managed_rule": { + Type: schema.TypeList, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "override": { + Type: schema.TypeList, + MaxItems: 100, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_group_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "rule": { + Type: schema.TypeList, + MaxItems: 1000, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.Allow), + string(frontdoor.Block), + string(frontdoor.Log), + string(frontdoor.Redirect), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + + "frontend_endpoint_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmFrontDoorFirewallPolicyCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsPolicyClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing args for Front Door Firewall Policy") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if requireResourcesToBeImported { + existing, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for present of existing Front Door Firewall Policy %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_frontdoor_firewall_policy", *existing.ID) + } + } + + location := azure.NormalizeLocation("Global") + enabled := frontdoor.PolicyEnabledStateDisabled + if d.Get("enabled").(bool) { + enabled = frontdoor.PolicyEnabledStateEnabled + } + mode := d.Get("mode").(string) + redirectUrl := d.Get("redirect_url").(string) + customBlockResponseStatusCode := d.Get("custom_block_response_status_code").(int) + customBlockResponseBody := d.Get("custom_block_response_body").(string) + customRules := d.Get("custom_rule").([]interface{}) + managedRules := d.Get("managed_rule").([]interface{}) + tags := d.Get("tags").(map[string]interface{}) + + frontdoorWebApplicationFirewallPolicy := frontdoor.WebApplicationFirewallPolicy{ + Name: utils.String(name), + Location: utils.String(location), + WebApplicationFirewallPolicyProperties: &frontdoor.WebApplicationFirewallPolicyProperties{ + PolicySettings: &frontdoor.PolicySettings{ + EnabledState: enabled, + Mode: frontdoor.PolicyMode(mode), + }, + CustomRules: expandArmFrontDoorFirewallCustomRules(customRules), + ManagedRules: expandArmFrontDoorFirewallManagedRules(managedRules), + }, + Tags: expandTags(tags), + } + + if redirectUrl != "" { + frontdoorWebApplicationFirewallPolicy.WebApplicationFirewallPolicyProperties.PolicySettings.RedirectURL = utils.String(redirectUrl) + } + if customBlockResponseBody != "" { + frontdoorWebApplicationFirewallPolicy.WebApplicationFirewallPolicyProperties.PolicySettings.CustomBlockResponseBody = utils.String(customBlockResponseBody) + } + if customBlockResponseStatusCode > 0 { + frontdoorWebApplicationFirewallPolicy.WebApplicationFirewallPolicyProperties.PolicySettings.CustomBlockResponseStatusCode = utils.Int32(int32(customBlockResponseStatusCode)) + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, frontdoorWebApplicationFirewallPolicy) + if err != nil { + return fmt.Errorf("Error creating Front Door Firewall policy %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for creation of Front Door Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Front Door Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if resp.ID == nil { + return fmt.Errorf("Cannot read Front Door Firewall %q (Resource Group %q) ID", name, resourceGroup) + } + d.SetId(*resp.ID) + + return resourceArmFrontDoorFirewallPolicyRead(d, meta) +} + +func resourceArmFrontDoorFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsPolicyClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["frontdoorwebapplicationfirewallpolicies"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Front Door Firewall Policy %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Front Door Firewall Policy %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if properties := resp.WebApplicationFirewallPolicyProperties; properties != nil { + if policy := properties.PolicySettings; policy != nil { + d.Set("enabled", policy.EnabledState == frontdoor.PolicyEnabledStateEnabled) + d.Set("mode", string(policy.Mode)) + d.Set("redirect_url", policy.RedirectURL) + d.Set("custom_block_response_status_code", policy.CustomBlockResponseStatusCode) + d.Set("custom_block_response_body", policy.CustomBlockResponseBody) + } + + if err := d.Set("custom_rule", flattenArmFrontDoorFirewallCustomRules(properties.CustomRules)); err != nil { + return fmt.Errorf("Error flattening `custom_rule`: %+v", err) + } + + if err := d.Set("managed_rule", flattenArmFrontDoorFirewallManagedRules(properties.ManagedRules)); err != nil { + return fmt.Errorf("Error flattening `managed_rule`: %+v", err) + } + + if err := d.Set("frontend_endpoint_ids", afd.FlattenFrontendEndpointLinkSlice(properties.FrontendEndpointLinks)); err != nil { + return fmt.Errorf("Error flattening `frontend_endpoint_ids`: %+v", err) + } + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmFrontDoorFirewallPolicyDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).frontdoor.FrontDoorsPolicyClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["frontdoorwebapplicationfirewallpolicies"] + + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error deleting Front Door Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deleting Front Door Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} + +func expandArmFrontDoorFirewallCustomRules(input []interface{}) *frontdoor.CustomRuleList { + if len(input) == 0 { + return nil + } + + output := make([]frontdoor.CustomRule, 0) + + for _, cr := range input { + custom := cr.(map[string]interface{}) + + enabled := frontdoor.CustomRuleEnabledStateDisabled + if custom["enabled"].(bool) { + enabled = frontdoor.CustomRuleEnabledStateEnabled + } + + name := custom["name"].(string) + priority := int32(custom["priority"].(int)) + ruleType := custom["type"].(string) + rateLimitDurationInMinutes := int32(custom["rate_limit_duration_in_minutes"].(int)) + rateLimitThreshold := int32(custom["rate_limit_threshold"].(int)) + matchConditions := custom["match_condition"].([]interface{}) + action := custom["action"].(string) + + customRule := frontdoor.CustomRule{ + Name: utils.String(name), + Priority: utils.Int32(priority), + EnabledState: enabled, + RuleType: frontdoor.RuleType(ruleType), + RateLimitDurationInMinutes: utils.Int32(rateLimitDurationInMinutes), + RateLimitThreshold: utils.Int32(rateLimitThreshold), + MatchConditions: expandArmFrontDoorFirewallMatchConditions(matchConditions), + Action: frontdoor.ActionType(action), + } + output = append(output, customRule) + } + + return &frontdoor.CustomRuleList{ + Rules: &output, + } +} + +func expandArmFrontDoorFirewallMatchConditions(input []interface{}) *[]frontdoor.MatchCondition { + if len(input) == 0 { + return nil + } + + result := make([]frontdoor.MatchCondition, 0) + + for _, v := range input { + match := v.(map[string]interface{}) + + matchVariable := match["match_variable"].(string) + selector := match["selector"].(string) + operator := match["operator"].(string) + negateCondition := match["negation_condition"].(bool) + matchValues := match["match_values"].([]interface{}) + transforms := match["transforms"].([]interface{}) + + matchCondition := frontdoor.MatchCondition{ + Operator: frontdoor.Operator(operator), + NegateCondition: &negateCondition, + MatchValue: utils.ExpandStringSlice(matchValues), + Transforms: expandArmFrontDoorFirewallTransforms(transforms), + } + + if matchVariable != "" { + matchCondition.MatchVariable = frontdoor.MatchVariable(matchVariable) + } + if selector != "" { + matchCondition.Selector = utils.String(selector) + } + + result = append(result, matchCondition) + } + + return &result +} + +func expandArmFrontDoorFirewallTransforms(input []interface{}) *[]frontdoor.TransformType { + if len(input) == 0 { + return nil + } + + result := make([]frontdoor.TransformType, 0) + for _, v := range input { + result = append(result, frontdoor.TransformType(v.(string))) + } + + return &result +} + +func expandArmFrontDoorFirewallManagedRules(input []interface{}) *frontdoor.ManagedRuleSetList { + if len(input) == 0 { + return nil + } + + managedRules := make([]frontdoor.ManagedRuleSet, 0) + + for _, mr := range input { + managedRule := mr.(map[string]interface{}) + + ruleType := managedRule["type"].(string) + version := managedRule["version"].(string) + overrides := managedRule["override"].([]interface{}) + + managedRuleSet := frontdoor.ManagedRuleSet{ + RuleSetType: utils.String(ruleType), + RuleSetVersion: utils.String(version), + } + + if ruleGroupOverrides := expandArmFrontDoorFirewallManagedRuleGroupOverride(overrides); ruleGroupOverrides != nil { + managedRuleSet.RuleGroupOverrides = ruleGroupOverrides + } + + managedRules = append(managedRules, managedRuleSet) + } + + return &frontdoor.ManagedRuleSetList{ + ManagedRuleSets: &managedRules, + } +} + +func expandArmFrontDoorFirewallManagedRuleGroupOverride(input []interface{}) *[]frontdoor.ManagedRuleGroupOverride { + if len(input) == 0 { + return nil + } + + managedRuleGroupOverrides := make([]frontdoor.ManagedRuleGroupOverride, 0) + for _, v := range input { + override := v.(map[string]interface{}) + + ruleGroupName := override["rule_group_name"].(string) + rules := override["rule"].([]interface{}) + + managedRuleGroupOverride := frontdoor.ManagedRuleGroupOverride{ + RuleGroupName: utils.String(ruleGroupName), + } + + if managedRuleOverride := expandArmFrontDoorFirewallRuleOverride(rules); managedRuleOverride != nil { + managedRuleGroupOverride.Rules = managedRuleOverride + } + + managedRuleGroupOverrides = append(managedRuleGroupOverrides, managedRuleGroupOverride) + } + + return &managedRuleGroupOverrides +} + +func expandArmFrontDoorFirewallRuleOverride(input []interface{}) *[]frontdoor.ManagedRuleOverride { + if len(input) == 0 { + return nil + } + + managedRuleOverrides := make([]frontdoor.ManagedRuleOverride, 0) + for _, v := range input { + rule := v.(map[string]interface{}) + + enabled := frontdoor.ManagedRuleEnabledStateDisabled + if rule["enabled"].(bool) { + enabled = frontdoor.ManagedRuleEnabledStateEnabled + } + ruleId := rule["rule_id"].(string) + action := rule["action"].(string) + + managedRuleOverride := frontdoor.ManagedRuleOverride{ + RuleID: utils.String(ruleId), + EnabledState: enabled, + Action: frontdoor.ActionType(action), + } + + managedRuleOverrides = append(managedRuleOverrides, managedRuleOverride) + } + + return &managedRuleOverrides +} + +func flattenArmFrontDoorFirewallCustomRules(input *frontdoor.CustomRuleList) []interface{} { + if input == nil || input.Rules == nil { + return make([]interface{}, 0) + } + + results := make([]interface{}, 0) + for _, r := range *input.Rules { + output := make(map[string]interface{}) + + output["name"] = r.Name + output["type"] = string(r.RuleType) + output["action"] = string(r.Action) + output["enabled"] = r.EnabledState == frontdoor.CustomRuleEnabledStateEnabled + output["match_condition"] = flattenArmFrontDoorFirewallMatchConditions(r.MatchConditions) + + if v := r.Priority; v != nil { + output["priority"] = int(*v) + } + + if v := r.RateLimitDurationInMinutes; v != nil { + output["rate_limit_duration_in_minutes"] = int(*v) + } + + if v := r.RateLimitThreshold; v != nil { + output["rate_limit_threshold"] = int(*v) + } + + results = append(results, output) + } + + return results +} + +func flattenArmFrontDoorFirewallMatchConditions(condition *[]frontdoor.MatchCondition) []interface{} { + if condition == nil { + return make([]interface{}, 0) + } + + results := make([]interface{}, 0) + for _, c := range *condition { + output := make(map[string]interface{}) + + output["match_variable"] = string(c.MatchVariable) + output["operator"] = string(c.Operator) + output["match_values"] = utils.FlattenStringSlice(c.MatchValue) + output["transforms"] = afd.FlattenTransformSlice(c.Transforms) + + if v := c.Selector; v != nil { + output["selector"] = *v + } + + if v := c.NegateCondition; v != nil { + output["negation_condition"] = *v + } + + results = append(results, output) + } + + return results +} + +func flattenArmFrontDoorFirewallManagedRules(input *frontdoor.ManagedRuleSetList) []interface{} { + if input == nil || input.ManagedRuleSets == nil { + return make([]interface{}, 0) + } + + results := make([]interface{}, 0) + for _, r := range *input.ManagedRuleSets { + output := make(map[string]interface{}) + + if v := r.RuleSetType; v != nil { + output["type"] = *v + } + + if v := r.RuleSetVersion; v != nil { + output["version"] = *v + } + + if v := r.RuleGroupOverrides; v != nil { + output["override"] = flattenArmFrontDoorFirewallOverrides(v) + } + + results = append(results, output) + } + + return results +} + +func flattenArmFrontDoorFirewallOverrides(groupOverride *[]frontdoor.ManagedRuleGroupOverride) []interface{} { + if groupOverride == nil { + return make([]interface{}, 0) + } + + results := make([]interface{}, 0) + for _, o := range *groupOverride { + output := make(map[string]interface{}) + + if v := o.RuleGroupName; v != nil { + output["rule_group_name"] = *v + } + + if rules := o.Rules; rules != nil { + output["rule"] = flattenArmFrontdoorFirewallRules(rules) + } + + results = append(results, output) + } + + return results +} + +func flattenArmFrontdoorFirewallRules(override *[]frontdoor.ManagedRuleOverride) []interface{} { + if override == nil { + return make([]interface{}, 0) + } + + results := make([]interface{}, 0) + for _, o := range *override { + output := make(map[string]interface{}) + + output["enabled"] = o.EnabledState == frontdoor.ManagedRuleEnabledStateEnabled + output["action"] = string(o.Action) + + if v := o.RuleID; v != nil { + output["rule_id"] = *v + } + + results = append(results, output) + } + + return results +} diff --git a/azurerm/resource_arm_front_door_firewall_policy_test.go b/azurerm/resource_arm_front_door_firewall_policy_test.go new file mode 100644 index 000000000000..275429e041a1 --- /dev/null +++ b/azurerm/resource_arm_front_door_firewall_policy_test.go @@ -0,0 +1,282 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMFrontDoorFirewallPolicy_basic(t *testing.T) { + resourceName := "azurerm_frontdoor_firewall_policy.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMFrontDoorFirewallPolicy_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoorWAF%d", ri)), + resource.TestCheckResourceAttr(resourceName, "mode", "Prevention"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFrontDoorFirewallPolicy_update(t *testing.T) { + resourceName := "azurerm_frontdoor_firewall_policy.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMFrontDoorFirewallPolicy_update(ri, "", testLocation()) + configUpdate := testAccAzureRMFrontDoorFirewallPolicy_update(ri, testAccAzureRMFrontDoorFirewallPolicy_updateTemplate(), testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoorWAF%d", ri)), + resource.TestCheckResourceAttr(resourceName, "mode", "Prevention"), + ), + }, + { + Config: configUpdate, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoorWAF%d", ri)), + resource.TestCheckResourceAttr(resourceName, "mode", "Prevention"), + resource.TestCheckResourceAttr(resourceName, "custom_rule.1.name", "Rule2"), + ), + }, + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName), + testCheckAzureRMFrontDoorFirewallPolicyAttrNotExists(resourceName, "custom_rule.1.name"), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoorWAF%d", ri)), + resource.TestCheckResourceAttr(resourceName, "mode", "Prevention"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFrontDoorFirewallPolicy_complete(t *testing.T) { + resourceName := "azurerm_frontdoor_firewall_policy.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMFrontDoorFirewallPolicy_update(ri, testAccAzureRMFrontDoorFirewallPolicy_updateTemplate(), testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoorWAF%d", ri)), + resource.TestCheckResourceAttr(resourceName, "mode", "Prevention"), + resource.TestCheckResourceAttr(resourceName, "redirect_url", "https://www.contoso.com"), + resource.TestCheckResourceAttr(resourceName, "custom_block_response_status_code", "403"), + resource.TestCheckResourceAttr(resourceName, "custom_rule.0.name", "Rule1"), + resource.TestCheckResourceAttr(resourceName, "custom_rule.1.name", "Rule2"), + resource.TestCheckResourceAttr(resourceName, "managed_rule.0.type", "DefaultRuleSet"), + resource.TestCheckResourceAttr(resourceName, "managed_rule.1.type", "BotProtection"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMFrontDoorFirewallPolicyExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Front Door Firewall Policy not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).frontdoor.FrontDoorsPolicyClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Front Door Firewall Policy %q (Resource Group %q) does not exist", name, resourceGroup) + } + return fmt.Errorf("Bad: Get on FrontDoorsPolicyClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMFrontDoorFirewallPolicyDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).frontdoor.FrontDoorsPolicyClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_frontdoor_firewall_policy" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Get on FrontDoorsPolicyClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testCheckAzureRMFrontDoorFirewallPolicyAttrNotExists(name string, attribute string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if testAttr := rs.Primary.Attributes[attribute]; testAttr != "" { + return fmt.Errorf("Attribute still exists: %s", attribute) + } + + return nil + } +} + +func testAccAzureRMFrontDoorFirewallPolicy_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "testAccRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_frontdoor_firewall_policy" "test" { + name = "testAccFrontDoorWAF%[1]d" + resource_group_name = azurerm_resource_group.test.name +} +`, rInt, location) +} + +func testAccAzureRMFrontDoorFirewallPolicy_updateTemplate() string { + return fmt.Sprintf(` + custom_rule { + name = "Rule2" + enabled = true + priority = 2 + rate_limit_duration_in_minutes = 1 + rate_limit_threshold = 10 + type = "MatchRule" + action = "Block" + + match_condition { + match_variable = "RemoteAddr" + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24"] + } + + match_condition { + match_variable = "RequestHeader" + selector = "UserAgent" + operator = "Contains" + negation_condition = false + match_values = ["windows"] + transforms = ["Lowercase", "Trim"] + } + } +`) +} + +func testAccAzureRMFrontDoorFirewallPolicy_update(rInt int, sTemplate string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "testAccRG-%[1]d" + location = "%[3]s" +} + +resource "azurerm_frontdoor_firewall_policy" "test" { + name = "testAccFrontDoorWAF%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + enabled = true + mode = "Prevention" + redirect_url = "https://www.contoso.com" + custom_block_response_status_code = 403 + custom_block_response_body = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg==" + + custom_rule { + name = "Rule1" + enabled = true + priority = 1 + rate_limit_duration_in_minutes = 1 + rate_limit_threshold = 10 + type = "MatchRule" + action = "Block" + + match_condition { + match_variable = "RemoteAddr" + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24", "10.0.0.0/24"] + } + } + + %[2]s + + managed_rule { + type = "DefaultRuleSet" + version = "preview-0.1" + + override { + rule_group_name = "PHP" + + rule { + rule_id = "933111" + enabled = false + action = "Block" + } + } + } + + managed_rule { + type = "BotProtection" + version = "preview-0.1" + } +} +`, rInt, sTemplate, location) +} diff --git a/azurerm/resource_arm_front_door_test.go b/azurerm/resource_arm_front_door_test.go new file mode 100644 index 000000000000..3d86db25ee1b --- /dev/null +++ b/azurerm/resource_arm_front_door_test.go @@ -0,0 +1,485 @@ +package azurerm + +import ( + "fmt" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMFrontDoor_basic(t *testing.T) { + resourceName := "azurerm_frontdoor.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + config := testAccAzureRMFrontDoor_basic(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoor-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "load_balancer_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "enforce_backend_pools_certificate_name_check", "false"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.name", fmt.Sprintf("testAccBackendBing-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.address", "www.bing.com"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.load_balancing_name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.health_probe_name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.http_port", "80"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.weight", "50"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.protocol", "Http"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.successful_samples_required", "2"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.name", fmt.Sprintf("testAccFrontendEndpoint1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.host_name", fmt.Sprintf("testAccFrontDoor-%d.azurefd.net", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.custom_https_provisioning_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_ttl_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.name", fmt.Sprintf("testAccRoutingRule1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.0", "Http"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.1", "Https"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_use_dynamic_compression", "false"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.forwarding_protocol", "MatchRequest"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_query_parameter_strip_directive", "StripNone"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.frontend_endpoints.0", fmt.Sprintf("testAccFrontendEndpoint1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.patterns_to_match.0", "/*"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFrontDoor_update(t *testing.T) { + resourceName := "azurerm_frontdoor.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + config := testAccAzureRMFrontDoor_basic(ri, rs, testLocation()) + update := testAccAzureRMFrontDoor_complete(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + ), + }, + { + Config: update, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoor-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "friendly_name", "tafd"), + resource.TestCheckResourceAttr(resourceName, "enforce_backend_pools_certificate_name_check", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.name", fmt.Sprintf("testAccBackendGoogle-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.address", "www.google.com"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.load_balancing_name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.health_probe_name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.http_port", "80"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.weight", "50"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.protocol", "Https"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_ttl_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_use_dynamic_compression", "true"), + ), + }, + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoor-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "load_balancer_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "enforce_backend_pools_certificate_name_check", "false"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.name", fmt.Sprintf("testAccBackendBing-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.address", "www.bing.com"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.load_balancing_name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.health_probe_name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.http_port", "80"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.weight", "50"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.protocol", "Http"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.successful_samples_required", "2"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.name", fmt.Sprintf("testAccFrontendEndpoint1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.host_name", fmt.Sprintf("testAccFrontDoor-%d.azurefd.net", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.custom_https_provisioning_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_ttl_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.name", fmt.Sprintf("testAccRoutingRule1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.0", "Http"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.1", "Https"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_use_dynamic_compression", "false"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.forwarding_protocol", "MatchRequest"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_query_parameter_strip_directive", "StripNone"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.frontend_endpoints.0", fmt.Sprintf("testAccFrontendEndpoint1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.patterns_to_match.0", "/*"), + ), + }, + }, + }) +} + +func TestAccAzureRMFrontDoor_complete(t *testing.T) { + resourceName := "azurerm_frontdoor.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + config := testAccAzureRMFrontDoor_complete(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoor-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "friendly_name", "tafd"), + resource.TestCheckResourceAttr(resourceName, "load_balancer_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "enforce_backend_pools_certificate_name_check", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.name", fmt.Sprintf("testAccBackendBing-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.address", "www.bing.com"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.load_balancing_name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.health_probe_name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.http_port", "80"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.0.backend.0.weight", "50"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.address", "www.google.com"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.load_balancing_name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.health_probe_name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.http_port", "80"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.priority", "1"), + resource.TestCheckResourceAttr(resourceName, "backend_pool.1.backend.0.weight", "50"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.name", fmt.Sprintf("testAccHealthProbeSetting1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_health_probe.0.protocol", "Https"), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.name", fmt.Sprintf("testAccLoadBalancingSettings1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "backend_pool_load_balancing.0.successful_samples_required", "2"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.name", fmt.Sprintf("testAccFrontendBing-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.host_name", fmt.Sprintf("testAccFrontDoor-%d.azurefd.net", ri)), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.custom_https_provisioning_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "frontend_endpoint.0.session_affinity_ttl_seconds", "0"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.name", fmt.Sprintf("testAccRoutingRule1-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.0", "Http"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.accepted_protocols.1", "Https"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_use_dynamic_compression", "true"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.forwarding_protocol", "MatchRequest"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.forwarding_configuration.0.cache_query_parameter_strip_directive", "StripNone"), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.frontend_endpoints.0", fmt.Sprintf("testAccFrontendBing-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "routing_rule.0.patterns_to_match.0", "/*"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFrontDoor_waf(t *testing.T) { + resourceName := "azurerm_frontdoor.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + config := testAccAzureRMFrontDoor_waf(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFrontDoorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFrontDoorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("testAccFrontDoor-%d", ri)), + resource.TestCheckResourceAttrSet(resourceName, "frontend_endpoint.0.web_application_firewall_policy_link_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMFrontDoorExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Front Door not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).frontdoor.FrontDoorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Front Door %q (Resource Group %q) does not exist", name, resourceGroup) + } + return fmt.Errorf("Bad: Get on FrontDoorsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMFrontDoorDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).frontdoor.FrontDoorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_front_door" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Get on FrontDoorsClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testAccAzureRMFrontDoor_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "testAccRG-%[1]d" + location = "%[3]s" +} + +resource "azurerm_frontdoor" "test" { + name = "testAccFrontDoor-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + enforce_backend_pools_certificate_name_check = false + + routing_rule { + name = "testAccRoutingRule1-%[1]d" + accepted_protocols = ["Http", "Https"] + patterns_to_match = ["/*"] + frontend_endpoints = ["testAccFrontendEndpoint1-%[1]d"] + forwarding_configuration { + forwarding_protocol = "MatchRequest" + backend_pool_name = "testAccBackendBing-%[1]d" + } + } + + backend_pool_load_balancing { + name = "testAccLoadBalancingSettings1-%[1]d" + } + + backend_pool_health_probe { + name = "testAccHealthProbeSetting1-%[1]d" + } + + backend_pool { + name = "testAccBackendBing-%[1]d" + backend { + host_header = "www.bing.com" + address = "www.bing.com" + http_port = 80 + https_port = 443 + } + + load_balancing_name = "testAccLoadBalancingSettings1-%[1]d" + health_probe_name = "testAccHealthProbeSetting1-%[1]d" + } + + frontend_endpoint { + name = "testAccFrontendEndpoint1-%[1]d" + host_name = "testAccFrontDoor-%[1]d.azurefd.net" + custom_https_provisioning_enabled = false + } +} +`, rInt, rString, location) +} + +func testAccAzureRMFrontDoor_complete(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "testAccRG-%[1]d" + location = "%[3]s" +} + +resource "azurerm_frontdoor" "test" { + name = "testAccFrontDoor-%[1]d" + friendly_name = "tafd" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + load_balancer_enabled = true + enforce_backend_pools_certificate_name_check = true + + routing_rule { + name = "testAccRoutingRule1-%[1]d" + enabled = true + accepted_protocols = ["Http", "Https"] + patterns_to_match = ["/*"] + frontend_endpoints = ["testAccFrontendBing-%[1]d"] + forwarding_configuration { + forwarding_protocol = "MatchRequest" + cache_use_dynamic_compression = true + backend_pool_name = "testAccBackendBing-%[1]d" + } + } + + backend_pool_load_balancing { + name = "testAccLoadBalancingSettings1-%[1]d" + sample_size = 4 + successful_samples_required = 2 + additional_latency_milliseconds = 0 + } + + backend_pool_health_probe { + name = "testAccHealthProbeSetting1-%[1]d" + path = "/" + protocol = "Https" + interval_in_seconds = 120 + } + + backend_pool { + name = "testAccBackendBing-%[1]d" + backend { + enabled = true + host_header = "www.bing.com" + address = "www.bing.com" + http_port = 80 + https_port = 443 + weight = 50 + priority = 1 + } + + load_balancing_name = "testAccLoadBalancingSettings1-%[1]d" + health_probe_name = "testAccHealthProbeSetting1-%[1]d" + } + + backend_pool { + name = "testAccBackendGoogle-%[1]d" + backend { + enabled = true + host_header = "www.google.com" + address = "www.google.com" + http_port = 80 + https_port = 443 + weight = 50 + priority = 1 + } + + load_balancing_name = "testAccLoadBalancingSettings1-%[1]d" + health_probe_name = "testAccHealthProbeSetting1-%[1]d" + } + + frontend_endpoint { + name = "testAccFrontendBing-%[1]d" + host_name = "testAccFrontDoor-%[1]d.azurefd.net" + session_affinity_enabled = true + session_affinity_ttl_seconds = 0 + custom_https_provisioning_enabled = false + } +} +`, rInt, rString, location) +} + +func testAccAzureRMFrontDoor_waf(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "testAccRG-%[1]d" + location = "%[3]s" +} + +resource "azurerm_frontdoor_firewall_policy" "test" { + name = "accTestWAF%[1]d" + resource_group_name = azurerm_resource_group.test.name + mode = "Prevention" +} + +resource "azurerm_frontdoor" "test" { + name = "testAccFrontDoor-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + enforce_backend_pools_certificate_name_check = false + + routing_rule { + name = "testAccRoutingRule1-%[1]d" + accepted_protocols = ["Http", "Https"] + patterns_to_match = ["/*"] + frontend_endpoints = ["testAccFrontendEndpoint1-%[1]d"] + forwarding_configuration { + forwarding_protocol = "MatchRequest" + backend_pool_name = "testAccBackendBing-%[1]d" + } + } + + backend_pool_load_balancing { + name = "testAccLoadBalancingSettings1-%[1]d" + } + + backend_pool_health_probe { + name = "testAccHealthProbeSetting1-%[1]d" + } + + backend_pool { + name = "testAccBackendBing-%[1]d" + backend { + host_header = "www.bing.com" + address = "www.bing.com" + http_port = 80 + https_port = 443 + } + + load_balancing_name = "testAccLoadBalancingSettings1-%[1]d" + health_probe_name = "testAccHealthProbeSetting1-%[1]d" + } + + frontend_endpoint { + name = "testAccFrontendEndpoint1-%[1]d" + host_name = "testAccFrontDoor-%[1]d.azurefd.net" + custom_https_provisioning_enabled = false + web_application_firewall_policy_link_id = azurerm_frontdoor_firewall_policy.test.id + } +} +`, rInt, rString, location) +} diff --git a/azurerm/resource_arm_function_app.go b/azurerm/resource_arm_function_app.go index 489df8332d4c..699ca8651583 100644 --- a/azurerm/resource_arm_function_app.go +++ b/azurerm/resource_arm_function_app.go @@ -10,7 +10,10 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,7 +49,6 @@ func resourceArmFunctionApp() *schema.Resource { "app_service_plan_id": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "enabled": { @@ -71,6 +73,9 @@ func resourceArmFunctionApp() *schema.Resource { "app_settings": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "enable_builtin_logging": { @@ -110,7 +115,7 @@ func resourceArmFunctionApp() *schema.Resource { string(web.SQLAzure), string(web.SQLServer), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, }, @@ -126,7 +131,7 @@ func resourceArmFunctionApp() *schema.Resource { "type": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(web.ManagedServiceIdentityTypeSystemAssigned), }, true), @@ -143,7 +148,7 @@ func resourceArmFunctionApp() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "default_hostname": { Type: schema.TypeString, @@ -199,10 +204,17 @@ func resourceArmFunctionApp() *schema.Resource { Optional: true, Computed: true, }, + "virtual_network_name": { + Type: schema.TypeString, + Optional: true, + }, + "cors": azure.SchemaWebCorsSettings(), }, }, }, + "auth_settings": azure.SchemaAppServiceAuthSettings(), + "site_credential": { Type: schema.TypeList, Computed: true, @@ -226,7 +238,7 @@ func resourceArmFunctionApp() *schema.Resource { } func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicesClient + client := meta.(*ArmClient).web.AppServicesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Function App creation.") @@ -234,7 +246,7 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -266,7 +278,7 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro enabled := d.Get("enabled").(bool) clientAffinityEnabled := d.Get("client_affinity_enabled").(bool) httpsOnly := d.Get("https_only").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) appServiceTier, err := getFunctionAppServiceTier(ctx, appServicePlanID, meta) if err != nil { return err @@ -280,7 +292,7 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro siteEnvelope := web.Site{ Kind: &kind, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), SiteProperties: &web.SiteProperties{ ServerFarmID: utils.String(appServicePlanID), Enabled: utils.Bool(enabled), @@ -316,14 +328,25 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(*read.ID) + authSettingsRaw := d.Get("auth_settings").([]interface{}) + authSettings := azure.ExpandAppServiceAuthSettings(authSettingsRaw) + + auth := web.SiteAuthSettings{ + ID: read.ID, + SiteAuthSettingsProperties: &authSettings} + + if _, err := client.UpdateAuthSettings(ctx, resourceGroup, name, auth); err != nil { + return fmt.Errorf("Error updating auth settings for Function App %q (resource group %q): %+s", name, resourceGroup, err) + } + return resourceArmFunctionAppUpdate(d, meta) } func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicesClient + client := meta.(*ArmClient).web.AppServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -337,7 +360,7 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro enabled := d.Get("enabled").(bool) clientAffinityEnabled := d.Get("client_affinity_enabled").(bool) httpsOnly := d.Get("https_only").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) appServiceTier, err := getFunctionAppServiceTier(ctx, appServicePlanID, meta) @@ -346,12 +369,13 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro } basicAppSettings := getBasicFunctionAppAppSettings(d, appServiceTier) siteConfig := expandFunctionAppSiteConfig(d) + siteConfig.AppSettings = &basicAppSettings siteEnvelope := web.Site{ Kind: &kind, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), SiteProperties: &web.SiteProperties{ ServerFarmID: utils.String(appServicePlanID), Enabled: utils.Bool(enabled), @@ -396,6 +420,20 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro } } + if d.HasChange("auth_settings") { + authSettingsRaw := d.Get("auth_settings").([]interface{}) + authSettingsProperties := azure.ExpandAppServiceAuthSettings(authSettingsRaw) + id := d.Id() + authSettings := web.SiteAuthSettings{ + ID: &id, + SiteAuthSettingsProperties: &authSettingsProperties, + } + + if _, err := client.UpdateAuthSettings(ctx, resGroup, name, authSettings); err != nil { + return fmt.Errorf("Error updating Authentication Settings for Function App %q: %+v", name, err) + } + } + if d.HasChange("connection_string") { // update the ConnectionStrings connectionStrings := expandFunctionAppConnectionStrings(d) @@ -412,10 +450,10 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro } func resourceArmFunctionAppRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicesClient + client := meta.(*ArmClient).web.AppServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -456,10 +494,14 @@ func resourceArmFunctionAppRead(d *schema.ResourceData, meta interface{}) error if err != nil { return err } - siteCredResp, err := siteCredFuture.Result(client) + siteCredResp, err := siteCredFuture.Result(*client) if err != nil { return fmt.Errorf("Error making Read request on AzureRM App Service Site Credential %q: %+v", name, err) } + authResp, err := client.GetAuthSettings(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving the AuthSettings for Function App %q (Resource Group %q): %+v", name, resGroup, err) + } d.Set("name", name) d.Set("resource_group_name", resGroup) @@ -514,20 +556,23 @@ func resourceArmFunctionAppRead(d *schema.ResourceData, meta interface{}) error return err } + authSettings := azure.FlattenAppServiceAuthSettings(authResp.SiteAuthSettingsProperties) + if err := d.Set("auth_settings", authSettings); err != nil { + return fmt.Errorf("Error setting `auth_settings`: %s", err) + } + siteCred := flattenFunctionAppSiteCredential(siteCredResp.UserProperties) if err = d.Set("site_credential", siteCred); err != nil { return err } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmFunctionAppDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).appServicesClient + client := meta.(*ArmClient).web.AppServicesClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -587,14 +632,14 @@ func getBasicFunctionAppAppSettings(d *schema.ResourceData, appServiceTier strin } func getFunctionAppServiceTier(ctx context.Context, appServicePlanId string, meta interface{}) (string, error) { - id, err := parseAzureResourceID(appServicePlanId) + id, err := azure.ParseAzureResourceID(appServicePlanId) if err != nil { return "", fmt.Errorf("[ERROR] Unable to parse App Service Plan ID %q: %+v", appServicePlanId, err) } log.Printf("[DEBUG] Retrieving App Server Plan %s", id.Path["serverfarms"]) - appServicePlansClient := meta.(*ArmClient).appServicePlansClient + appServicePlansClient := meta.(*ArmClient).web.AppServicePlansClient appServicePlan, err := appServicePlansClient.Get(ctx, id.ResourceGroup, id.Path["serverfarms"]) if err != nil { return "", fmt.Errorf("[ERROR] Could not retrieve App Service Plan ID %q: %+v", appServicePlanId, err) @@ -645,6 +690,16 @@ func expandFunctionAppSiteConfig(d *schema.ResourceData) web.SiteConfig { siteConfig.LinuxFxVersion = utils.String(v.(string)) } + if v, ok := config["cors"]; ok { + corsSettings := v.(interface{}) + expand := azure.ExpandWebCorsSettings(corsSettings) + siteConfig.Cors = &expand + } + + if v, ok := config["virtual_network_name"]; ok { + siteConfig.VnetName = utils.String(v.(string)) + } + return siteConfig } @@ -673,6 +728,12 @@ func flattenFunctionAppSiteConfig(input *web.SiteConfig) []interface{} { result["linux_fx_version"] = *input.LinuxFxVersion } + if input.VnetName != nil { + result["virtual_network_name"] = *input.VnetName + } + + result["cors"] = azure.FlattenWebCorsSettings(input.Cors) + results = append(results, result) return results } diff --git a/azurerm/resource_arm_function_app_test.go b/azurerm/resource_arm_function_app_test.go index d56ca91ce42f..9c0f71afdd73 100644 --- a/azurerm/resource_arm_function_app_test.go +++ b/azurerm/resource_arm_function_app_test.go @@ -2,10 +2,12 @@ package azurerm import ( "fmt" + "os" "strings" "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -45,7 +47,7 @@ func TestAccAzureRMFunctionApp_basic(t *testing.T) { } func TestAccAzureRMFunctionApp_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -596,8 +598,104 @@ func TestAccAzureRMFunctionApp_updateLogging(t *testing.T) { }) } +func TestAccAzureRMFunctionApp_authSettings(t *testing.T) { + resourceName := "azurerm_function_app.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + tenantID := os.Getenv("ARM_TENANT_ID") + config := testAccAzureRMFunctionApp_authSettings(ri, rs, testLocation(), tenantID) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFunctionAppDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAppServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.issuer", fmt.Sprintf("https://sts.windows.net/%s", tenantID)), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.runtime_version", "1.0"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.unauthenticated_client_action", "RedirectToLoginPage"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.token_refresh_extension_hours", "75"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.token_store_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.additional_login_params.test_key", "test_value"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.allowed_external_redirect_urls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.allowed_external_redirect_urls.0", "https://terra.form"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.active_directory.0.client_id", "aadclientid"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.active_directory.0.client_secret", "aadsecret"), + resource.TestCheckResourceAttr(resourceName, "auth_settings.0.active_directory.0.allowed_audiences.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFunctionApp_corsSettings(t *testing.T) { + resourceName := "azurerm_function_app.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + config := testAccAzureRMFunctionApp_corsSettings(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFunctionAppDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFunctionAppExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "site_config.0.cors.#", "1"), + resource.TestCheckResourceAttr(resourceName, "site_config.0.cors.0.support_credentials", "true"), + resource.TestCheckResourceAttr(resourceName, "site_config.0.cors.0.allowed_origins.#", "4"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFunctionApp_vnetName(t *testing.T) { + resourceName := "azurerm_function_app.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + vnetName := strings.ToLower(acctest.RandString(11)) + config := testAccAzureRMFunctionApp_vnetName(ri, rs, testLocation(), vnetName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFunctionAppDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFunctionAppExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "site_config.0.virtual_network_name", vnetName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMFunctionAppDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).appServicesClient + client := testAccProvider.Meta().(*ArmClient).web.AppServicesClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_function_app" { @@ -636,7 +734,7 @@ func testCheckAzureRMFunctionAppExists(resourceName string) resource.TestCheckFu return fmt.Errorf("Bad: no resource group found in state for Function App: %s", functionAppName) } - client := testAccProvider.Meta().(*ArmClient).appServicesClient + client := testAccProvider.Meta().(*ArmClient).web.AppServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, functionAppName) if err != nil { @@ -665,7 +763,7 @@ func testCheckAzureRMFunctionAppHasContentShare(resourceName string) resource.Te return fmt.Errorf("Bad: no resource group found in state for Function App: %s", functionAppName) } - client := testAccProvider.Meta().(*ArmClient).appServicesClient + client := testAccProvider.Meta().(*ArmClient).web.AppServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext appSettingsResp, err := client.ListApplicationSettings(ctx, resourceGroup, functionAppName) @@ -697,7 +795,7 @@ func testCheckAzureRMFunctionAppHasNoContentShare(resourceName string) resource. return fmt.Errorf("Bad: no resource group found in state for Function App: %s", functionAppName) } - client := testAccProvider.Meta().(*ArmClient).appServicesClient + client := testAccProvider.Meta().(*ArmClient).web.AppServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext appSettingsResp, err := client.ListApplicationSettings(ctx, resourceGroup, functionAppName) @@ -1000,7 +1098,7 @@ resource "azurerm_function_app" "test" { resource_group_name = "${azurerm_resource_group.test.name}" app_service_plan_id = "${azurerm_app_service_plan.test.id}" storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" - + site_config { linux_fx_version = "DOCKER|(golang:latest)" } @@ -1130,7 +1228,7 @@ resource "azurerm_function_app" "test" { resource_group_name = "${azurerm_resource_group.test.name}" app_service_plan_id = "${azurerm_app_service_plan.test.id}" storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" - + app_settings = { "hello" = "world" } @@ -1179,7 +1277,7 @@ resource "azurerm_function_app" "test" { resource_group_name = "${azurerm_resource_group.test.name}" app_service_plan_id = "${azurerm_app_service_plan.test.id}" storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" - + app_settings = { "hello" = "world" } @@ -1425,3 +1523,154 @@ resource "azurerm_function_app" "test" { } `, rInt, location, storage) } + +func testAccAzureRMFunctionApp_authSettings(rInt int, storage string, location string, tenantID string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_function_app" "test" { + name = "acctest-%[1]d-func" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + app_service_plan_id = "${azurerm_app_service_plan.test.id}" + storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" + + auth_settings { + enabled = true + issuer = "https://sts.windows.net/%[4]s" + runtime_version = "1.0" + unauthenticated_client_action = "RedirectToLoginPage" + token_refresh_extension_hours = 75 + token_store_enabled = true + + additional_login_params = { + test_key = "test_value" + } + + allowed_external_redirect_urls = [ + "https://terra.form", + ] + + active_directory { + client_id = "aadclientid" + client_secret = "aadsecret" + + allowed_audiences = [ + "activedirectorytokenaudiences", + ] + } + } +} +`, rInt, location, storage, tenantID) +} + +func testAccAzureRMFunctionApp_corsSettings(rInt int, storage string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_function_app" "test" { + name = "acctest-%[1]d-func" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + app_service_plan_id = "${azurerm_app_service_plan.test.id}" + storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" + + site_config { + cors { + allowed_origins = [ + "http://www.contoso.com", + "www.contoso.com", + "contoso.com", + "http://localhost:4201", + ] + + support_credentials = true + } + } +} +`, rInt, location, storage) +} + +func testAccAzureRMFunctionApp_vnetName(rInt int, storage, location, vnetName string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_function_app" "test" { + name = "acctest-%[1]d-func" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + app_service_plan_id = "${azurerm_app_service_plan.test.id}" + storage_connection_string = "${azurerm_storage_account.test.primary_connection_string}" + + site_config { + virtual_network_name = "%[4]s" + } +} +`, rInt, location, storage, vnetName) +} diff --git a/azurerm/resource_arm_hdinsight_hadoop_cluster.go b/azurerm/resource_arm_hdinsight_hadoop_cluster.go index 89a80cc435aa..3ce3d2d1d988 100644 --- a/azurerm/resource_arm_hdinsight_hadoop_cluster.go +++ b/azurerm/resource_arm_hdinsight_hadoop_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -93,7 +95,7 @@ func resourceArmHDInsightHadoopCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -116,7 +118,7 @@ func resourceArmHDInsightHadoopClusterCreate(d *schema.ResourceData, meta interf resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -142,7 +144,7 @@ func resourceArmHDInsightHadoopClusterCreate(d *schema.ResourceData, meta interf return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -173,7 +175,7 @@ func resourceArmHDInsightHadoopClusterCreate(d *schema.ResourceData, meta interf Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -203,7 +205,7 @@ func resourceArmHDInsightHadoopClusterRead(d *schema.ResourceData, meta interfac configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -264,9 +266,7 @@ func resourceArmHDInsightHadoopClusterRead(d *schema.ResourceData, meta interfac d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightHadoopComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_hadoop_cluster_test.go b/azurerm/resource_arm_hdinsight_hadoop_cluster_test.go index c47a878ed337..5c5bc2d1440a 100644 --- a/azurerm/resource_arm_hdinsight_hadoop_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_hadoop_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightHadoopCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightHadoopCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightHadoopCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -356,20 +357,20 @@ resource "azurerm_hdinsight_hadoop_cluster" "test" { head_node { vm_size = "Standard_D3_v2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D4_v2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_D3_v2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } @@ -450,6 +451,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_hadoop_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -521,6 +523,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_hadoop_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" diff --git a/azurerm/resource_arm_hdinsight_hbase_cluster.go b/azurerm/resource_arm_hdinsight_hbase_cluster.go index 469d24b37126..f73c0ff3ccb9 100644 --- a/azurerm/resource_arm_hdinsight_hbase_cluster.go +++ b/azurerm/resource_arm_hdinsight_hbase_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -91,7 +93,7 @@ func resourceArmHDInsightHBaseCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -114,7 +116,7 @@ func resourceArmHDInsightHBaseClusterCreate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -140,7 +142,7 @@ func resourceArmHDInsightHBaseClusterCreate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -171,7 +173,7 @@ func resourceArmHDInsightHBaseClusterCreate(d *schema.ResourceData, meta interfa Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -201,7 +203,7 @@ func resourceArmHDInsightHBaseClusterRead(d *schema.ResourceData, meta interface configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -262,9 +264,7 @@ func resourceArmHDInsightHBaseClusterRead(d *schema.ResourceData, meta interface d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightHBaseComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_hbase_cluster_test.go b/azurerm/resource_arm_hdinsight_hbase_cluster_test.go index 163a16333c45..7a1f86ad45a7 100644 --- a/azurerm/resource_arm_hdinsight_hbase_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_hbase_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightHBaseCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightHBaseCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightHBaseCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -356,20 +357,20 @@ resource "azurerm_hdinsight_hbase_cluster" "test" { head_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } @@ -450,6 +451,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_hbase_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -521,6 +523,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_hbase_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" diff --git a/azurerm/resource_arm_hdinsight_interactive_query_cluster.go b/azurerm/resource_arm_hdinsight_interactive_query_cluster.go index 7ab6b23f6272..55edeece0951 100644 --- a/azurerm/resource_arm_hdinsight_interactive_query_cluster.go +++ b/azurerm/resource_arm_hdinsight_interactive_query_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -91,7 +93,7 @@ func resourceArmHDInsightInteractiveQueryCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -114,7 +116,7 @@ func resourceArmHDInsightInteractiveQueryClusterCreate(d *schema.ResourceData, m resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -140,7 +142,7 @@ func resourceArmHDInsightInteractiveQueryClusterCreate(d *schema.ResourceData, m return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -171,7 +173,7 @@ func resourceArmHDInsightInteractiveQueryClusterCreate(d *schema.ResourceData, m Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -201,7 +203,7 @@ func resourceArmHDInsightInteractiveQueryClusterRead(d *schema.ResourceData, met configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -262,9 +264,7 @@ func resourceArmHDInsightInteractiveQueryClusterRead(d *schema.ResourceData, met d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightInteractiveQueryComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_interactive_query_cluster_test.go b/azurerm/resource_arm_hdinsight_interactive_query_cluster_test.go index 9638ccb7b4e9..854f1d618a9a 100644 --- a/azurerm/resource_arm_hdinsight_interactive_query_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_interactive_query_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightInteractiveQueryCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightInteractiveQueryCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightInteractiveQueryCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -356,20 +357,20 @@ resource "azurerm_hdinsight_interactive_query_cluster" "test" { head_node { vm_size = "Standard_D13_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D14_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } diff --git a/azurerm/resource_arm_hdinsight_kafka_cluster.go b/azurerm/resource_arm_hdinsight_kafka_cluster.go index aad20f2dfeb9..9815d5f6b543 100644 --- a/azurerm/resource_arm_hdinsight_kafka_cluster.go +++ b/azurerm/resource_arm_hdinsight_kafka_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -92,7 +94,7 @@ func resourceArmHDInsightKafkaCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -115,7 +117,7 @@ func resourceArmHDInsightKafkaClusterCreate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -141,7 +143,7 @@ func resourceArmHDInsightKafkaClusterCreate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -172,7 +174,7 @@ func resourceArmHDInsightKafkaClusterCreate(d *schema.ResourceData, meta interfa Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -202,7 +204,7 @@ func resourceArmHDInsightKafkaClusterRead(d *schema.ResourceData, meta interface configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -263,9 +265,7 @@ func resourceArmHDInsightKafkaClusterRead(d *schema.ResourceData, meta interface d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightKafkaComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_kafka_cluster_test.go b/azurerm/resource_arm_hdinsight_kafka_cluster_test.go index 26f4cfd639ed..3ef595fdb477 100644 --- a/azurerm/resource_arm_hdinsight_kafka_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_kafka_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightKafkaCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightKafkaCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightKafkaCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -357,13 +358,13 @@ resource "azurerm_hdinsight_kafka_cluster" "test" { head_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 number_of_disks_per_node = 2 } @@ -371,7 +372,7 @@ resource "azurerm_hdinsight_kafka_cluster" "test" { zookeeper_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } @@ -453,6 +454,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_kafka_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -525,6 +527,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_kafka_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" diff --git a/azurerm/resource_arm_hdinsight_ml_services_cluster.go b/azurerm/resource_arm_hdinsight_ml_services_cluster.go index ff15e3582565..1dfbe778dd08 100644 --- a/azurerm/resource_arm_hdinsight_ml_services_cluster.go +++ b/azurerm/resource_arm_hdinsight_ml_services_cluster.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -95,7 +97,7 @@ func resourceArmHDInsightMLServicesCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "edge_ssh_endpoint": { Type: schema.TypeString, @@ -133,7 +135,7 @@ func resourceArmHDInsightMLServicesClusterCreate(d *schema.ResourceData, meta in resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) gatewayRaw := d.Get("gateway").([]interface{}) @@ -158,7 +160,7 @@ func resourceArmHDInsightMLServicesClusterCreate(d *schema.ResourceData, meta in return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -188,7 +190,7 @@ func resourceArmHDInsightMLServicesClusterCreate(d *schema.ResourceData, meta in Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -218,7 +220,7 @@ func resourceArmHDInsightMLServicesClusterRead(d *schema.ResourceData, meta inte configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -295,7 +297,5 @@ func resourceArmHDInsightMLServicesClusterRead(d *schema.ResourceData, meta inte d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/resource_arm_hdinsight_ml_services_cluster_test.go b/azurerm/resource_arm_hdinsight_ml_services_cluster_test.go index a61d8f9101b4..dcc59f01eab6 100644 --- a/azurerm/resource_arm_hdinsight_ml_services_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_ml_services_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightMLServicesCluster_basic(t *testing.T) { @@ -51,7 +52,7 @@ func TestAccAzureRMHDInsightMLServicesCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightMLServicesCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -374,26 +375,26 @@ resource "azurerm_hdinsight_ml_services_cluster" "test" { head_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } edge_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } diff --git a/azurerm/resource_arm_hdinsight_rserver_cluster.go b/azurerm/resource_arm_hdinsight_rserver_cluster.go index 937d94af3bff..ff829eb0c85f 100644 --- a/azurerm/resource_arm_hdinsight_rserver_cluster.go +++ b/azurerm/resource_arm_hdinsight_rserver_cluster.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -95,7 +97,7 @@ func resourceArmHDInsightRServerCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "edge_ssh_endpoint": { Type: schema.TypeString, @@ -133,7 +135,7 @@ func resourceArmHDInsightRServerClusterCreate(d *schema.ResourceData, meta inter resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) gatewayRaw := d.Get("gateway").([]interface{}) @@ -158,7 +160,7 @@ func resourceArmHDInsightRServerClusterCreate(d *schema.ResourceData, meta inter return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -188,7 +190,7 @@ func resourceArmHDInsightRServerClusterCreate(d *schema.ResourceData, meta inter Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -218,7 +220,7 @@ func resourceArmHDInsightRServerClusterRead(d *schema.ResourceData, meta interfa configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -295,7 +297,5 @@ func resourceArmHDInsightRServerClusterRead(d *schema.ResourceData, meta interfa d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } diff --git a/azurerm/resource_arm_hdinsight_rserver_cluster_test.go b/azurerm/resource_arm_hdinsight_rserver_cluster_test.go index 58fb1be1094b..983bfa626ac2 100644 --- a/azurerm/resource_arm_hdinsight_rserver_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_rserver_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightRServerCluster_basic(t *testing.T) { @@ -51,7 +52,7 @@ func TestAccAzureRMHDInsightRServerCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightRServerCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -374,26 +375,26 @@ resource "azurerm_hdinsight_rserver_cluster" "test" { head_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } edge_node { vm_size = "Standard_D3_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } diff --git a/azurerm/resource_arm_hdinsight_spark_cluster.go b/azurerm/resource_arm_hdinsight_spark_cluster.go index 71f6a997b68f..7998973a4b96 100644 --- a/azurerm/resource_arm_hdinsight_spark_cluster.go +++ b/azurerm/resource_arm_hdinsight_spark_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -91,7 +93,7 @@ func resourceArmHDInsightSparkCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -114,7 +116,7 @@ func resourceArmHDInsightSparkClusterCreate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -140,7 +142,7 @@ func resourceArmHDInsightSparkClusterCreate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -171,7 +173,7 @@ func resourceArmHDInsightSparkClusterCreate(d *schema.ResourceData, meta interfa Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -201,7 +203,7 @@ func resourceArmHDInsightSparkClusterRead(d *schema.ResourceData, meta interface configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -262,9 +264,7 @@ func resourceArmHDInsightSparkClusterRead(d *schema.ResourceData, meta interface d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightSparkComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_spark_cluster_test.go b/azurerm/resource_arm_hdinsight_spark_cluster_test.go index 0615882acf70..977fea952afe 100644 --- a/azurerm/resource_arm_hdinsight_spark_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_spark_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightSparkCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightSparkCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightSparkCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -279,13 +280,13 @@ resource "azurerm_hdinsight_spark_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" } worker_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 @@ -354,22 +355,22 @@ resource "azurerm_hdinsight_spark_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Medium" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } @@ -406,13 +407,13 @@ resource "azurerm_hdinsight_spark_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" } worker_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 5 @@ -450,6 +451,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_spark_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -475,7 +477,7 @@ resource "azurerm_hdinsight_spark_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" subnet_id = "${azurerm_subnet.test.id}" @@ -483,7 +485,7 @@ resource "azurerm_hdinsight_spark_cluster" "test" { } worker_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 @@ -521,6 +523,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_spark_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -546,7 +549,7 @@ resource "azurerm_hdinsight_spark_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" subnet_id = "${azurerm_subnet.test.id}" @@ -554,7 +557,7 @@ resource "azurerm_hdinsight_spark_cluster" "test" { } worker_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 diff --git a/azurerm/resource_arm_hdinsight_storm_cluster.go b/azurerm/resource_arm_hdinsight_storm_cluster.go index 0573ee82eb29..f2d1d2a2b8ac 100644 --- a/azurerm/resource_arm_hdinsight_storm_cluster.go +++ b/azurerm/resource_arm_hdinsight_storm_cluster.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -92,7 +94,7 @@ func resourceArmHDInsightStormCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "https_endpoint": { Type: schema.TypeString, @@ -115,7 +117,7 @@ func resourceArmHDInsightStormClusterCreate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) clusterVersion := d.Get("cluster_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) tier := hdinsight.Tier(d.Get("tier").(string)) componentVersionsRaw := d.Get("component_version").([]interface{}) @@ -141,7 +143,7 @@ func resourceArmHDInsightStormClusterCreate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error expanding `roles`: %+v", err) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -172,7 +174,7 @@ func resourceArmHDInsightStormClusterCreate(d *schema.ResourceData, meta interfa Roles: roles, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { @@ -202,7 +204,7 @@ func resourceArmHDInsightStormClusterRead(d *schema.ResourceData, meta interface configurationsClient := meta.(*ArmClient).hdinsight.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -263,9 +265,7 @@ func resourceArmHDInsightStormClusterRead(d *schema.ResourceData, meta interface d.Set("ssh_endpoint", sshEndpoint) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func expandHDInsightStormComponentVersion(input []interface{}) map[string]*string { diff --git a/azurerm/resource_arm_hdinsight_storm_cluster_test.go b/azurerm/resource_arm_hdinsight_storm_cluster_test.go index ea63e5a59601..7aaef8fd79bb 100644 --- a/azurerm/resource_arm_hdinsight_storm_cluster_test.go +++ b/azurerm/resource_arm_hdinsight_storm_cluster_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMHDInsightStormCluster_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMHDInsightStormCluster_basic(t *testing.T) { } func TestAccAzureRMHDInsightStormCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -279,13 +280,13 @@ resource "azurerm_hdinsight_storm_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" } worker_node { - vm_size = "Standard_DS3_V2" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 @@ -354,22 +355,22 @@ resource "azurerm_hdinsight_storm_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } worker_node { - vm_size = "Standard_D3_V2" + vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] target_instance_count = 3 } zookeeper_node { vm_size = "Standard_A4_V2" username = "acctestusrvm" - ssh_keys = [ "${var.ssh_key}" ] + ssh_keys = ["${var.ssh_key}"] } } } @@ -406,13 +407,13 @@ resource "azurerm_hdinsight_storm_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" } worker_node { - vm_size = "Standard_D3_V2" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 5 @@ -450,6 +451,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_storm_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -475,7 +477,7 @@ resource "azurerm_hdinsight_storm_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" subnet_id = "${azurerm_subnet.test.id}" @@ -483,7 +485,7 @@ resource "azurerm_hdinsight_storm_cluster" "test" { } worker_node { - vm_size = "Standard_D3_V2" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 @@ -521,6 +523,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.0.2.0/24" } + resource "azurerm_hdinsight_storm_cluster" "test" { name = "acctesthdi-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -546,7 +549,7 @@ resource "azurerm_hdinsight_storm_cluster" "test" { roles { head_node { - vm_size = "Standard_A3" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" subnet_id = "${azurerm_subnet.test.id}" @@ -554,7 +557,7 @@ resource "azurerm_hdinsight_storm_cluster" "test" { } worker_node { - vm_size = "Standard_D3_V2" + vm_size = "Standard_A4_V2" username = "acctestusrvm" password = "AccTestvdSC4daf986!" target_instance_count = 3 diff --git a/azurerm/resource_arm_image.go b/azurerm/resource_arm_image.go index 147797b59b7a..b2787a9a51ef 100644 --- a/azurerm/resource_arm_image.go +++ b/azurerm/resource_arm_image.go @@ -4,8 +4,11 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -58,7 +61,7 @@ func resourceArmImage() *schema.Resource { "os_type": { Type: schema.TypeString, Optional: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(compute.Linux), string(compute.Windows), @@ -68,7 +71,7 @@ func resourceArmImage() *schema.Resource { "os_state": { Type: schema.TypeString, Optional: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(compute.Generalized), string(compute.Specialized), @@ -79,7 +82,7 @@ func resourceArmImage() *schema.Resource { Type: schema.TypeString, Computed: true, Optional: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: azure.ValidateResourceID, }, @@ -95,7 +98,7 @@ func resourceArmImage() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(compute.None), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(compute.CachingTypesNone), string(compute.CachingTypesReadOnly), @@ -147,7 +150,7 @@ func resourceArmImage() *schema.Resource { string(compute.CachingTypesReadOnly), string(compute.CachingTypesReadWrite), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "size_gb": { @@ -160,13 +163,13 @@ func resourceArmImage() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmImageCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).imageClient + client := meta.(*ArmClient).compute.ImagesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Image creation.") @@ -175,7 +178,7 @@ func resourceArmImageCreateUpdate(d *schema.ResourceData, meta interface{}) erro resGroup := d.Get("resource_group_name").(string) zoneResilient := d.Get("zone_resilient").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -189,7 +192,7 @@ func resourceArmImageCreateUpdate(d *schema.ResourceData, meta interface{}) erro } location := azure.NormalizeLocation(d.Get("location").(string)) - expandedTags := expandTags(d.Get("tags").(map[string]interface{})) + expandedTags := tags.Expand(d.Get("tags").(map[string]interface{})) properties := compute.ImageProperties{} @@ -264,10 +267,10 @@ func resourceArmImageCreateUpdate(d *schema.ResourceData, meta interface{}) erro } func resourceArmImageRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).imageClient + client := meta.(*ArmClient).compute.ImagesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -307,16 +310,14 @@ func resourceArmImageRead(d *schema.ResourceData, meta interface{}) error { d.Set("zone_resilient", resp.StorageProfile.ZoneResilient) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmImageDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).imageClient + client := meta.(*ArmClient).compute.ImagesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -365,7 +366,9 @@ func flattenAzureRmImageDataDisks(diskImages *[]compute.ImageDataDisk) []interfa if disk.DiskSizeGB != nil { l["size_gb"] = *disk.DiskSizeGB } - l["lun"] = *disk.Lun + if v := disk.Lun; v != nil { + l["lun"] = *v + } if disk.ManagedDisk != nil && disk.ManagedDisk.ID != nil { l["managed_disk_id"] = *disk.ManagedDisk.ID } @@ -393,7 +396,6 @@ func expandAzureRmImageOsDisk(d *schema.ResourceData) (*compute.ImageOSDisk, err osState := compute.OperatingSystemStateTypes(v) osDisk.OsState = osState } - managedDiskID := config["managed_disk_id"].(string) if managedDiskID != "" { managedDisk := &compute.SubResource{ @@ -420,15 +422,15 @@ func expandAzureRmImageOsDisk(d *schema.ResourceData) (*compute.ImageOSDisk, err } func expandAzureRmImageDataDisks(d *schema.ResourceData) ([]compute.ImageDataDisk, error) { - disks := d.Get("data_disk").([]interface{}) dataDisks := make([]compute.ImageDataDisk, 0, len(disks)) for _, diskConfig := range disks { config := diskConfig.(map[string]interface{}) - managedDiskID := d.Get("managed_disk_id").(string) - blobURI := d.Get("blob_uri").(string) + managedDiskID := config["managed_disk_id"].(string) + + blobURI := config["blob_uri"].(string) lun := int32(config["lun"].(int)) dataDisk := compute.ImageDataDisk{ @@ -436,12 +438,12 @@ func expandAzureRmImageDataDisks(d *schema.ResourceData) ([]compute.ImageDataDis BlobURI: &blobURI, } - if size := d.Get("size_gb"); size != 0 { + if size := config["size_gb"]; size != 0 { diskSize := int32(size.(int)) dataDisk.DiskSizeGB = &diskSize } - if v := d.Get("caching").(string); v != "" { + if v := config["caching"].(string); v != "" { caching := compute.CachingTypes(v) dataDisk.Caching = caching } @@ -457,5 +459,4 @@ func expandAzureRmImageDataDisks(d *schema.ResourceData) ([]compute.ImageDataDis } return dataDisks, nil - } diff --git a/azurerm/resource_arm_image_test.go b/azurerm/resource_arm_image_test.go index 8967c5bd4361..69d913a3a955 100644 --- a/azurerm/resource_arm_image_test.go +++ b/azurerm/resource_arm_image_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "golang.org/x/crypto/ssh" ) @@ -96,7 +97,7 @@ func TestAccAzureRMImage_standaloneImageZoneRedundant(t *testing.T) { } func TestAccAzureRMImage_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -245,7 +246,7 @@ func TestAccAzureRMImageVMSS_customImageVMSSFromVHD(t *testing.T) { func testGeneralizeVMImage(resourceGroup string, vmName string, userName string, password string, hostName string, port string, location string) resource.TestCheckFunc { return func(s *terraform.State) error { armClient := testAccProvider.Meta().(*ArmClient) - vmClient := armClient.vmClient + vmClient := armClient.compute.VMClient ctx := armClient.StopContext normalizedLocation := azure.NormalizeLocation(location) @@ -309,7 +310,6 @@ func deprovisionVM(userName string, password string, hostName string, port strin func testCheckAzureRMImageExists(resourceName string, shouldExist bool) resource.TestCheckFunc { return func(s *terraform.State) error { - log.Printf("[INFO] testing MANAGED IMAGE EXISTS - BEGIN.") rs, ok := s.RootModule().Resources[resourceName] @@ -323,7 +323,7 @@ func testCheckAzureRMImageExists(resourceName string, shouldExist bool) resource return fmt.Errorf("Bad: no resource group found in state for image: %s", dName) } - client := testAccProvider.Meta().(*ArmClient).imageClient + client := testAccProvider.Meta().(*ArmClient).compute.ImagesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, dName, "") @@ -346,7 +346,7 @@ func testCheckAzureVMExists(sourceVM string, shouldExist bool) resource.TestChec return func(s *terraform.State) error { log.Printf("[INFO] testing MANAGED IMAGE VM EXISTS - BEGIN.") - client := testAccProvider.Meta().(*ArmClient).vmClient + client := testAccProvider.Meta().(*ArmClient).compute.VMClient ctx := testAccProvider.Meta().(*ArmClient).StopContext vmRs, vmOk := s.RootModule().Resources[sourceVM] if !vmOk { @@ -381,7 +381,7 @@ func testCheckAzureVMSSExists(sourceVMSS string, shouldExist bool) resource.Test return func(s *terraform.State) error { log.Printf("[INFO] testing MANAGED IMAGE VMSS EXISTS - BEGIN.") - vmssClient := testAccProvider.Meta().(*ArmClient).vmScaleSetClient + vmssClient := testAccProvider.Meta().(*ArmClient).compute.VMScaleSetClient ctx := testAccProvider.Meta().(*ArmClient).StopContext vmRs, vmOk := s.RootModule().Resources[sourceVMSS] if !vmOk { @@ -413,7 +413,7 @@ func testCheckAzureVMSSExists(sourceVMSS string, shouldExist bool) resource.Test } func testCheckAzureRMImageDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).diskClient + client := testAccProvider.Meta().(*ArmClient).compute.DisksClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -665,7 +665,6 @@ func testAccAzureRMImage_standaloneImage_requiresImport(rInt int, userName strin return fmt.Sprintf(` %s - resource "azurerm_image" "import" { name = "${azurerm_image.test.name}" location = "${azurerm_image.test.location}" diff --git a/azurerm/resource_arm_iot_dps.go b/azurerm/resource_arm_iot_dps.go index 79d335bc2ab1..dc16a1d68a56 100644 --- a/azurerm/resource_arm_iot_dps.go +++ b/azurerm/resource_arm_iot_dps.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "regexp" "strconv" "time" @@ -17,14 +18,16 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func resourceArmIotDPS() *schema.Resource { return &schema.Resource{ - Create: resourceArmIotDPSCreateOrUpdate, + Create: resourceArmIotDPSCreateUpdate, Read: resourceArmIotDPSRead, - Update: resourceArmIotDPSCreateOrUpdate, + Update: resourceArmIotDPSCreateUpdate, Delete: resourceArmIotDPSDelete, Importer: &schema.ResourceImporter{ @@ -39,7 +42,7 @@ func resourceArmIotDPS() *schema.Resource { ValidateFunc: validate.IoTHubName, }, - "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + "resource_group_name": azure.SchemaResourceGroupName(), // azure.SchemaResourceGroupNameDiffSuppress(), "location": azure.SchemaLocation(), @@ -84,19 +87,63 @@ func resourceArmIotDPS() *schema.Resource { }, }, - "tags": tagsSchema(), + "linked_hub": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connection_string": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + ForceNew: true, + // Azure returns the key as ****. We'll suppress that here. + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + secretKeyRegex := regexp.MustCompile("(SharedAccessKey)=[^;]+") + maskedNew := secretKeyRegex.ReplaceAllString(new, "$1=****") + return (new == d.Get(k).(string)) && (maskedNew == old) + }, + Sensitive: true, + }, + "location": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + StateFunc: azure.NormalizeLocation, + ForceNew: true, + }, + "apply_allocation_policy": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "allocation_weight": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntBetween(0, 1000), + }, + "hostname": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "tags": tags.Schema(), }, } } -func resourceArmIotDPSCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceArmIotDPSCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).iothub.DPSResourceClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, name, resourceGroup) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -110,11 +157,13 @@ func resourceArmIotDPSCreateOrUpdate(d *schema.ResourceData, meta interface{}) e } iotdps := iothub.ProvisioningServiceDescription{ - Location: utils.String(d.Get("location").(string)), - Name: utils.String(name), - Sku: expandIoTDPSSku(d), - Properties: &iothub.IotDpsPropertiesDescription{}, - Tags: expandTags(d.Get("tags").(map[string]interface{})), + Location: utils.String(d.Get("location").(string)), + Name: utils.String(name), + Sku: expandIoTDPSSku(d), + Properties: &iothub.IotDpsPropertiesDescription{ + IotHubs: expandIoTDPSIoTHubs(d.Get("linked_hub").([]interface{})), + }, + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, iotdps) @@ -144,7 +193,7 @@ func resourceArmIotDPSRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).iothub.DPSResourceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -170,23 +219,28 @@ func resourceArmIotDPSRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("sku", sku); err != nil { return fmt.Errorf("Error setting `sku`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - return nil + if props := resp.Properties; props != nil { + if err := d.Set("linked_hub", flattenIoTDPSLinkedHub(props.IotHubs)); err != nil { + return fmt.Errorf("Error setting `linked_hub`: %+v", err) + } + } + + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmIotDPSDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).iothub.DPSResourceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } resourceGroup := id.ResourceGroup name := id.Path["provisioningServices"] - future, err := client.Delete(ctx, resourceGroup, name) + future, err := client.Delete(ctx, name, resourceGroup) if err != nil { if !response.WasNotFound(future.Response()) { return fmt.Errorf("Error deleting IoT Device Provisioning Service %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -196,7 +250,7 @@ func resourceArmIotDPSDelete(d *schema.ResourceData, meta interface{}) error { return waitForIotDPSToBeDeleted(ctx, client, resourceGroup, name) } -func waitForIotDPSToBeDeleted(ctx context.Context, client iothub.IotDpsResourceClient, resourceGroup, name string) error { +func waitForIotDPSToBeDeleted(ctx context.Context, client *iothub.IotDpsResourceClient, resourceGroup, name string) error { // we can't use the Waiter here since the API returns a 404 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for IoT Device Provisioning Service %q (Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ @@ -212,9 +266,9 @@ func waitForIotDPSToBeDeleted(ctx context.Context, client iothub.IotDpsResourceC return nil } -func iotdpsStateStatusCodeRefreshFunc(ctx context.Context, client iothub.IotDpsResourceClient, resourceGroup, name string) resource.StateRefreshFunc { +func iotdpsStateStatusCodeRefreshFunc(ctx context.Context, client *iothub.IotDpsResourceClient, resourceGroup, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, resourceGroup, name) + res, err := client.Get(ctx, name, resourceGroup) log.Printf("Retrieving IoT Device Provisioning Service %q (Resource Group %q) returned Status %d", resourceGroup, name, res.StatusCode) @@ -244,6 +298,24 @@ func expandIoTDPSSku(d *schema.ResourceData) *iothub.IotDpsSkuInfo { } } +func expandIoTDPSIoTHubs(input []interface{}) *[]iothub.DefinitionDescription { + linkedHubs := make([]iothub.DefinitionDescription, 0) + + for _, attr := range input { + linkedHubConfig := attr.(map[string]interface{}) + linkedHub := iothub.DefinitionDescription{ + ConnectionString: utils.String(linkedHubConfig["connection_string"].(string)), + AllocationWeight: utils.Int32(int32(linkedHubConfig["allocation_weight"].(int))), + ApplyAllocationPolicy: utils.Bool(linkedHubConfig["apply_allocation_policy"].(bool)), + Location: utils.String(linkedHubConfig["location"].(string)), + } + + linkedHubs = append(linkedHubs, linkedHub) + } + + return &linkedHubs +} + func flattenIoTDPSSku(input *iothub.IotDpsSkuInfo) []interface{} { output := make(map[string]interface{}) @@ -255,3 +327,34 @@ func flattenIoTDPSSku(input *iothub.IotDpsSkuInfo) []interface{} { return []interface{}{output} } + +func flattenIoTDPSLinkedHub(input *[]iothub.DefinitionDescription) []interface{} { + linkedHubs := make([]interface{}, 0) + if input == nil { + return linkedHubs + } + + for _, attr := range *input { + linkedHub := make(map[string]interface{}) + + if attr.Name != nil { + linkedHub["hostname"] = *attr.Name + } + if attr.ApplyAllocationPolicy != nil { + linkedHub["apply_allocation_policy"] = *attr.ApplyAllocationPolicy + } + if attr.AllocationWeight != nil { + linkedHub["allocation_weight"] = *attr.AllocationWeight + } + if attr.ConnectionString != nil { + linkedHub["connection_string"] = *attr.ConnectionString + } + if attr.Location != nil { + linkedHub["location"] = *attr.Location + } + + linkedHubs = append(linkedHubs, linkedHub) + } + + return linkedHubs +} diff --git a/azurerm/resource_arm_iot_dps_cerificate.go b/azurerm/resource_arm_iot_dps_cerificate.go new file mode 100644 index 000000000000..270e78915a87 --- /dev/null +++ b/azurerm/resource_arm_iot_dps_cerificate.go @@ -0,0 +1,155 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/Azure/azure-sdk-for-go/services/provisioningservices/mgmt/2018-01-22/iothub" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmIotDPSCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceArmIotDPSCertificateCreateUpdate, + Read: resourceArmIotDPSCertificateRead, + Update: resourceArmIotDPSCertificateCreateUpdate, + Delete: resourceArmIotDPSCertificateDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.IoTHubName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "iot_dps_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.IoTHubName, + }, + + "certificate_content": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + Sensitive: true, + }, + }, + } +} + +func resourceArmIotDPSCertificateCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).iothub.DPSCertificateClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + iotDPSName := d.Get("iot_dps_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_iot_dps_certificate", *existing.ID) + } + } + + certificate := iothub.CertificateBodyDescription{ + Certificate: utils.String(d.Get("certificate_content").(string)), + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, iotDPSName, name, certificate, ""); err != nil { + return fmt.Errorf("Error creating/updating IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + + resp, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + if err != nil { + return fmt.Errorf("Error retrieving IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmIotDPSCertificateRead(d, meta) +} + +func resourceArmIotDPSCertificateRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).iothub.DPSCertificateClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + iotDPSName := id.Path["provisioningServices"] + name := id.Path["certificates"] + + resp, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("iot_dps_name", iotDPSName) + // We are unable to set `certificate_content` since it is not returned from the API + + return nil +} + +func resourceArmIotDPSCertificateDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).iothub.DPSCertificateClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + iotDPSName := id.Path["provisioningServices"] + name := id.Path["certificates"] + + resp, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return fmt.Errorf("Error retrieving IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + + if resp.Etag == nil { + return fmt.Errorf("Error deleting IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q) because Etag is nil", name, iotDPSName, resourceGroup) + } + + // TODO address this delete call if https://github.com/Azure/azure-rest-api-specs/pull/6311 get's merged + if _, err := client.Delete(ctx, resourceGroup, *resp.Etag, iotDPSName, name, "", nil, nil, iothub.ServerAuthentication, nil, nil, nil, ""); err != nil { + return fmt.Errorf("Error deleting IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q): %+v", name, iotDPSName, resourceGroup, err) + } + return nil +} diff --git a/azurerm/resource_arm_iot_dps_certificate_test.go b/azurerm/resource_arm_iot_dps_certificate_test.go new file mode 100644 index 000000000000..a86994a287c5 --- /dev/null +++ b/azurerm/resource_arm_iot_dps_certificate_test.go @@ -0,0 +1,242 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMIotDPSCertificate_basic(t *testing.T) { + resourceName := "azurerm_iot_dps_certificate.test" + rInt := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMIotDPSCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMIotDPSCertificate_basic(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSCertificateExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_content", + }, + }, + }, + }) +} + +func TestAccAzureRMIotDPSCertificate_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_iot_dps_certificate.test" + rInt := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMIotDPSCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMIotDPSCertificate_basic(rInt, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSCertificateExists(resourceName), + ), + }, + { + Config: testAccAzureRMIotDPSCertificate_requiresImport(rInt, location), + ExpectError: testRequiresImportError("azurerm_iotdps"), + }, + }, + }) +} + +func TestAccAzureRMIotDPSCertificate_update(t *testing.T) { + resourceName := "azurerm_iot_dps_certificate.test" + rInt := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMIotDPSCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMIotDPSCertificate_basic(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSCertificateExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_content", + }, + }, + { + Config: testAccAzureRMIotDPSCertificate_update(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSCertificateExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "certificate_content", + }, + }, + }, + }) +} + +func testCheckAzureRMIotDPSCertificateDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).iothub.DPSCertificateClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_iot_dps_certificate" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + iotDPSName := rs.Primary.Attributes["iot_dps_name"] + + resp, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("IoT Device Provisioning Service Certificate %s still exists in (device provisioning service %s / resource group %s)", name, iotDPSName, resourceGroup) + } + } + return nil +} + +func testCheckAzureRMIotDPSCertificateExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + iotDPSName := rs.Primary.Attributes["iot_dps_name"] + + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for IoT Device Provisioning Service Certificate: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).iothub.DPSCertificateClient + resp, err := client.Get(ctx, name, resourceGroup, iotDPSName, "") + if err != nil { + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: IoT Device Provisioning Service Certificate %q (Device Provisioning Service %q / Resource Group %q) does not exist", name, iotDPSName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on iothubDPSCertificateClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMIotDPSCertificate_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iot_dps" "test" { + name = "acctestIoTDPS-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "S1" + tier = "Standard" + capacity = "1" + } +} + +resource "azurerm_iot_dps_certificate" "test" { + name = "acctestIoTDPSCertificate-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + iot_dps_name = "${azurerm_iot_dps.test.name}" + + certificate_content = "${filebase64("testdata/batch_certificate.cer")}" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMIotDPSCertificate_requiresImport(rInt int, location string) string { + template := testAccAzureRMIotDPS_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_iot_dps_certificate" "test" { + name = "${azurerm_iot_dps_certificate.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + iot_dps_name = "${azurerm_iot_dps.test.name}" + + certificate_content = "${filebase64("testdata/batch_certificate.cer")}" +} +`, template) +} + +func testAccAzureRMIotDPSCertificate_update(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iot_dps" "test" { + name = "acctestIoTDPS-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "S1" + tier = "Standard" + capacity = "1" + } + + tags = { + purpose = "testing" + } +} + +resource "azurerm_iot_dps_certificate" "test" { + name = "acctestIoTDPSCertificate-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + iot_dps_name = "${azurerm_iot_dps.test.name}" + + certificate_content = "${filebase64("testdata/application_gateway_test.cer")}" +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_iot_dps_test.go b/azurerm/resource_arm_iot_dps_test.go index 9f0b27396ecb..aee48e7002c3 100644 --- a/azurerm/resource_arm_iot_dps_test.go +++ b/azurerm/resource_arm_iot_dps_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMIotDPS_basic(t *testing.T) { @@ -35,12 +36,12 @@ func TestAccAzureRMIotDPS_basic(t *testing.T) { } func TestAccAzureRMIotDPS_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } - resourceName := "azurerm_iotdps.test" + resourceName := "azurerm_iot_dps.test" rInt := tf.AccRandTimeInt() location := testLocation() @@ -98,6 +99,41 @@ func TestAccAzureRMIotDPS_update(t *testing.T) { }) } +func TestAccAzureRMIotDPS_linkedHubs(t *testing.T) { + resourceName := "azurerm_iot_dps.test" + rInt := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMIotDPSDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMIotDPS_linkedHubs(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMIotDPS_linkedHubsUpdated(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotDPSExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMIotDPSDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).iothub.DPSResourceClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -148,7 +184,6 @@ func testCheckAzureRMIotDPSExists(resourceName string) resource.TestCheckFunc { } return nil - } } @@ -163,12 +198,12 @@ resource "azurerm_iot_dps" "test" { name = "acctestIoTDPS-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" - + sku { name = "S1" tier = "Standard" capacity = "1" - } + } } `, rInt, location, rInt) } @@ -182,7 +217,7 @@ resource "azurerm_iot_dps" "import" { name = "${azurerm_iot_dps.test.name}" resource_group_name = "${azurerm_iot_dps.test.name}" location = "${azurerm_iot_dps.test.location}" - + sku { name = "S1" tier = "Standard" @@ -203,7 +238,7 @@ resource "azurerm_iot_dps" "test" { name = "acctestIoTDPS-%d" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" - + sku { name = "S1" tier = "Standard" @@ -216,3 +251,63 @@ resource "azurerm_iot_dps" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMIotDPS_linkedHubs(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iot_dps" "test" { + name = "acctestIoTDPS-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "S1" + tier = "Standard" + capacity = "1" + } + + linked_hub { + connection_string = "HostName=test.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=booo" + location = "${azurerm_resource_group.test.location}" + allocation_weight = 15 + apply_allocation_policy = true + } + + linked_hub { + connection_string = "HostName=test2.azure-devices.net;SharedAccessKeyName=iothubowner2;SharedAccessKey=key2" + location = "${azurerm_resource_group.test.location}" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMIotDPS_linkedHubsUpdated(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iot_dps" "test" { + name = "acctestIoTDPS-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "S1" + tier = "Standard" + capacity = "1" + } + + linked_hub { + connection_string = "HostName=test.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=booo" + location = "${azurerm_resource_group.test.location}" + allocation_weight = 150 + } +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_iothub.go b/azurerm/resource_arm_iothub.go old mode 100755 new mode 100644 index 40c5e084cd9c..5176b4533440 --- a/azurerm/resource_arm_iothub.go +++ b/azurerm/resource_arm_iothub.go @@ -19,6 +19,9 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -161,6 +164,64 @@ func resourceArmIotHub() *schema.Resource { }, }, + "file_upload": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connection_string": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + secretKeyRegex := regexp.MustCompile("(SharedAccessKey|AccountKey)=[^;]+") + sbProtocolRegex := regexp.MustCompile("sb://([^:]+)(:5671)?/;") + + // Azure will always mask the Access Keys and will include the port number in the GET response + // 5671 is the default port for Azure Service Bus connections + maskedNew := sbProtocolRegex.ReplaceAllString(new, "sb://$1:5671/;") + maskedNew = secretKeyRegex.ReplaceAllString(maskedNew, "$1=****") + return (new == d.Get(k).(string)) && (maskedNew == old) + }, + Sensitive: true, + }, + "container_name": { + Type: schema.TypeString, + Required: true, + }, + "notifications": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "max_delivery_count": { + Type: schema.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + "sas_ttl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, + "default_ttl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, + "lock_duration": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, + }, + }, + }, + "endpoint": { Type: schema.TypeList, Optional: true, @@ -353,10 +414,9 @@ func resourceArmIotHub() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } - } func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) error { @@ -367,10 +427,10 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - azureRMLockByName(name, iothubResourceName) - defer azureRMUnlockByName(name, iothubResourceName) + locks.ByName(name, iothubResourceName) + defer locks.UnlockByName(name, iothubResourceName) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -399,7 +459,7 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err location := azure.NormalizeLocation(d.Get("location").(string)) skuInfo := expandIoTHubSku(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) fallbackRoute := expandIoTHubFallbackRoute(d) endpoints, err := expandIoTHubEndpoints(d, subscriptionID) @@ -407,6 +467,11 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error expanding `endpoint`: %+v", err) } + storageEndpoints, messagingEndpoints, enableFileUploadNotifications, err := expandIoTHubFileUpload(d) + if err != nil { + return fmt.Errorf("Error expanding `file_upload`: %+v", err) + } + routes := expandIoTHubRoutes(d) ipFilterRules := expandIPFilterRules(d) @@ -421,8 +486,11 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err Routes: routes, FallbackRoute: fallbackRoute, }, + StorageEndpoints: storageEndpoints, + MessagingEndpoints: messagingEndpoints, + EnableFileUploadNotifications: &enableFileUploadNotifications, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, properties, "") @@ -448,7 +516,7 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).iothub.ResourceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -479,7 +547,6 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error { } if properties := hub.Properties; properties != nil { - for k, v := range properties.EventHubEndpoints { if v == nil { continue @@ -492,7 +559,6 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error { d.Set("event_hub_operations_endpoint", v.Endpoint) d.Set("event_hub_operations_path", v.Path) } - } d.Set("hostname", properties.HostName) @@ -517,6 +583,10 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `ip_filter_rule` in IoTHub %q: %+v", name, err) } + fileUpload := flattenIoTHubFileUpload(properties.StorageEndpoints, properties.MessagingEndpoints, properties.EnableFileUploadNotifications) + if err := d.Set("file_upload", fileUpload); err != nil { + return fmt.Errorf("Error setting `file_upload` in IoTHub %q: %+v", name, err) + } } d.Set("name", name) @@ -529,13 +599,11 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `sku`: %+v", err) } d.Set("type", hub.Type) - flattenAndSetTags(d, hub.Tags) - - return nil + return tags.FlattenAndSet(d, hub.Tags) } func resourceArmIotHubDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -546,8 +614,8 @@ func resourceArmIotHubDelete(d *schema.ResourceData, meta interface{}) error { name := id.Path["IotHubs"] resourceGroup := id.ResourceGroup - azureRMLockByName(name, iothubResourceName) - defer azureRMUnlockByName(name, iothubResourceName) + locks.ByName(name, iothubResourceName) + defer locks.UnlockByName(name, iothubResourceName) future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -560,7 +628,7 @@ func resourceArmIotHubDelete(d *schema.ResourceData, meta interface{}) error { return waitForIotHubToBeDeleted(ctx, client, resourceGroup, name) } -func waitForIotHubToBeDeleted(ctx context.Context, client devices.IotHubResourceClient, resourceGroup, name string) error { +func waitForIotHubToBeDeleted(ctx context.Context, client *devices.IotHubResourceClient, resourceGroup, name string) error { // we can't use the Waiter here since the API returns a 404 once it's deleted which is considered a polling status code.. log.Printf("[DEBUG] Waiting for IotHub (%q in Resource Group %q) to be deleted", name, resourceGroup) stateConf := &resource.StateChangeConf{ @@ -576,7 +644,7 @@ func waitForIotHubToBeDeleted(ctx context.Context, client devices.IotHubResource return nil } -func iothubStateStatusCodeRefreshFunc(ctx context.Context, client devices.IotHubResourceClient, resourceGroup, name string) resource.StateRefreshFunc { +func iothubStateStatusCodeRefreshFunc(ctx context.Context, client *devices.IotHubResourceClient, resourceGroup, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroup, name) @@ -621,6 +689,40 @@ func expandIoTHubRoutes(d *schema.ResourceData) *[]devices.RouteProperties { return &routeProperties } +func expandIoTHubFileUpload(d *schema.ResourceData) (map[string]*devices.StorageEndpointProperties, map[string]*devices.MessagingEndpointProperties, bool, error) { + fileUploadList := d.Get("file_upload").([]interface{}) + + storageEndpointProperties := make(map[string]*devices.StorageEndpointProperties) + messagingEndpointProperties := make(map[string]*devices.MessagingEndpointProperties) + notifications := false + + if len(fileUploadList) > 0 { + fileUploadMap := fileUploadList[0].(map[string]interface{}) + + connectionStr := fileUploadMap["connection_string"].(string) + containerName := fileUploadMap["container_name"].(string) + notifications = fileUploadMap["notifications"].(bool) + maxDeliveryCount := int32(fileUploadMap["max_delivery_count"].(int)) + sasTTL := fileUploadMap["sas_ttl"].(string) + defaultTTL := fileUploadMap["default_ttl"].(string) + lockDuration := fileUploadMap["lock_duration"].(string) + + storageEndpointProperties["$default"] = &devices.StorageEndpointProperties{ + SasTTLAsIso8601: &sasTTL, + ConnectionString: &connectionStr, + ContainerName: &containerName, + } + + messagingEndpointProperties["fileNotifications"] = &devices.MessagingEndpointProperties{ + LockDurationAsIso8601: &lockDuration, + TTLAsIso8601: &defaultTTL, + MaxDeliveryCount: &maxDeliveryCount, + } + } + + return storageEndpointProperties, messagingEndpointProperties, notifications, nil +} + func expandIoTHubEndpoints(d *schema.ResourceData, subscriptionId string) (*devices.RoutingEndpoints, error) { routeEndpointList := d.Get("endpoint").([]interface{}) @@ -770,11 +872,47 @@ func flattenIoTHubSharedAccessPolicy(input *[]devices.SharedAccessSignatureAutho return results } +func flattenIoTHubFileUpload(storageEndpoints map[string]*devices.StorageEndpointProperties, messagingEndpoints map[string]*devices.MessagingEndpointProperties, enableFileUploadNotifications *bool) []interface{} { + results := make([]interface{}, 0) + output := make(map[string]interface{}) + + if storageEndpointProperties, ok := storageEndpoints["$default"]; ok { + if connString := storageEndpointProperties.ConnectionString; connString != nil { + output["connection_string"] = *connString + } + if containerName := storageEndpointProperties.ContainerName; containerName != nil { + output["container_name"] = *containerName + } + if sasTTLAsIso8601 := storageEndpointProperties.SasTTLAsIso8601; sasTTLAsIso8601 != nil { + output["sas_ttl"] = *sasTTLAsIso8601 + } + + if messagingEndpointProperties, ok := messagingEndpoints["fileNotifications"]; ok { + if lockDurationAsIso8601 := messagingEndpointProperties.LockDurationAsIso8601; lockDurationAsIso8601 != nil { + output["lock_duration"] = *lockDurationAsIso8601 + } + if ttlAsIso8601 := messagingEndpointProperties.TTLAsIso8601; ttlAsIso8601 != nil { + output["default_ttl"] = *ttlAsIso8601 + } + if maxDeliveryCount := messagingEndpointProperties.MaxDeliveryCount; maxDeliveryCount != nil { + output["max_delivery_count"] = *maxDeliveryCount + } + } + + if enableFileUploadNotifications != nil { + output["notifications"] = *enableFileUploadNotifications + } + + results = append(results, output) + } + + return results +} + func flattenIoTHubEndpoint(input *devices.RoutingProperties) []interface{} { results := make([]interface{}, 0) if input != nil && input.Endpoints != nil { - if containers := input.Endpoints.StorageContainers; containers != nil { for _, container := range *containers { output := make(map[string]interface{}) diff --git a/azurerm/resource_arm_iothub_consumer_group.go b/azurerm/resource_arm_iothub_consumer_group.go index 1c6e8603d2e8..fc00ce1c6440 100644 --- a/azurerm/resource_arm_iothub_consumer_group.go +++ b/azurerm/resource_arm_iothub_consumer_group.go @@ -8,6 +8,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -56,7 +57,7 @@ func resourceArmIotHubConsumerGroupCreate(d *schema.ResourceData, meta interface endpointName := d.Get("eventhub_endpoint_name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -91,7 +92,7 @@ func resourceArmIotHubConsumerGroupRead(d *schema.ResourceData, meta interface{} client := meta.(*ArmClient).iothub.ResourceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -122,7 +123,7 @@ func resourceArmIotHubConsumerGroupDelete(d *schema.ResourceData, meta interface client := meta.(*ArmClient).iothub.ResourceClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_iothub_consumer_group_test.go b/azurerm/resource_arm_iothub_consumer_group_test.go index ea84023a732d..720d652c1e35 100644 --- a/azurerm/resource_arm_iothub_consumer_group_test.go +++ b/azurerm/resource_arm_iothub_consumer_group_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMIotHubConsumerGroup_events(t *testing.T) { @@ -37,7 +38,7 @@ func TestAccAzureRMIotHubConsumerGroup_events(t *testing.T) { } func TestAccAzureRMIotHubConsumerGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -143,7 +144,6 @@ func testCheckAzureRMIotHubConsumerGroupExists(resourceName string) resource.Tes } return nil - } } diff --git a/azurerm/resource_arm_iothub_shared_access_policy.go b/azurerm/resource_arm_iothub_shared_access_policy.go index c6c9e9f1dcb5..e52c22c6249d 100644 --- a/azurerm/resource_arm_iothub_shared_access_policy.go +++ b/azurerm/resource_arm_iothub_shared_access_policy.go @@ -13,6 +13,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -124,8 +126,8 @@ func resourceArmIotHubSharedAccessPolicyCreateUpdate(d *schema.ResourceData, met iothubName := d.Get("iothub_name").(string) resourceGroup := d.Get("resource_group_name").(string) - azureRMLockByName(iothubName, iothubResourceName) - defer azureRMUnlockByName(iothubName, iothubResourceName) + locks.ByName(iothubName, iothubResourceName) + defer locks.UnlockByName(iothubName, iothubResourceName) iothub, err := client.Get(ctx, resourceGroup, iothubName) if err != nil { @@ -155,7 +157,7 @@ func resourceArmIotHubSharedAccessPolicyCreateUpdate(d *schema.ResourceData, met existingAccessPolicy := accessPolicyIterator.Value() if strings.EqualFold(*existingAccessPolicy.KeyName, keyName) { - if d.IsNewResource() && requireResourcesToBeImported { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_iothub_shared_access_policy", resourceId) } accessPolicies = append(accessPolicies, expandedAccessPolicy) @@ -191,7 +193,7 @@ func resourceArmIotHubSharedAccessPolicyRead(d *schema.ResourceData, meta interf client := meta.(*ArmClient).iothub.ResourceClient ctx := meta.(*ArmClient).StopContext - parsedIothubSAPId, err := parseAzureResourceID(d.Id()) + parsedIothubSAPId, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err @@ -243,7 +245,7 @@ func resourceArmIotHubSharedAccessPolicyDelete(d *schema.ResourceData, meta inte client := meta.(*ArmClient).iothub.ResourceClient ctx := meta.(*ArmClient).StopContext - parsedIothubSAPId, err := parseAzureResourceID(d.Id()) + parsedIothubSAPId, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err @@ -253,8 +255,8 @@ func resourceArmIotHubSharedAccessPolicyDelete(d *schema.ResourceData, meta inte iothubName := parsedIothubSAPId.Path["IotHubs"] keyName := parsedIothubSAPId.Path["IotHubKeys"] - azureRMLockByName(iothubName, iothubResourceName) - defer azureRMUnlockByName(iothubName, iothubResourceName) + locks.ByName(iothubName, iothubResourceName) + defer locks.UnlockByName(iothubName, iothubResourceName) iothub, err := client.Get(ctx, resourceGroup, iothubName) if err != nil { @@ -300,7 +302,6 @@ type accessRights struct { } func expandAccessRights(d *schema.ResourceData) string { - var possibleAccessRights = []struct { schema string right string diff --git a/azurerm/resource_arm_iothub_shared_access_policy_test.go b/azurerm/resource_arm_iothub_shared_access_policy_test.go index 8da3fda6c814..c4605491e47b 100644 --- a/azurerm/resource_arm_iothub_shared_access_policy_test.go +++ b/azurerm/resource_arm_iothub_shared_access_policy_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -59,7 +60,7 @@ func TestAccAzureRMIotHubSharedAccessPolicy_writeWithoutRead(t *testing.T) { } func TestAccAzureRMIotHubSharedAccessPolicy_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -199,7 +200,6 @@ func testCheckAzureRMIotHubSharedAccessPolicyExists(resourceName string) resourc } return fmt.Errorf("Bad: No shared access policy %s defined for IotHub %s", keyName, iothubName) - } } diff --git a/azurerm/resource_arm_iothub_test.go b/azurerm/resource_arm_iothub_test.go index 1306c3920320..738dd9ef7a67 100644 --- a/azurerm/resource_arm_iothub_test.go +++ b/azurerm/resource_arm_iothub_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMIotHub_basic(t *testing.T) { @@ -60,7 +61,7 @@ func TestAccAzureRMIotHub_ipFilterRules(t *testing.T) { } func TestAccAzureRMIotHub_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -141,6 +142,33 @@ func TestAccAzureRMIotHub_customRoutes(t *testing.T) { }) } +func TestAccAzureRMIotHub_fileUpload(t *testing.T) { + resourceName := "azurerm_iothub.test" + rInt := tf.AccRandTimeInt() + rStr := acctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMIotHubDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMIotHub_fileUpload(rInt, rStr, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMIotHubExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "file_upload.#", "1"), + resource.TestCheckResourceAttr(resourceName, "file_upload.0.lock_duration", "PT5M"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMIotHub_fallbackRoute(t *testing.T) { resourceName := "azurerm_iothub.test" rInt := tf.AccRandTimeInt() @@ -218,7 +246,6 @@ func testCheckAzureRMIotHubExists(resourceName string) resource.TestCheckFunc { } return nil - } } @@ -311,13 +338,13 @@ resource "azurerm_iothub" "test" { name = "S1" tier = "Standard" capacity = "1" - } - - ip_filter_rule { - name = "test" - ip_mask = "10.0.0.0/31" - action = "Accept" - } + } + + ip_filter_rule { + name = "test" + ip_mask = "10.0.0.0/31" + action = "Accept" + } tags = { purpose = "testing" @@ -368,7 +395,7 @@ resource "azurerm_eventhub_authorization_rule" "test" { namespace_name = "${azurerm_eventhub_namespace.test.name}" eventhub_name = "${azurerm_eventhub.test.name}" name = "acctest" - send = true + send = true } resource "azurerm_iothub" "test" { @@ -430,9 +457,9 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_iothub" "test" { - name = "acctestIoTHub-%d" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" + name = "acctestIoTHub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" sku { name = "S1" @@ -446,9 +473,55 @@ resource "azurerm_iothub" "test" { enabled = true } - tags = { + tags = { purpose = "testing" } } `, rInt, location, rInt) } + +func testAccAzureRMIotHub_fileUpload(rInt int, rStr string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "test" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_iothub" "test" { + name = "acctestIoTHub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "S1" + tier = "Standard" + capacity = "1" + } + + file_upload { + connection_string = "${azurerm_storage_account.test.primary_blob_connection_string}" + container_name = "${azurerm_storage_container.test.name}" + notifications = true + max_delivery_count = 12 + sas_ttl = "PT2H" + default_ttl = "PT3H" + lock_duration = "PT5M" + } +} +`, rInt, location, rStr, rInt) +} diff --git a/azurerm/resource_arm_key_vault.go b/azurerm/resource_arm_key_vault.go index df5c07078935..af3479dd39e5 100644 --- a/azurerm/resource_arm_key_vault.go +++ b/azurerm/resource_arm_key_vault.go @@ -16,6 +16,10 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/set" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -52,14 +56,19 @@ func resourceArmKeyVault() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), + // Remove in 2.0 "sku": { - Type: schema.TypeList, - Required: true, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "This property has been deprecated in favour of the 'sku_name' property and will be removed in version 2.0 of the provider", + ConflictsWith: []string{"sku_name"}, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringInSlice([]string{ string(keyvault.Standard), string(keyvault.Premium), @@ -69,6 +78,16 @@ func resourceArmKeyVault() *schema.Resource { }, }, + "sku_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + string(keyvault.Standard), + string(keyvault.Premium), + }, false), + }, + "vault_uri": { Type: schema.TypeString, Computed: true, @@ -77,7 +96,7 @@ func resourceArmKeyVault() *schema.Resource { "tenant_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "access_policy": { @@ -91,17 +110,17 @@ func resourceArmKeyVault() *schema.Resource { "tenant_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "object_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "application_id": { Type: schema.TypeString, Optional: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "certificate_permissions": azure.SchemaKeyVaultCertificatePermissions(), "key_permissions": azure.SchemaKeyVaultKeyPermissions(), @@ -164,20 +183,44 @@ func resourceArmKeyVault() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext + + // Remove in 2.0 + var sku keyvault.Sku + + if inputs := d.Get("sku").([]interface{}); len(inputs) != 0 { + input := inputs[0].(map[string]interface{}) + v := input["name"].(string) + + sku = keyvault.Sku{ + Family: &armKeyVaultSkuFamily, + Name: keyvault.SkuName(v), + } + } else { + // Keep in 2.0 + sku = keyvault.Sku{ + Family: &armKeyVaultSkuFamily, + Name: keyvault.SkuName(d.Get("sku_name").(string)), + } + } + + if sku.Name == "" { + return fmt.Errorf("either 'sku_name' or 'sku' must be defined in the configuration file") + } + log.Printf("[INFO] preparing arguments for Azure ARM KeyVault creation.") name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -195,7 +238,7 @@ func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) e enabledForDeployment := d.Get("enabled_for_deployment").(bool) enabledForDiskEncryption := d.Get("enabled_for_disk_encryption").(bool) enabledForTemplateDeployment := d.Get("enabled_for_template_deployment").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) networkAclsRaw := d.Get("network_acls").([]interface{}) networkAcls, subnetIds := expandKeyVaultNetworkAcls(networkAclsRaw) @@ -210,25 +253,25 @@ func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) e Location: &location, Properties: &keyvault.VaultProperties{ TenantID: &tenantUUID, - Sku: expandKeyVaultSku(d), + Sku: &sku, AccessPolicies: accessPolicies, EnabledForDeployment: &enabledForDeployment, EnabledForDiskEncryption: &enabledForDiskEncryption, EnabledForTemplateDeployment: &enabledForTemplateDeployment, NetworkAcls: networkAcls, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } // Locking this resource so we don't make modifications to it at the same time if there is a // key vault access policy trying to update it as well - azureRMLockByName(name, keyVaultResourceName) - defer azureRMUnlockByName(name, keyVaultResourceName) + locks.ByName(name, keyVaultResourceName) + defer locks.UnlockByName(name, keyVaultResourceName) // also lock on the Virtual Network ID's since modifications in the networking stack are exclusive virtualNetworkNames := make([]string, 0) for _, v := range subnetIds { - id, err2 := parseAzureResourceID(v) + id, err2 := azure.ParseAzureResourceID(v) if err2 != nil { return err2 } @@ -239,8 +282,8 @@ func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) e } } - azureRMLockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + locks.MultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) if _, err = client.CreateOrUpdate(ctx, resourceGroup, name, parameters); err != nil { return fmt.Errorf("Error updating Key Vault %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -281,10 +324,10 @@ func resourceArmKeyVaultCreateUpdate(d *schema.ResourceData, meta interface{}) e } func resourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -314,8 +357,17 @@ func resourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { d.Set("enabled_for_template_deployment", props.EnabledForTemplateDeployment) d.Set("vault_uri", props.VaultURI) - if err := d.Set("sku", flattenKeyVaultSku(props.Sku)); err != nil { - return fmt.Errorf("Error setting `sku` for KeyVault %q: %+v", *resp.Name, err) + if sku := props.Sku; sku != nil { + // Remove in 2.0 + if err := d.Set("sku", flattenKeyVaultSku(sku)); err != nil { + return fmt.Errorf("Error setting 'sku' for KeyVault %q: %+v", *resp.Name, err) + } + + if err := d.Set("sku_name", string(sku.Name)); err != nil { + return fmt.Errorf("Error setting 'sku_name' for KeyVault %q: %+v", *resp.Name, err) + } + } else { + return fmt.Errorf("Error making Read request on KeyVault %q (Resource Group %q): Unable to retrieve 'sku' value", name, resourceGroup) } if err := d.Set("network_acls", flattenKeyVaultNetworkAcls(props.NetworkAcls)); err != nil { @@ -328,23 +380,22 @@ func resourceArmKeyVaultRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } resourceGroup := id.ResourceGroup name := id.Path["vaults"] - azureRMLockByName(name, keyVaultResourceName) - defer azureRMUnlockByName(name, keyVaultResourceName) + locks.ByName(name, keyVaultResourceName) + defer locks.UnlockByName(name, keyVaultResourceName) read, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -365,7 +416,7 @@ func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error { continue } - id, err2 := parseAzureResourceID(*v.ID) + id, err2 := azure.ParseAzureResourceID(*v.ID) if err2 != nil { return err2 } @@ -379,8 +430,8 @@ func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error { } } - azureRMLockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + locks.MultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) resp, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -392,16 +443,7 @@ func resourceArmKeyVaultDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func expandKeyVaultSku(d *schema.ResourceData) *keyvault.Sku { - skuSets := d.Get("sku").([]interface{}) - sku := skuSets[0].(map[string]interface{}) - - return &keyvault.Sku{ - Family: &armKeyVaultSkuFamily, - Name: keyvault.SkuName(sku["name"].(string)), - } -} - +// Remove in 2.0 func flattenKeyVaultSku(sku *keyvault.Sku) []interface{} { result := map[string]interface{}{ "name": string(sku.Name), diff --git a/azurerm/resource_arm_key_vault_access_policy.go b/azurerm/resource_arm_key_vault_access_policy.go index 329a38d064fa..f0022d86647b 100644 --- a/azurerm/resource_arm_key_vault_access_policy.go +++ b/azurerm/resource_arm_key_vault_access_policy.go @@ -8,6 +8,8 @@ import ( uuid "github.com/satori/go.uuid" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" "github.com/hashicorp/terraform/helper/schema" @@ -78,21 +80,21 @@ func resourceArmKeyVaultAccessPolicy() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "object_id": { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "application_id": { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "certificate_permissions": azure.SchemaKeyVaultCertificatePermissions(), @@ -107,7 +109,7 @@ func resourceArmKeyVaultAccessPolicy() *schema.Resource { } func resourceArmKeyVaultAccessPolicyCreateOrDelete(d *schema.ResourceData, meta interface{}, action keyvault.AccessPolicyUpdateKind) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] Preparing arguments for Key Vault Access Policy: %s.", action) @@ -140,7 +142,6 @@ func resourceArmKeyVaultAccessPolicyCreateOrDelete(d *schema.ResourceData, meta return fmt.Errorf("key_value_id does not contain `vaults`: %q", vaultId) } vaultName = vaultNameTemp - } else if resourceGroup == "" { return fmt.Errorf("one of `resource_group_name` must be set when `vault_name` is used") } @@ -169,10 +170,10 @@ func resourceArmKeyVaultAccessPolicyCreateOrDelete(d *schema.ResourceData, meta } // Locking to prevent parallel changes causing issues - azureRMLockByName(vaultName, keyVaultResourceName) - defer azureRMUnlockByName(vaultName, keyVaultResourceName) + locks.ByName(vaultName, keyVaultResourceName) + defer locks.UnlockByName(vaultName, keyVaultResourceName) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { props := keyVault.Properties if props == nil { return fmt.Errorf("Error parsing Key Vault: `properties` was nil") @@ -275,10 +276,10 @@ func resourceArmKeyVaultAccessPolicyUpdate(d *schema.ResourceData, meta interfac } func resourceArmKeyVaultAccessPolicyRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err diff --git a/azurerm/resource_arm_key_vault_access_policy_test.go b/azurerm/resource_arm_key_vault_access_policy_test.go index 4e528584e554..7caa77576ce1 100644 --- a/azurerm/resource_arm_key_vault_access_policy_test.go +++ b/azurerm/resource_arm_key_vault_access_policy_test.go @@ -8,6 +8,8 @@ import ( "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" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -68,7 +70,7 @@ func TestAccAzureRMKeyVaultAccessPolicy_basicClassic(t *testing.T) { } func TestAccAzureRMKeyVaultAccessPolicy_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -196,7 +198,7 @@ func TestAccAzureRMKeyVaultAccessPolicy_nonExistentVault(t *testing.T) { func testCheckAzureRMKeyVaultAccessPolicyExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultClient + client := testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -205,7 +207,7 @@ func testCheckAzureRMKeyVaultAccessPolicyExists(resourceName string) resource.Te return fmt.Errorf("Not found: %s", resourceName) } - id, err := parseAzureResourceID(rs.Primary.ID) + id, err := azure.ParseAzureResourceID(rs.Primary.ID) if err != nil { return err @@ -290,9 +292,9 @@ func testAccAzureRMKeyVaultAccessPolicy_requiresImport(rString string, location %s resource "azurerm_key_vault_access_policy" "import" { - key_vault_id = "${azurerm_key_vault.test.id}" - tenant_id = "${azurerm_key_vault_access_policy.test.tenant_id}" - object_id = "${azurerm_key_vault_access_policy.test.object_id}" + key_vault_id = "${azurerm_key_vault.test.id}" + tenant_id = "${azurerm_key_vault_access_policy.test.tenant_id}" + object_id = "${azurerm_key_vault_access_policy.test.object_id}" key_permissions = [ "get", @@ -412,9 +414,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" tags = { environment = "Production" @@ -438,9 +438,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" tags = { environment = "Production" diff --git a/azurerm/resource_arm_key_vault_certificate.go b/azurerm/resource_arm_key_vault_certificate.go index d2a36b3c981e..bdfcd66c9bb0 100644 --- a/azurerm/resource_arm_key_vault_certificate.go +++ b/azurerm/resource_arm_key_vault_certificate.go @@ -16,12 +16,14 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) //todo refactor and find a home for this wayward func func resourceArmKeyVaultChildResourceImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - client := meta.(*ArmClient).keyVaultClient + client := meta.(*ArmClient).keyvault.VaultsClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -234,7 +236,8 @@ func resourceArmKeyVaultCertificate() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, }, }, "key_usage": { @@ -328,14 +331,14 @@ func resourceArmKeyVaultCertificate() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -361,7 +364,7 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface d.Set("key_vault_id", id) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.GetCertificate(ctx, keyVaultBaseUrl, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -374,7 +377,7 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface } } - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) policy := expandKeyVaultCertificatePolicy(d) if v, ok := d.GetOk("certificate"); ok { @@ -384,7 +387,7 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface Base64EncodedCertificate: utils.String(certificate.CertificateData), Password: utils.String(certificate.CertificatePassword), CertificatePolicy: &policy, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.ImportCertificate(ctx, keyVaultBaseUrl, name, importParameters); err != nil { return err @@ -393,7 +396,7 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface // Generate new parameters := keyvault.CertificateCreateParameters{ CertificatePolicy: &policy, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateCertificate(ctx, keyVaultBaseUrl, name, parameters); err != nil { return err @@ -422,7 +425,7 @@ func resourceArmKeyVaultCertificateCreate(d *schema.ResourceData, meta interface return resourceArmKeyVaultCertificateRead(d, meta) } -func keyVaultCertificateCreationRefreshFunc(ctx context.Context, client keyvault.BaseClient, keyVaultBaseUrl string, name string) resource.StateRefreshFunc { +func keyVaultCertificateCreationRefreshFunc(ctx context.Context, client *keyvault.BaseClient, keyVaultBaseUrl string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.GetCertificate(ctx, keyVaultBaseUrl, name, "") if err != nil { @@ -438,8 +441,8 @@ func keyVaultCertificateCreationRefreshFunc(ctx context.Context, client keyvault } func resourceArmKeyVaultCertificateRead(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -491,7 +494,7 @@ func resourceArmKeyVaultCertificateRead(d *schema.ResourceData, meta interface{} d.Set("secret_id", cert.Sid) if contents := cert.Cer; contents != nil { - d.Set("certificate_data", string(*contents)) + d.Set("certificate_data", strings.ToUpper(hex.EncodeToString(*contents))) } if v := cert.X509Thumbprint; v != nil { @@ -502,14 +505,12 @@ func resourceArmKeyVaultCertificateRead(d *schema.ResourceData, meta interface{} d.Set("thumbprint", strings.ToUpper(hex.EncodeToString(x509Thumbprint))) } - flattenAndSetTags(d, cert.Tags) - - return nil + return tags.FlattenAndSet(d, cert.Tags) } func resourceArmKeyVaultCertificateDelete(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -622,7 +623,6 @@ func expandKeyVaultCertificatePolicy(d *schema.ResourceData) keyvault.Certificat subjectAlternativeNames := &keyvault.SubjectAlternativeNames{} if v, ok := cert["subject_alternative_names"]; ok { - if sans := v.([]interface{}); len(sans) > 0 { if sans[0] != nil { san := sans[0].(map[string]interface{}) diff --git a/azurerm/resource_arm_key_vault_certificate_test.go b/azurerm/resource_arm_key_vault_certificate_test.go index 7f77d6c3f09e..7c368d91db5a 100644 --- a/azurerm/resource_arm_key_vault_certificate_test.go +++ b/azurerm/resource_arm_key_vault_certificate_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -68,7 +69,7 @@ func TestAccAzureRMKeyVaultCertificate_basicImportPFXClassic(t *testing.T) { } func TestAccAzureRMKeyVaultCertificate_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -244,8 +245,30 @@ func TestAccAzureRMKeyVaultCertificate_basicExtendedKeyUsage(t *testing.T) { }) } +func TestAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(t *testing.T) { + resourceName := "azurerm_key_vault_certificate.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultCertificateExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "certificate_data"), + resource.TestCheckResourceAttr(resourceName, "certificate_policy.0.x509_certificate_properties.0.extended_key_usage.#", "0"), + ), + }, + }, + }) +} + func testCheckAzureRMKeyVaultCertificateDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -257,7 +280,7 @@ func testCheckAzureRMKeyVaultCertificateDestroy(s *terraform.State) error { vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Certificate %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -283,7 +306,7 @@ func testCheckAzureRMKeyVaultCertificateDestroy(s *terraform.State) error { func testCheckAzureRMKeyVaultCertificateExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -296,7 +319,7 @@ func testCheckAzureRMKeyVaultCertificateExists(resourceName string) resource.Tes vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Certificate %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -320,7 +343,7 @@ func testCheckAzureRMKeyVaultCertificateExists(resourceName string) resource.Tes func testCheckAzureRMKeyVaultCertificateDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -332,7 +355,7 @@ func testCheckAzureRMKeyVaultCertificateDisappears(resourceName string) resource vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Certificate %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -369,9 +392,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -394,7 +415,7 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_certificate" "test" { - name = "acctestcert%s" + name = "acctestcert%s" key_vault_id = "${azurerm_key_vault.test.id}" certificate { @@ -437,9 +458,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -542,9 +561,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -572,8 +589,8 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_certificate" "test" { - name = "acctestcert%s" - key_vault_id = "${azurerm_key_vault.test.id}" + name = "acctestcert%s" + key_vault_id = "${azurerm_key_vault.test.id}" certificate_policy { issuer_parameters { @@ -634,9 +651,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -656,7 +671,7 @@ resource "azurerm_key_vault" "test" { secret_permissions = [ "set", ] - + storage_permissions = [ "set", ] @@ -664,7 +679,7 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_certificate" "test" { - name = "acctestcert%s" + name = "acctestcert%s" key_vault_id = "${azurerm_key_vault.test.id}" certificate_policy { @@ -733,9 +748,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -759,7 +772,7 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_certificate" "test" { - name = "acctestcert%s" + name = "acctestcert%s" key_vault_id = "${azurerm_key_vault.test.id}" certificate_policy { @@ -825,9 +838,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "standard" - } + sku_name = "standard" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -907,3 +918,95 @@ resource "azurerm_key_vault_certificate" "test" { } `, rString, location, rString, rString) } + +func testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkeyvault%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku_name = "standard" + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + certificate_permissions = [ + "create", + "delete", + "get", + "update", + ] + + key_permissions = [ + "create", + ] + + secret_permissions = [ + "set", + ] + + storage_permissions = [ + "set", + ] + } +} + +resource "azurerm_key_vault_certificate" "test" { + name = "acctestcert%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + extended_key_usage = [] + + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + + subject = "CN=hello-world" + validity_in_months = 12 + } + } +} +`, rString, location, rString, rString) +} diff --git a/azurerm/resource_arm_key_vault_key.go b/azurerm/resource_arm_key_vault_key.go index df3701fee596..5ea92dc134db 100644 --- a/azurerm/resource_arm_key_vault_key.go +++ b/azurerm/resource_arm_key_vault_key.go @@ -1,6 +1,7 @@ package azurerm import ( + "encoding/base64" "fmt" "log" @@ -10,6 +11,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -61,15 +64,17 @@ func resourceArmKeyVaultKey() *schema.Resource { // TODO: add `oct` back in once this is fixed // https://github.com/Azure/azure-rest-api-specs/issues/1739#issuecomment-332236257 string(keyvault.EC), + string(keyvault.ECHSM), string(keyvault.RSA), string(keyvault.RSAHSM), }, false), }, "key_size": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"curve"}, }, "key_opts": { @@ -90,6 +95,23 @@ func resourceArmKeyVaultKey() *schema.Resource { }, }, + "curve": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(keyvault.P256), + string(keyvault.P384), + string(keyvault.P521), + string(keyvault.SECP256K1), + }, false), + // TODO: the curve name should probably be mandatory for EC in the future, + // but handle the diff so that we don't break existing configurations and + // imported EC keys + ConflictsWith: []string{"key_size"}, + }, + // Computed "version": { Type: schema.TypeString, @@ -106,14 +128,24 @@ func resourceArmKeyVaultKey() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "x": { + Type: schema.TypeString, + Computed: true, + }, + + "y": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), }, } } func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext log.Print("[INFO] preparing arguments for AzureRM KeyVault Key creation.") @@ -140,7 +172,7 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro d.Set("key_vault_id", id) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.GetKey(ctx, keyVaultBaseUri, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -155,7 +187,7 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro keyType := d.Get("key_type").(string) keyOptions := expandKeyVaultKeyOptions(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) // TODO: support Importing Keys once this is fixed: // https://github.com/Azure/azure-rest-api-specs/issues/1747 @@ -165,10 +197,23 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro KeyAttributes: &keyvault.KeyAttributes{ Enabled: utils.Bool(true), }, - KeySize: utils.Int32(int32(d.Get("key_size").(int))), - Tags: expandTags(tags), + + Tags: tags.Expand(t), } + if parameters.Kty == keyvault.EC || parameters.Kty == keyvault.ECHSM { + curveName := d.Get("curve").(string) + parameters.Curve = keyvault.JSONWebKeyCurveName(curveName) + } else if parameters.Kty == keyvault.RSA || parameters.Kty == keyvault.RSAHSM { + keySize, ok := d.GetOk("key_size") + if !ok { + return fmt.Errorf("Key size is required when creating an RSA key") + } + parameters.KeySize = utils.Int32(int32(keySize.(int))) + } + // TODO: support `oct` once this is fixed + // https://github.com/Azure/azure-rest-api-specs/issues/1739#issuecomment-332236257 + if _, err := client.CreateKey(ctx, keyVaultBaseUri, name, parameters); err != nil { return fmt.Errorf("Error Creating Key: %+v", err) } @@ -185,8 +230,8 @@ func resourceArmKeyVaultKeyCreate(d *schema.ResourceData, meta interface{}) erro } func resourceArmKeyVaultKeyUpdate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -213,14 +258,14 @@ func resourceArmKeyVaultKeyUpdate(d *schema.ResourceData, meta interface{}) erro } keyOptions := expandKeyVaultKeyOptions(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := keyvault.KeyUpdateParameters{ KeyOps: keyOptions, KeyAttributes: &keyvault.KeyAttributes{ Enabled: utils.Bool(true), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = client.UpdateKey(ctx, id.KeyVaultBaseUrl, id.Name, id.Version, parameters); err != nil { @@ -231,8 +276,8 @@ func resourceArmKeyVaultKeyUpdate(d *schema.ResourceData, meta interface{}) erro } func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -283,19 +328,28 @@ func resourceArmKeyVaultKeyRead(d *schema.ResourceData, meta interface{}) error d.Set("n", key.N) d.Set("e", key.E) + d.Set("x", key.X) + d.Set("y", key.Y) + if key.N != nil { + nBytes, err := base64.RawURLEncoding.DecodeString(*key.N) + if err != nil { + return fmt.Errorf("Could not decode N: %+v", err) + } + d.Set("key_size", len(nBytes)*8) + } + + d.Set("curve", key.Crv) } // Computed d.Set("version", id.Version) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmKeyVaultKeyDelete(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) diff --git a/azurerm/resource_arm_key_vault_key_test.go b/azurerm/resource_arm_key_vault_key_test.go index b9c37fd56431..272bf3122848 100644 --- a/azurerm/resource_arm_key_vault_key_test.go +++ b/azurerm/resource_arm_key_vault_key_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -66,7 +67,7 @@ func TestAccAzureRMKeyVaultKey_basicECClassic(t *testing.T) { } func TestAccAzureRMKeyVaultKey_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -94,6 +95,51 @@ func TestAccAzureRMKeyVaultKey_requiresImport(t *testing.T) { }) } +func TestAccAzureRMKeyVaultKey_basicECHSM(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_basicECHSM(rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMKeyVaultKey_curveEC(t *testing.T) { + resourceName := "azurerm_key_vault_key.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultKey_curveEC(rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultKeyDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultKeyExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMKeyVaultKey_basicRSA(t *testing.T) { resourceName := "azurerm_key_vault_key.test" rs := acctest.RandString(6) @@ -249,7 +295,7 @@ func TestAccAzureRMKeyVaultKey_disappearsWhenParentKeyVaultDeleted(t *testing.T) } func testCheckAzureRMKeyVaultKeyDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -261,7 +307,7 @@ func testCheckAzureRMKeyVaultKeyDestroy(s *terraform.State) error { vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Secret %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -287,7 +333,7 @@ func testCheckAzureRMKeyVaultKeyDestroy(s *terraform.State) error { func testCheckAzureRMKeyVaultKeyExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -299,7 +345,7 @@ func testCheckAzureRMKeyVaultKeyExists(resourceName string) resource.TestCheckFu vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Key %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -323,7 +369,7 @@ func testCheckAzureRMKeyVaultKeyExists(resourceName string) resource.TestCheckFu func testCheckAzureRMKeyVaultKeyDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -336,7 +382,7 @@ func testCheckAzureRMKeyVaultKeyDisappears(resourceName string) resource.TestChe vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Key %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -373,9 +419,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -428,9 +472,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -474,10 +516,10 @@ func testAccAzureRMKeyVaultKey_requiresImport(rString string, location string) s %s resource "azurerm_key_vault_key" "import" { - name = "${azurerm_key_vault_key.test.name}" - key_vault_id = "${azurerm_key_vault.test.id}" - key_type = "EC" - key_size = 2048 + name = "${azurerm_key_vault_key.test.name}" + key_vault_id = "${azurerm_key_vault.test.id}" + key_type = "EC" + key_size = 2048 key_opts = [ "sign", @@ -502,9 +544,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -530,10 +570,10 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_key" "test" { - name = "key-%s" - key_vault_id = "${azurerm_key_vault.test.id}" - key_type = "RSA" - key_size = 2048 + name = "key-%s" + key_vault_id = "${azurerm_key_vault.test.id}" + key_type = "RSA" + key_size = 2048 key_opts = [ "decrypt", @@ -562,9 +602,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -589,10 +627,10 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_key" "test" { - name = "key-%s" - key_vault_id = "${azurerm_key_vault.test.id}" - key_type = "RSA-HSM" - key_size = 2048 + name = "key-%s" + key_vault_id = "${azurerm_key_vault.test.id}" + key_type = "RSA-HSM" + key_size = 2048 key_opts = [ "decrypt", @@ -621,9 +659,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -648,10 +684,10 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_key" "test" { - name = "key-%s" - key_vault_id = "${azurerm_key_vault.test.id}" - key_type = "RSA" - key_size = 2048 + name = "key-%s" + key_vault_id = "${azurerm_key_vault.test.id}" + key_type = "RSA" + key_size = 2048 key_opts = [ "decrypt", @@ -684,9 +720,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -727,3 +761,113 @@ resource "azurerm_key_vault_key" "test" { } `, rString, location, rString, rString) } + +func testAccAzureRMKeyVaultKey_curveEC(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku { + name = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "create", + "delete", + "get", + ] + + secret_permissions = [ + "get", + "delete", + "set", + ] + } + + tags = { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "EC" + curve = "P-521" + + key_opts = [ + "sign", + "verify", + ] +} +`, rString, location, rString, rString) +} + +func testAccAzureRMKeyVaultKey_basicECHSM(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku { + name = "premium" + } + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + key_permissions = [ + "create", + "delete", + "get", + ] + + secret_permissions = [ + "get", + "delete", + "set", + ] + } + + tags = { + environment = "Production" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + key_type = "EC-HSM" + curve = "P-521" + + key_opts = [ + "sign", + "verify", + ] +} +`, rString, location, rString, rString) +} diff --git a/azurerm/resource_arm_key_vault_secret.go b/azurerm/resource_arm_key_vault_secret.go index cf54c2b1de74..93070146053a 100644 --- a/azurerm/resource_arm_key_vault_secret.go +++ b/azurerm/resource_arm_key_vault_secret.go @@ -9,6 +9,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -66,14 +68,14 @@ func resourceArmKeyVaultSecret() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmKeyVaultSecretCreate(d *schema.ResourceData, meta interface{}) error { - vaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + vaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext log.Print("[INFO] preparing arguments for AzureRM KeyVault Secret creation.") @@ -101,7 +103,7 @@ func resourceArmKeyVaultSecretCreate(d *schema.ResourceData, meta interface{}) e d.Set("key_vault_id", id) } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.GetSecret(ctx, keyVaultBaseUrl, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -116,12 +118,12 @@ func resourceArmKeyVaultSecretCreate(d *schema.ResourceData, meta interface{}) e value := d.Get("value").(string) contentType := d.Get("content_type").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := keyvault.SecretSetParameters{ Value: utils.String(value), ContentType: utils.String(contentType), - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.SetSecret(ctx, keyVaultBaseUrl, name, parameters); err != nil { @@ -143,8 +145,8 @@ func resourceArmKeyVaultSecretCreate(d *schema.ResourceData, meta interface{}) e } func resourceArmKeyVaultSecretUpdate(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext log.Print("[INFO] preparing arguments for AzureRM KeyVault Secret update.") @@ -173,14 +175,14 @@ func resourceArmKeyVaultSecretUpdate(d *schema.ResourceData, meta interface{}) e value := d.Get("value").(string) contentType := d.Get("content_type").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) if d.HasChange("value") { // for changing the value of the secret we need to create a new version parameters := keyvault.SecretSetParameters{ Value: utils.String(value), ContentType: utils.String(contentType), - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = client.SetSecret(ctx, id.KeyVaultBaseUrl, id.Name, parameters); err != nil { @@ -202,7 +204,7 @@ func resourceArmKeyVaultSecretUpdate(d *schema.ResourceData, meta interface{}) e } else { parameters := keyvault.SecretUpdateParameters{ ContentType: utils.String(contentType), - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = client.UpdateSecret(ctx, id.KeyVaultBaseUrl, id.Name, id.Version, parameters); err != nil { @@ -214,8 +216,8 @@ func resourceArmKeyVaultSecretUpdate(d *schema.ResourceData, meta interface{}) e } func resourceArmKeyVaultSecretRead(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) @@ -266,13 +268,12 @@ func resourceArmKeyVaultSecretRead(d *schema.ResourceData, meta interface{}) err d.Set("version", respID.Version) d.Set("content_type", resp.ContentType) - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmKeyVaultSecretDelete(d *schema.ResourceData, meta interface{}) error { - keyVaultClient := meta.(*ArmClient).keyVaultClient - client := meta.(*ArmClient).keyVaultManagementClient + keyVaultClient := meta.(*ArmClient).keyvault.VaultsClient + client := meta.(*ArmClient).keyvault.ManagementClient ctx := meta.(*ArmClient).StopContext id, err := azure.ParseKeyVaultChildID(d.Id()) diff --git a/azurerm/resource_arm_key_vault_secret_test.go b/azurerm/resource_arm_key_vault_secret_test.go index d9c388ed0426..23a686ffe8e4 100644 --- a/azurerm/resource_arm_key_vault_secret_test.go +++ b/azurerm/resource_arm_key_vault_secret_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -66,7 +67,7 @@ func TestAccAzureRMKeyVaultSecret_basicClassic(t *testing.T) { } func TestAccAzureRMKeyVaultSecret_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -195,7 +196,7 @@ func TestAccAzureRMKeyVaultSecret_update(t *testing.T) { } func testCheckAzureRMKeyVaultSecretDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -207,7 +208,7 @@ func testCheckAzureRMKeyVaultSecretDestroy(s *terraform.State) error { vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Secret %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -233,7 +234,7 @@ func testCheckAzureRMKeyVaultSecretDestroy(s *terraform.State) error { func testCheckAzureRMKeyVaultSecretExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -245,7 +246,7 @@ func testCheckAzureRMKeyVaultSecretExists(resourceName string) resource.TestChec vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Secret %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -269,7 +270,7 @@ func testCheckAzureRMKeyVaultSecretExists(resourceName string) resource.TestChec func testCheckAzureRMKeyVaultSecretDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultManagementClient + client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -281,7 +282,7 @@ func testCheckAzureRMKeyVaultSecretDisappears(resourceName string) resource.Test vaultBaseUrl := rs.Primary.Attributes["vault_uri"] keyVaultId := rs.Primary.Attributes["key_vault_id"] - ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyVaultClient, keyVaultId) + ok, err := azure.KeyVaultExists(ctx, testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient, keyVaultId) if err != nil { return fmt.Errorf("Error checking if key vault %q for Secret %q in Vault at url %q exists: %v", keyVaultId, name, vaultBaseUrl, err) } @@ -318,9 +319,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -343,9 +342,9 @@ resource "azurerm_key_vault" "test" { } resource "azurerm_key_vault_secret" "test" { - name = "secret-%s" - value = "rick-and-morty" - key_vault_id = "${azurerm_key_vault.test.id}" + name = "secret-%s" + value = "rick-and-morty" + key_vault_id = "${azurerm_key_vault.test.id}" } `, rString, location, rString, rString) } @@ -365,9 +364,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -403,9 +400,9 @@ func testAccAzureRMKeyVaultSecret_requiresImport(rString string, location string %s resource "azurerm_key_vault_secret" "import" { - name = "${azurerm_key_vault_secret.test.name}" - value = "${azurerm_key_vault_secret.test.value}" - key_vault_id = "${azurerm_key_vault_secret.test.key_vault_id}" + name = "${azurerm_key_vault_secret.test.name}" + value = "${azurerm_key_vault_secret.test.value}" + key_vault_id = "${azurerm_key_vault_secret.test.key_vault_id}" } `, template) } @@ -425,9 +422,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -452,7 +447,7 @@ resource "azurerm_key_vault" "test" { resource "azurerm_key_vault_secret" "test" { name = "secret-%s" value = "" - key_vault_id = "${azurerm_key_vault.test.id}" + key_vault_id = "${azurerm_key_vault.test.id}" content_type = "application/xml" tags = { @@ -477,9 +472,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" diff --git a/azurerm/resource_arm_key_vault_test.go b/azurerm/resource_arm_key_vault_test.go index 724f731863dc..90decce79c86 100644 --- a/azurerm/resource_arm_key_vault_test.go +++ b/azurerm/resource_arm_key_vault_test.go @@ -2,6 +2,7 @@ package azurerm import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -9,6 +10,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -85,6 +87,53 @@ func TestAccAzureRMKeyVault_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureRMKeyVaultExists(resourceName), resource.TestCheckResourceAttr(resourceName, "network_acls.#", "0"), + resource.TestCheckResourceAttr(resourceName, "sku_name", "premium"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMKeyVault_basicNotDefined(t *testing.T) { + ri := tf.AccRandTimeInt() + config := testAccAzureRMKeyVault_basicNotDefined(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultDestroy, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile("either 'sku_name' or 'sku' must be defined in the configuration file"), + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMKeyVault_basicClassic(t *testing.T) { + resourceName := "azurerm_key_vault.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMKeyVault_basicClassic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_acls.#", "0"), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "premium"), ), }, { @@ -97,7 +146,7 @@ func TestAccAzureRMKeyVault_basic(t *testing.T) { } func TestAccAzureRMKeyVault_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -313,7 +362,7 @@ func TestAccAzureRMKeyVault_justCert(t *testing.T) { } func testCheckAzureRMKeyVaultDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).keyVaultClient + client := testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -352,7 +401,7 @@ func testCheckAzureRMKeyVaultExists(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for vault: %s", vaultName) } - client := testAccProvider.Meta().(*ArmClient).keyVaultClient + client := testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, vaultName) @@ -382,7 +431,7 @@ func testCheckAzureRMKeyVaultDisappears(resourceName string) resource.TestCheckF return fmt.Errorf("Bad: no resource group found in state for vault: %s", vaultName) } - client := testAccProvider.Meta().(*ArmClient).keyVaultClient + client := testAccProvider.Meta().(*ArmClient).keyvault.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Delete(ctx, resourceGroup, vaultName) @@ -407,6 +456,70 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_key_vault" "test" { + name = "vault%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku_name = "premium" + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.client_id}" + + key_permissions = [ + "create", + ] + + secret_permissions = [ + "set", + ] + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMKeyVault_basicNotDefined(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "vault%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.client_id}" + + key_permissions = [ + "create", + ] + + secret_permissions = [ + "set", + ] + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMKeyVault_basicClassic(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_key_vault" "test" { name = "vault%d" location = "${azurerm_resource_group.test.location}" @@ -438,16 +551,13 @@ func testAccAzureRMKeyVault_requiresImport(rInt int, location string) string { return fmt.Sprintf(` %s - resource "azurerm_key_vault" "import" { name = "${azurerm_key_vault.test.name}" location = "${azurerm_key_vault.test.location}" resource_group_name = "${azurerm_key_vault.test.resource_group_name}" tenant_id = "${azurerm_key_vault.test.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -510,9 +620,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -547,9 +655,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -589,9 +695,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -694,9 +798,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -738,9 +840,7 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" access_policy { tenant_id = "${data.azurerm_client_config.current.tenant_id}" @@ -755,7 +855,6 @@ resource "azurerm_key_vault" "test" { } func testAccAzureRMKeyVault_accessPolicyUpperLimit(rInt int, location string, rs string) string { - var storageAccountConfigs string var accessPoliciesConfigs string @@ -778,44 +877,43 @@ resource "azurerm_key_vault" "test" { resource_group_name = "${azurerm_resource_group.test.name}" tenant_id = "${data.azurerm_client_config.current.tenant_id}" - sku { - name = "premium" - } + sku_name = "premium" %s } %s + `, rInt, location, rInt, accessPoliciesConfigs, storageAccountConfigs) } func testAccAzureRMKeyVault_generateStorageAccountConfigs(accountNum int, rs string) string { return fmt.Sprintf(` resource "azurerm_storage_account" "testsa%d" { - name = "testsa%s%d" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "GRS" - - identity { - type = "SystemAssigned" - } - - tags = { - environment = "testing" - } + name = "testsa%s%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "GRS" + + identity { + type = "SystemAssigned" + } + + tags = { + environment = "testing" + } } `, accountNum, rs, accountNum) } func testAccAzureRMKeyVault_generateAccessPolicyConfigs(accountNum int) string { return fmt.Sprintf(` - access_policy { - tenant_id = "${data.azurerm_client_config.current.tenant_id}" - object_id = "${azurerm_storage_account.testsa%d.identity.0.principal_id}" - - key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] - secret_permissions = ["get"] - } +access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${azurerm_storage_account.testsa%d.identity.0.principal_id}" + + key_permissions = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify"] + secret_permissions = ["get"] +} `, accountNum) } diff --git a/azurerm/resource_arm_kubernetes_cluster.go b/azurerm/resource_arm_kubernetes_cluster.go index 6a0524b3c689..9759c8f16e84 100644 --- a/azurerm/resource_arm_kubernetes_cluster.go +++ b/azurerm/resource_arm_kubernetes_cluster.go @@ -1,13 +1,11 @@ package azurerm import ( - "bytes" "fmt" "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-02-01/containerservice" - "github.com/hashicorp/terraform/helper/hashcode" + "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-06-01/containerservice" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" @@ -15,14 +13,16 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func resourceArmKubernetesCluster() *schema.Resource { return &schema.Resource{ - Create: resourceArmKubernetesClusterCreateUpdate, + Create: resourceArmKubernetesClusterCreate, Read: resourceArmKubernetesClusterRead, - Update: resourceArmKubernetesClusterCreateUpdate, + Update: resourceArmKubernetesClusterUpdate, Delete: resourceArmKubernetesClusterDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -46,6 +46,12 @@ func resourceArmKubernetesCluster() *schema.Resource { dockerBridgeCidr := profile["docker_bridge_cidr"].(string) dnsServiceIP := profile["dns_service_ip"].(string) serviceCidr := profile["service_cidr"].(string) + podCidr := profile["pod_cidr"].(string) + + // Azure network plugin is not compatible with pod_cidr + if podCidr != "" && networkPlugin == "azure" { + return fmt.Errorf("`pod_cidr` and `azure` cannot be set together.") + } // All empty values. if dockerBridgeCidr == "" && dnsServiceIP == "" && serviceCidr == "" { @@ -119,6 +125,31 @@ func resourceArmKubernetesCluster() *schema.Resource { ValidateFunc: validation.IntBetween(1, 100), }, + "max_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 100), + }, + + "min_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 100), + }, + + "enable_auto_scaling": { + Type: schema.TypeBool, + Optional: true, + }, + + "availability_zones": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + // TODO: remove this field in the next major version "dns_prefix": { Type: schema.TypeString, @@ -173,13 +204,18 @@ func resourceArmKubernetesCluster() *schema.Resource { Computed: true, ForceNew: true, }, + + "node_taints": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, }, }, - // TODO: 2.0 - we should be able to make this a List to be able to detect changes in the Client Secret "service_principal": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, MaxItems: 1, Elem: &schema.Resource{ @@ -187,20 +223,17 @@ func resourceArmKubernetesCluster() *schema.Resource { "client_id": { Type: schema.TypeString, Required: true, - ForceNew: true, ValidateFunc: validate.NoEmptyStrings, }, "client_secret": { Type: schema.TypeString, - ForceNew: true, Required: true, Sensitive: true, ValidateFunc: validate.NoEmptyStrings, }, }, }, - Set: resourceKubernetesClusterServicePrincipalProfileHash, }, // Optional @@ -268,6 +301,20 @@ func resourceArmKubernetesCluster() *schema.Resource { }, }, }, + + "kube_dashboard": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, }, }, }, @@ -305,6 +352,26 @@ func resourceArmKubernetesCluster() *schema.Resource { }, }, + "windows_profile": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "admin_username": { + Type: schema.TypeString, + Required: true, + }, + "admin_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "network_profile": { Type: schema.TypeList, Optional: true, @@ -365,6 +432,18 @@ func resourceArmKubernetesCluster() *schema.Resource { ForceNew: true, ValidateFunc: validate.CIDR, }, + + "load_balancer_sku": { + Type: schema.TypeString, + Optional: true, + Default: string(containerservice.Basic), + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(containerservice.Basic), + string(containerservice.Standard), + }, true), + DiffSuppressFunc: suppress.CaseDifference, + }, }, }, }, @@ -426,7 +505,7 @@ func resourceArmKubernetesCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "fqdn": { Type: schema.TypeString, @@ -520,7 +599,9 @@ func resourceArmKubernetesCluster() *schema.Resource { "node_resource_group": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, "api_server_authorized_ip_ranges": { @@ -531,21 +612,27 @@ func resourceArmKubernetesCluster() *schema.Resource { ValidateFunc: validate.CIDR, }, }, + + "enable_pod_security_policy": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, }, } } -func resourceArmKubernetesClusterCreateUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceArmKubernetesClusterCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).containers.KubernetesClustersClient ctx := meta.(*ArmClient).StopContext tenantId := meta.(*ArmClient).tenantId - log.Printf("[INFO] preparing arguments for Managed Kubernetes Cluster create/update.") + log.Printf("[INFO] preparing arguments for Managed Kubernetes Cluster create.") resGroup := d.Get("resource_group_name").(string) name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -563,24 +650,16 @@ func resourceArmKubernetesClusterCreateUpdate(d *schema.ResourceData, meta inter kubernetesVersion := d.Get("kubernetes_version").(string) linuxProfile := expandKubernetesClusterLinuxProfile(d) - agentProfiles := expandKubernetesClusterAgentPoolProfiles(d) + agentProfiles, err := expandKubernetesClusterAgentPoolProfiles(d) + if err != nil { + return err + } + windowsProfile := expandKubernetesClusterWindowsProfile(d) servicePrincipalProfile := expandAzureRmKubernetesClusterServicePrincipal(d) networkProfile := expandKubernetesClusterNetworkProfile(d) addonProfiles := expandKubernetesClusterAddonProfiles(d) - tags := d.Get("tags").(map[string]interface{}) - - // we can't do this in the CustomizeDiff since the interpolations aren't evaluated at that point - if networkProfile != nil { - // ensure there's a Subnet ID attached - if networkProfile.NetworkPlugin == containerservice.Azure { - for _, profile := range agentProfiles { - if profile.VnetSubnetID == nil { - return fmt.Errorf("A `vnet_subnet_id` must be specified when the `network_plugin` is set to `azure`.") - } - } - } - } + t := d.Get("tags").(map[string]interface{}) rbacRaw := d.Get("role_based_access_control").([]interface{}) rbacEnabled, azureADProfile := expandKubernetesClusterRoleBasedAccessControl(rbacRaw, tenantId) @@ -588,6 +667,10 @@ func resourceArmKubernetesClusterCreateUpdate(d *schema.ResourceData, meta inter apiServerAuthorizedIPRangesRaw := d.Get("api_server_authorized_ip_ranges").(*schema.Set).List() apiServerAuthorizedIPRanges := utils.ExpandStringSlice(apiServerAuthorizedIPRangesRaw) + nodeResourceGroup := d.Get("node_resource_group").(string) + + enablePodSecurityPolicy := d.Get("enable_pod_security_policy").(bool) + parameters := containerservice.ManagedCluster{ Name: &name, Location: &location, @@ -600,19 +683,22 @@ func resourceArmKubernetesClusterCreateUpdate(d *schema.ResourceData, meta inter EnableRBAC: utils.Bool(rbacEnabled), KubernetesVersion: utils.String(kubernetesVersion), LinuxProfile: linuxProfile, + WindowsProfile: windowsProfile, NetworkProfile: networkProfile, ServicePrincipalProfile: servicePrincipalProfile, + NodeResourceGroup: utils.String(nodeResourceGroup), + EnablePodSecurityPolicy: utils.Bool(enablePodSecurityPolicy), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, name, parameters) if err != nil { - return fmt.Errorf("Error creating/updating Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("Error creating Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for completion of Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resGroup, err) + return fmt.Errorf("Error waiting for creation of Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resGroup, err) } read, err := client.Get(ctx, resGroup, name) @@ -629,11 +715,120 @@ func resourceArmKubernetesClusterCreateUpdate(d *schema.ResourceData, meta inter return resourceArmKubernetesClusterRead(d, meta) } +func resourceArmKubernetesClusterUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).containers.KubernetesClustersClient + ctx := meta.(*ArmClient).StopContext + tenantId := meta.(*ArmClient).tenantId + + log.Printf("[INFO] preparing arguments for Managed Kubernetes Cluster update.") + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["managedClusters"] + + if d.HasChange("service_principal") { + log.Printf("[DEBUG] Updating the Service Principal for Kubernetes Cluster %q (Resource Group %q)..", name, resourceGroup) + servicePrincipals := d.Get("service_principal").([]interface{}) + servicePrincipalRaw := servicePrincipals[0].(map[string]interface{}) + + clientId := servicePrincipalRaw["client_id"].(string) + clientSecret := servicePrincipalRaw["client_secret"].(string) + + params := containerservice.ManagedClusterServicePrincipalProfile{ + ClientID: utils.String(clientId), + Secret: utils.String(clientSecret), + } + future, err := client.ResetServicePrincipalProfile(ctx, resourceGroup, name, params) + if err != nil { + return fmt.Errorf("Error updating Service Principal for Kubernetes Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for update of Service Principal for Kubernetes Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + log.Printf("[DEBUG] Updated the Service Principal for Kubernetes Cluster %q (Resource Group %q).", name, resourceGroup) + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + dnsPrefix := d.Get("dns_prefix").(string) + kubernetesVersion := d.Get("kubernetes_version").(string) + + linuxProfile := expandKubernetesClusterLinuxProfile(d) + agentProfiles, err := expandKubernetesClusterAgentPoolProfiles(d) + if err != nil { + return err + } + windowsProfile := expandKubernetesClusterWindowsProfile(d) + networkProfile := expandKubernetesClusterNetworkProfile(d) + servicePrincipalProfile := expandAzureRmKubernetesClusterServicePrincipal(d) + addonProfiles := expandKubernetesClusterAddonProfiles(d) + + t := d.Get("tags").(map[string]interface{}) + + rbacRaw := d.Get("role_based_access_control").([]interface{}) + rbacEnabled, azureADProfile := expandKubernetesClusterRoleBasedAccessControl(rbacRaw, tenantId) + + apiServerAuthorizedIPRangesRaw := d.Get("api_server_authorized_ip_ranges").(*schema.Set).List() + apiServerAuthorizedIPRanges := utils.ExpandStringSlice(apiServerAuthorizedIPRangesRaw) + + nodeResourceGroup := d.Get("node_resource_group").(string) + + enablePodSecurityPolicy := d.Get("enable_pod_security_policy").(bool) + + // TODO: should these values be conditionally updated? + parameters := containerservice.ManagedCluster{ + Name: &name, + Location: &location, + ManagedClusterProperties: &containerservice.ManagedClusterProperties{ + APIServerAuthorizedIPRanges: apiServerAuthorizedIPRanges, + AadProfile: azureADProfile, + AddonProfiles: addonProfiles, + AgentPoolProfiles: &agentProfiles, + DNSPrefix: utils.String(dnsPrefix), + EnableRBAC: utils.Bool(rbacEnabled), + KubernetesVersion: utils.String(kubernetesVersion), + LinuxProfile: linuxProfile, + WindowsProfile: windowsProfile, + NetworkProfile: networkProfile, + ServicePrincipalProfile: servicePrincipalProfile, + NodeResourceGroup: utils.String(nodeResourceGroup), + EnablePodSecurityPolicy: utils.Bool(enablePodSecurityPolicy), + }, + Tags: tags.Expand(t), + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) + if err != nil { + return fmt.Errorf("Error updating Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for update of Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Managed Kubernetes Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if read.ID == nil { + return fmt.Errorf("Cannot read ID for Managed Kubernetes Cluster %q (Resource Group %q)", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmKubernetesClusterRead(d, meta) +} + func resourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).containers.KubernetesClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -667,6 +862,7 @@ func resourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{}) d.Set("fqdn", props.Fqdn) d.Set("kubernetes_version", props.KubernetesVersion) d.Set("node_resource_group", props.NodeResourceGroup) + d.Set("enable_pod_security_policy", props.EnablePodSecurityPolicy) apiServerAuthorizedIPRanges := utils.FlattenStringSlice(props.APIServerAuthorizedIPRanges) if err := d.Set("api_server_authorized_ip_ranges", apiServerAuthorizedIPRanges); err != nil { @@ -688,6 +884,11 @@ func resourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error setting `linux_profile`: %+v", err) } + windowsProfile := flattenKubernetesClusterWindowsProfile(props.WindowsProfile, d) + if err := d.Set("windows_profile", windowsProfile); err != nil { + return fmt.Errorf("Error setting `windows_profile`: %+v", err) + } + networkProfile := flattenKubernetesClusterNetworkProfile(props.NetworkProfile) if err := d.Set("network_profile", networkProfile); err != nil { return fmt.Errorf("Error setting `network_profile`: %+v", err) @@ -698,7 +899,7 @@ func resourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error setting `role_based_access_control`: %+v", err) } - servicePrincipal := flattenAzureRmKubernetesClusterServicePrincipalProfile(props.ServicePrincipalProfile) + servicePrincipal := flattenAzureRmKubernetesClusterServicePrincipalProfile(props.ServicePrincipalProfile, d) if err := d.Set("service_principal", servicePrincipal); err != nil { return fmt.Errorf("Error setting `service_principal`: %+v", err) } @@ -727,16 +928,14 @@ func resourceArmKubernetesClusterRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error setting `kube_config`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmKubernetesClusterDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).containers.KubernetesClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -833,6 +1032,17 @@ func expandKubernetesClusterAddonProfiles(d *schema.ResourceData) map[string]*co } } + kubeDashboard := profile["kube_dashboard"].([]interface{}) + if len(kubeDashboard) > 0 { + value := kubeDashboard[0].(map[string]interface{}) + enabled := value["enabled"].(bool) + + addonProfiles["kubeDashboard"] = &containerservice.ManagedClusterAddonProfile{ + Enabled: utils.Bool(enabled), + Config: nil, + } + } + return addonProfiles } @@ -899,10 +1109,24 @@ func flattenKubernetesClusterAddonProfiles(profile map[string]*containerservice. } values["aci_connector_linux"] = aciConnectors + kubeDashboards := make([]interface{}, 0) + if kubeDashboard := profile["kubeDashboard"]; kubeDashboard != nil { + enabled := false + if enabledVal := kubeDashboard.Enabled; enabledVal != nil { + enabled = *enabledVal + } + + output := map[string]interface{}{ + "enabled": enabled, + } + kubeDashboards = append(kubeDashboards, output) + } + values["kube_dashboard"] = kubeDashboards + return []interface{}{values} } -func expandKubernetesClusterAgentPoolProfiles(d *schema.ResourceData) []containerservice.ManagedClusterAgentPoolProfile { +func expandKubernetesClusterAgentPoolProfiles(d *schema.ResourceData) ([]containerservice.ManagedClusterAgentPoolProfile, error) { configs := d.Get("agent_pool_profile").([]interface{}) profiles := make([]containerservice.ManagedClusterAgentPoolProfile, 0) @@ -933,10 +1157,41 @@ func expandKubernetesClusterAgentPoolProfiles(d *schema.ResourceData) []containe if vnetSubnetID != "" { profile.VnetSubnetID = utils.String(vnetSubnetID) } + + if maxCount := int32(config["max_count"].(int)); maxCount > 0 { + profile.MaxCount = utils.Int32(maxCount) + } + + if minCount := int32(config["min_count"].(int)); minCount > 0 { + profile.MinCount = utils.Int32(minCount) + } + + if enableAutoScalingItf := config["enable_auto_scaling"]; enableAutoScalingItf != nil { + profile.EnableAutoScaling = utils.Bool(enableAutoScalingItf.(bool)) + + // Auto scaling will change the number of nodes, but the original count number should not be sent again. + // This avoid the cluster being resized after creation. + if *profile.EnableAutoScaling && !d.IsNewResource() { + profile.Count = nil + } + } + + if availavilityZones := utils.ExpandStringSlice(config["availability_zones"].([]interface{})); len(*availavilityZones) > 0 { + profile.AvailabilityZones = availavilityZones + } + + if *profile.EnableAutoScaling && (profile.MinCount == nil || profile.MaxCount == nil) { + return nil, fmt.Errorf("Can't create an AKS cluster with autoscaling enabled but not setting min_count or max_count") + } + + if nodeTaints := utils.ExpandStringSlice(config["node_taints"].([]interface{})); len(*nodeTaints) > 0 { + profile.NodeTaints = nodeTaints + } + profiles = append(profiles, profile) } - return profiles + return profiles, nil } func flattenKubernetesClusterAgentPoolProfiles(profiles *[]containerservice.ManagedClusterAgentPoolProfile, fqdn *string) []interface{} { @@ -947,43 +1202,69 @@ func flattenKubernetesClusterAgentPoolProfiles(profiles *[]containerservice.Mana agentPoolProfiles := make([]interface{}, 0) for _, profile := range *profiles { - agentPoolProfile := make(map[string]interface{}) - - if profile.Type != "" { - agentPoolProfile["type"] = string(profile.Type) + count := 0 + if profile.Count != nil { + count = int(*profile.Count) } - if profile.Count != nil { - agentPoolProfile["count"] = int(*profile.Count) + enableAutoScaling := false + if profile.EnableAutoScaling != nil { + enableAutoScaling = *profile.EnableAutoScaling } + fqdnVal := "" if fqdn != nil { // temporarily persist the parent FQDN here until `fqdn` is removed from the `agent_pool_profile` - agentPoolProfile["fqdn"] = *fqdn + fqdnVal = *fqdn } - if profile.Name != nil { - agentPoolProfile["name"] = *profile.Name + maxCount := 0 + if profile.MaxCount != nil { + maxCount = int(*profile.MaxCount) } - if profile.VMSize != "" { - agentPoolProfile["vm_size"] = string(profile.VMSize) + maxPods := 0 + if profile.MaxPods != nil { + maxPods = int(*profile.MaxPods) } - if profile.OsDiskSizeGB != nil { - agentPoolProfile["os_disk_size_gb"] = int(*profile.OsDiskSizeGB) + minCount := 0 + if profile.MinCount != nil { + minCount = int(*profile.MinCount) } - if profile.VnetSubnetID != nil { - agentPoolProfile["vnet_subnet_id"] = *profile.VnetSubnetID + name := "" + if profile.Name != nil { + name = *profile.Name } - if profile.OsType != "" { - agentPoolProfile["os_type"] = string(profile.OsType) + osDiskSizeGB := 0 + if profile.OsDiskSizeGB != nil { + osDiskSizeGB = int(*profile.OsDiskSizeGB) } - if profile.MaxPods != nil { - agentPoolProfile["max_pods"] = int(*profile.MaxPods) + subnetId := "" + if profile.VnetSubnetID != nil { + subnetId = *profile.VnetSubnetID + } + + agentPoolProfile := map[string]interface{}{ + "availability_zones": utils.FlattenStringSlice(profile.AvailabilityZones), + "count": count, + "enable_auto_scaling": enableAutoScaling, + "max_count": maxCount, + "max_pods": maxPods, + "min_count": minCount, + "name": name, + "node_taints": utils.FlattenStringSlice(profile.NodeTaints), + "os_disk_size_gb": osDiskSizeGB, + "os_type": string(profile.OsType), + "type": string(profile.Type), + "vm_size": string(profile.VMSize), + "vnet_subnet_id": subnetId, + + // TODO: remove in 2.0 + "fqdn": fqdnVal, } agentPoolProfiles = append(agentPoolProfiles, agentPoolProfile) @@ -1008,20 +1289,17 @@ func expandKubernetesClusterLinuxProfile(d *schema.ResourceData) *containerservi if key, ok := linuxKeys[0].(map[string]interface{}); ok { keyData = key["key_data"].(string) } - sshPublicKey := containerservice.SSHPublicKey{ - KeyData: &keyData, - } - sshPublicKeys := []containerservice.SSHPublicKey{sshPublicKey} - - profile := containerservice.LinuxProfile{ + return &containerservice.LinuxProfile{ AdminUsername: &adminUsername, SSH: &containerservice.SSHConfiguration{ - PublicKeys: &sshPublicKeys, + PublicKeys: &[]containerservice.SSHPublicKey{ + { + KeyData: &keyData, + }, + }, }, } - - return &profile } func flattenKubernetesClusterLinuxProfile(profile *containerservice.LinuxProfile) []interface{} { @@ -1029,28 +1307,76 @@ func flattenKubernetesClusterLinuxProfile(profile *containerservice.LinuxProfile return []interface{}{} } - values := make(map[string]interface{}) - + adminUsername := "" if username := profile.AdminUsername; username != nil { - values["admin_username"] = *username + adminUsername = *username } sshKeys := make([]interface{}, 0) if ssh := profile.SSH; ssh != nil { if keys := ssh.PublicKeys; keys != nil { for _, sshKey := range *keys { - outputs := make(map[string]interface{}) - if keyData := sshKey.KeyData; keyData != nil { - outputs["key_data"] = *keyData + keyData := "" + if kd := sshKey.KeyData; kd != nil { + keyData = *kd } - sshKeys = append(sshKeys, outputs) + sshKeys = append(sshKeys, map[string]interface{}{ + "key_data": keyData, + }) } } } - values["ssh_key"] = sshKeys + return []interface{}{ + map[string]interface{}{ + "admin_username": adminUsername, + "ssh_key": sshKeys, + }, + } +} + +func expandKubernetesClusterWindowsProfile(d *schema.ResourceData) *containerservice.ManagedClusterWindowsProfile { + profiles := d.Get("windows_profile").([]interface{}) - return []interface{}{values} + if len(profiles) == 0 { + return nil + } + + config := profiles[0].(map[string]interface{}) + + adminUsername := config["admin_username"].(string) + adminPassword := config["admin_password"].(string) + + profile := containerservice.ManagedClusterWindowsProfile{ + AdminUsername: &adminUsername, + AdminPassword: &adminPassword, + } + + return &profile +} + +func flattenKubernetesClusterWindowsProfile(profile *containerservice.ManagedClusterWindowsProfile, d *schema.ResourceData) []interface{} { + if profile == nil { + return []interface{}{} + } + + adminUsername := "" + if username := profile.AdminUsername; username != nil { + adminUsername = *username + } + + // admin password isn't returned, so let's look it up + adminPassword := "" + if v, ok := d.GetOk("windows_profile.0.admin_password"); ok { + adminPassword = v.(string) + } + + return []interface{}{ + map[string]interface{}{ + "admin_password": adminPassword, + "admin_username": adminUsername, + }, + } } func expandKubernetesClusterNetworkProfile(d *schema.ResourceData) *containerservice.NetworkProfileType { @@ -1062,12 +1388,13 @@ func expandKubernetesClusterNetworkProfile(d *schema.ResourceData) *containerser config := configs[0].(map[string]interface{}) networkPlugin := config["network_plugin"].(string) - networkPolicy := config["network_policy"].(string) + loadBalancerSku := config["load_balancer_sku"].(string) networkProfile := containerservice.NetworkProfileType{ - NetworkPlugin: containerservice.NetworkPlugin(networkPlugin), - NetworkPolicy: containerservice.NetworkPolicy(networkPolicy), + NetworkPlugin: containerservice.NetworkPlugin(networkPlugin), + NetworkPolicy: containerservice.NetworkPolicy(networkPolicy), + LoadBalancerSku: containerservice.LoadBalancerSku(loadBalancerSku), } if v, ok := config["dns_service_ip"]; ok && v.(string) != "" { @@ -1098,31 +1425,37 @@ func flattenKubernetesClusterNetworkProfile(profile *containerservice.NetworkPro return []interface{}{} } - values := make(map[string]interface{}) - - values["network_plugin"] = profile.NetworkPlugin - - if profile.NetworkPolicy != "" { - values["network_policy"] = string(profile.NetworkPolicy) - } - - if profile.ServiceCidr != nil { - values["service_cidr"] = *profile.ServiceCidr - } - + dnsServiceIP := "" if profile.DNSServiceIP != nil { - values["dns_service_ip"] = *profile.DNSServiceIP + dnsServiceIP = *profile.DNSServiceIP } + dockerBridgeCidr := "" if profile.DockerBridgeCidr != nil { - values["docker_bridge_cidr"] = *profile.DockerBridgeCidr + dockerBridgeCidr = *profile.DockerBridgeCidr } + serviceCidr := "" + if profile.ServiceCidr != nil { + serviceCidr = *profile.ServiceCidr + } + + podCidr := "" if profile.PodCidr != nil { - values["pod_cidr"] = *profile.PodCidr + podCidr = *profile.PodCidr } - return []interface{}{values} + return []interface{}{ + map[string]interface{}{ + "dns_service_ip": dnsServiceIP, + "docker_bridge_cidr": dockerBridgeCidr, + "load_balancer_sku": string(profile.LoadBalancerSku), + "network_plugin": string(profile.NetworkPlugin), + "network_policy": string(profile.NetworkPolicy), + "pod_cidr": podCidr, + "service_cidr": serviceCidr, + }, + } } func expandKubernetesClusterRoleBasedAccessControl(input []interface{}, providerTenantId string) (bool, *containerservice.ManagedClusterAADProfile) { @@ -1167,16 +1500,17 @@ func flattenKubernetesClusterRoleBasedAccessControl(input *containerservice.Mana results := make([]interface{}, 0) if profile := input.AadProfile; profile != nil { - output := make(map[string]interface{}) - + clientAppId := "" if profile.ClientAppID != nil { - output["client_app_id"] = *profile.ClientAppID + clientAppId = *profile.ClientAppID } + serverAppId := "" if profile.ServerAppID != nil { - output["server_app_id"] = *profile.ServerAppID + serverAppId = *profile.ServerAppID } + serverAppSecret := "" // since input.ServerAppSecret isn't returned we're pulling this out of the existing state (which won't work for Imports) // role_based_access_control.0.azure_active_directory.0.server_app_secret if existing, ok := d.GetOk("role_based_access_control"); ok { @@ -1187,17 +1521,23 @@ func flattenKubernetesClusterRoleBasedAccessControl(input *containerservice.Mana azureADVal := azureADVals[0].(map[string]interface{}) v := azureADVal["server_app_secret"] if v != nil { - output["server_app_secret"] = v.(string) + serverAppSecret = v.(string) } } } } + tenantId := "" if profile.TenantID != nil { - output["tenant_id"] = *profile.TenantID + tenantId = *profile.TenantID } - results = append(results, output) + results = append(results, map[string]interface{}{ + "client_app_id": clientAppId, + "server_app_id": serverAppId, + "server_app_secret": serverAppSecret, + "tenant_id": tenantId, + }) } return []interface{}{ @@ -1210,92 +1550,90 @@ func flattenKubernetesClusterRoleBasedAccessControl(input *containerservice.Mana func expandAzureRmKubernetesClusterServicePrincipal(d *schema.ResourceData) *containerservice.ManagedClusterServicePrincipalProfile { value, exists := d.GetOk("service_principal") - if !exists { + configs := value.([]interface{}) + + if !exists || len(configs) == 0 { return nil } - configs := value.(*schema.Set).List() - config := configs[0].(map[string]interface{}) clientId := config["client_id"].(string) clientSecret := config["client_secret"].(string) - principal := containerservice.ManagedClusterServicePrincipalProfile{ + return &containerservice.ManagedClusterServicePrincipalProfile{ ClientID: &clientId, Secret: &clientSecret, } - - return &principal } -func flattenAzureRmKubernetesClusterServicePrincipalProfile(profile *containerservice.ManagedClusterServicePrincipalProfile) *schema.Set { +func flattenAzureRmKubernetesClusterServicePrincipalProfile(profile *containerservice.ManagedClusterServicePrincipalProfile, d *schema.ResourceData) []interface{} { if profile == nil { - return nil + return []interface{}{} } - servicePrincipalProfiles := &schema.Set{ - F: resourceKubernetesClusterServicePrincipalProfileHash, + clientId := "" + if v := profile.ClientID; v != nil { + clientId = *v } - values := make(map[string]interface{}) - - if clientId := profile.ClientID; clientId != nil { - values["client_id"] = *clientId - } - if secret := profile.Secret; secret != nil { - values["client_secret"] = *secret - } + // client secret isn't returned by the API so pass the existing value along + clientSecret := "" + if sp, ok := d.GetOk("service_principal"); ok { + var val []interface{} - servicePrincipalProfiles.Add(values) - - return servicePrincipalProfiles -} - -func resourceKubernetesClusterServicePrincipalProfileHash(v interface{}) int { - // TODO: this method should be able to be removed in time - var buf bytes.Buffer + // prior to 1.34 this was a *schema.Set, now it's a List - try both + if v, ok := sp.([]interface{}); ok { + val = v + } else if v, ok := sp.(*schema.Set); ok { + val = v.List() + } - if m, ok := v.(map[string]interface{}); ok { - buf.WriteString(fmt.Sprintf("%s-", m["client_id"].(string))) + if len(val) > 0 { + raw := val[0].(map[string]interface{}) + clientSecret = raw["client_secret"].(string) + } } - return hashcode.String(buf.String()) + return []interface{}{ + map[string]interface{}{ + "client_id": clientId, + "client_secret": clientSecret, + }, + } } func flattenKubernetesClusterKubeConfig(config kubernetes.KubeConfig) []interface{} { - values := make(map[string]interface{}) - // we don't size-check these since they're validated in the Parse method cluster := config.Clusters[0].Cluster user := config.Users[0].User name := config.Users[0].Name - values["host"] = cluster.Server - values["username"] = name - values["password"] = user.Token - values["client_certificate"] = user.ClientCertificteData - values["client_key"] = user.ClientKeyData - values["cluster_ca_certificate"] = cluster.ClusterAuthorityData - - return []interface{}{values} + return []interface{}{ + map[string]interface{}{ + "client_certificate": user.ClientCertificteData, + "client_key": user.ClientKeyData, + "cluster_ca_certificate": cluster.ClusterAuthorityData, + "host": cluster.Server, + "password": user.Token, + "username": name, + }, + } } func flattenKubernetesClusterKubeConfigAAD(config kubernetes.KubeConfigAAD) []interface{} { - values := make(map[string]interface{}) - // we don't size-check these since they're validated in the Parse method cluster := config.Clusters[0].Cluster name := config.Users[0].Name - values["host"] = cluster.Server - values["username"] = name - - values["password"] = "" - values["client_certificate"] = "" - values["client_key"] = "" - - values["cluster_ca_certificate"] = cluster.ClusterAuthorityData - - return []interface{}{values} + return []interface{}{ + map[string]interface{}{ + "client_certificate": "", + "client_key": "", + "cluster_ca_certificate": cluster.ClusterAuthorityData, + "host": cluster.Server, + "password": "", + "username": name, + }, + } } diff --git a/azurerm/resource_arm_kubernetes_cluster_test.go b/azurerm/resource_arm_kubernetes_cluster_test.go index 67fd26c6171d..3ee78bdd04cb 100644 --- a/azurerm/resource_arm_kubernetes_cluster_test.go +++ b/azurerm/resource_arm_kubernetes_cluster_test.go @@ -9,8 +9,12 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) +var olderKubernetesVersion = "1.13.10" +var currentKubernetesVersion = "1.14.6" + func TestAccAzureRMKubernetesCluster_basic(t *testing.T) { resourceName := "azurerm_kubernetes_cluster.test" ri := tf.AccRandTimeInt() @@ -39,19 +43,21 @@ func TestAccAzureRMKubernetesCluster_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "kube_admin_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "kube_admin_config_raw", ""), resource.TestCheckResourceAttrSet(resourceName, "agent_pool_profile.0.max_pods"), + resource.TestCheckResourceAttr(resourceName, "network_profile.0.load_balancer_sku", "Basic"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, }, }, }) } func TestAccAzureRMKubernetesCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -105,9 +111,10 @@ func TestAccAzureRMKubernetesCluster_roleBasedAccessControl(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, }, }, }) @@ -142,10 +149,13 @@ func TestAccAzureRMKubernetesCluster_roleBasedAccessControlAAD(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"role_based_access_control.0.azure_active_directory.0.server_app_secret"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "service_principal.0.client_secret", + "role_based_access_control.0.azure_active_directory.0.server_app_secret", + }, }, { // should be no changes since the default for Tenant ID comes from the Provider block @@ -155,22 +165,61 @@ func TestAccAzureRMKubernetesCluster_roleBasedAccessControlAAD(t *testing.T) { testCheckAzureRMKubernetesClusterExists(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "service_principal.0.client_secret", + "role_based_access_control.0.azure_active_directory.0.server_app_secret", + }, + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_linuxProfile(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_linuxProfile(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.client_key"), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.client_certificate"), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.cluster_ca_certificate"), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.host"), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.username"), + resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.password"), + resource.TestCheckResourceAttrSet(resourceName, "agent_pool_profile.0.max_pods"), + resource.TestCheckResourceAttrSet(resourceName, "linux_profile.0.admin_username"), + ), + }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"role_based_access_control.0.azure_active_directory.0.server_app_secret"}, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, }, }, }) } -func TestAccAzureRMKubernetesCluster_linuxProfile(t *testing.T) { +func TestAccAzureRMKubernetesCluster_windowsProfile(t *testing.T) { resourceName := "azurerm_kubernetes_cluster.test" ri := tf.AccRandTimeInt() clientId := os.Getenv("ARM_CLIENT_ID") clientSecret := os.Getenv("ARM_CLIENT_SECRET") - config := testAccAzureRMKubernetesCluster_linuxProfile(ri, clientId, clientSecret, testLocation()) + config := testAccAzureRMKubernetesCluster_windowsProfile(ri, clientId, clientSecret, testLocation()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -188,13 +237,19 @@ func TestAccAzureRMKubernetesCluster_linuxProfile(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.username"), resource.TestCheckResourceAttrSet(resourceName, "kube_config.0.password"), resource.TestCheckResourceAttrSet(resourceName, "agent_pool_profile.0.max_pods"), + resource.TestCheckResourceAttrSet(resourceName, "agent_pool_profile.1.max_pods"), resource.TestCheckResourceAttrSet(resourceName, "linux_profile.0.admin_username"), + resource.TestCheckResourceAttrSet(resourceName, "windows_profile.0.admin_username"), ), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "windows_profile.0.admin_password", + "service_principal.0.client_secret", + }, }, }, }) @@ -242,17 +297,17 @@ func TestAccAzureRMKubernetesCluster_upgradeConfig(t *testing.T) { CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMKubernetesCluster_upgrade(ri, location, clientId, clientSecret, "1.10.9"), + Config: testAccAzureRMKubernetesCluster_upgrade(ri, location, clientId, clientSecret, olderKubernetesVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMKubernetesClusterExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "kubernetes_version", "1.10.9"), + resource.TestCheckResourceAttr(resourceName, "kubernetes_version", olderKubernetesVersion), ), }, { - Config: testAccAzureRMKubernetesCluster_upgrade(ri, location, clientId, clientSecret, "1.11.5"), + Config: testAccAzureRMKubernetesCluster_upgrade(ri, location, clientId, clientSecret, currentKubernetesVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMKubernetesClusterExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "kubernetes_version", "1.11.5"), + resource.TestCheckResourceAttr(resourceName, "kubernetes_version", currentKubernetesVersion), ), }, }, @@ -360,6 +415,30 @@ func TestAccAzureRMKubernetesCluster_addonProfileRouting(t *testing.T) { }) } +func TestAccAzureRMKubernetesCluster_addonProfileKubeDashboard(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_addonProfileKubeDashboard(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "addon_profile.0.kube_dashboard.#", "1"), + resource.TestCheckResourceAttr(resourceName, "addon_profile.0.kube_dashboard.0.enabled", "false"), + ), + }, + }, + }) +} + func TestAccAzureRMKubernetesCluster_advancedNetworkingKubenet(t *testing.T) { resourceName := "azurerm_kubernetes_cluster.test" ri := tf.AccRandTimeInt() @@ -548,6 +627,52 @@ func TestAccAzureRMKubernetesCluster_advancedNetworkingAzureNPMPolicyComplete(t }) } +func TestAccAzureRMKubernetesCluster_standardLoadBalancer(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_standardLoadBalancer(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_profile.0.load_balancer_sku", "Standard"), + ), + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_standardLoadBalancerComplete(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_standardLoadBalancerComplete(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_profile.0.load_balancer_sku", "Standard"), + ), + }, + }, + }) +} + func TestAccAzureRMKubernetesCluster_apiServerAuthorizedIPRanges(t *testing.T) { resourceName := "azurerm_kubernetes_cluster.test" ri := tf.AccRandTimeInt() @@ -580,9 +705,10 @@ func TestAccAzureRMKubernetesCluster_apiServerAuthorizedIPRanges(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, }, }, }) @@ -620,9 +746,77 @@ func TestAccAzureRMKubernetesCluster_virtualMachineScaleSets(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_autoScalingNoAvailabilityZones(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_autoscaleNoAvailabilityZones(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.type", "VirtualMachineScaleSets"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.min_count", "1"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.max_count", "2"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.enable_auto_scaling", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_autoScalingWithAvailabilityZones(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_autoscaleWithAvailabilityZones(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.type", "VirtualMachineScaleSets"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.min_count", "1"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.max_count", "2"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.enable_auto_scaling", "true"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.availability_zones.#", "2"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.availability_zones.0", "1"), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.0.availability_zones.1", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, }, }, }) @@ -652,6 +846,86 @@ func TestAccAzureRMKubernetesCluster_multipleAgents(t *testing.T) { }) } +func TestAccAzureRMKubernetesCluster_nodeTaints(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_nodeTaints(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "agent_pool_profile.1.node_taints.0", "key=value:NoSchedule"), + ), + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_nodeResourceGroup(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_nodeResourceGroup(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, + }, + }, + }) +} + +func TestAccAzureRMKubernetesCluster_enablePodSecurityPolicy(t *testing.T) { + resourceName := "azurerm_kubernetes_cluster.test" + ri := tf.AccRandTimeInt() + clientId := os.Getenv("ARM_CLIENT_ID") + clientSecret := os.Getenv("ARM_CLIENT_SECRET") + config := testAccAzureRMKubernetesCluster_enablePodSecurityPolicy(ri, clientId, clientSecret, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKubernetesClusterDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKubernetesClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enable_pod_security_policy", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"service_principal.0.client_secret"}, + }, + }, + }) +} + func testCheckAzureRMKubernetesClusterExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -795,7 +1069,7 @@ resource "azurerm_kubernetes_cluster" "test" { `, rInt, location, rInt, rInt, rInt, clientId, clientSecret) } -func testAccAzureRMKubernetesCluster_addAgent(rInt int, clientId string, clientSecret string, location string) string { +func testAccAzureRMKubernetesCluster_windowsProfile(rInt int, clientId string, clientSecret string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" @@ -808,11 +1082,73 @@ resource "azurerm_kubernetes_cluster" "test" { resource_group_name = "${azurerm_resource_group.test.name}" dns_prefix = "acctestaks%d" - agent_pool_profile { - name = "default" - count = "2" - vm_size = "Standard_DS2_v2" - } + linux_profile { + admin_username = "acctestuser%d" + + ssh_key { + key_data = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqaZoyiz1qbdOQ8xEf6uEu1cCwYowo5FHtsBhqLoDnnp7KUTEBN+L2NxRIfQ781rxV6Iq5jSav6b2Q8z5KiseOlvKA/RF2wqU0UPYqQviQhLmW6THTpmrv/YkUCuzxDpsH7DUDhZcwySLKVVe0Qm3+5N2Ta6UYH3lsDf9R9wTP2K/+vAnflKebuypNlmocIvakFWoZda18FOmsOoIVXQ8HWFNCuw9ZCunMSN62QGamCe3dL5cXlkgHYv7ekJE15IA9aOJcM7e90oeTqo+7HTcWfdu0qQqPWY5ujyMw/llas8tsXY85LFqRnr3gJ02bAscjc477+X+j/gkpFoN1QEmt terraform@demo.tld" + } + } + + windows_profile { + admin_username = "azureuser" + admin_password = "pass_123-worD" + } + + agent_pool_profile { + name = "linux" + type = "VirtualMachineScaleSets" + count = "1" + vm_size = "Standard_DS2_v2" + max_pods = 30 + os_type = "Linux" + os_disk_size_gb = "30" + } + + agent_pool_profile { + name = "win" + type = "VirtualMachineScaleSets" + count = "1" + vm_size = "Standard_DS3_v2" + max_pods = 30 + os_type = "Windows" + os_disk_size_gb = "30" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } + + network_profile { + network_plugin = "azure" + network_policy = "azure" + dns_service_ip = "10.10.0.10" + docker_bridge_cidr = "172.18.0.1/16" + service_cidr = "10.10.0.0/16" + } +} +`, rInt, location, rInt, rInt, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_addAgent(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + + agent_pool_profile { + name = "default" + count = "2" + vm_size = "Standard_DS2_v2" + } service_principal { client_id = "%s" @@ -934,6 +1270,11 @@ resource "azurerm_subnet" "test" { resource_group_name = "${azurerm_resource_group.test.name}" virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "172.0.2.0/24" + + # TODO: remove in 2.0 + lifecycle { + ignore_changes = ["route_table_id"] + } } resource "azurerm_kubernetes_cluster" "test" { @@ -999,9 +1340,10 @@ resource "azurerm_subnet" "test-aci" { delegation { name = "aciDelegation" + service_delegation { name = "Microsoft.ContainerInstance/containerGroups" - actions = [ "Microsoft.Network/virtualNetworks/subnets/action" ] + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] } } } @@ -1149,6 +1491,47 @@ resource "azurerm_kubernetes_cluster" "test" { `, rInt, location, rInt, rInt, rInt, clientId, clientSecret) } +func testAccAzureRMKubernetesCluster_addonProfileKubeDashboard(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + + linux_profile { + admin_username = "acctestuser%d" + + ssh_key { + key_data = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqaZoyiz1qbdOQ8xEf6uEu1cCwYowo5FHtsBhqLoDnnp7KUTEBN+L2NxRIfQ781rxV6Iq5jSav6b2Q8z5KiseOlvKA/RF2wqU0UPYqQviQhLmW6THTpmrv/YkUCuzxDpsH7DUDhZcwySLKVVe0Qm3+5N2Ta6UYH3lsDf9R9wTP2K/+vAnflKebuypNlmocIvakFWoZda18FOmsOoIVXQ8HWFNCuw9ZCunMSN62QGamCe3dL5cXlkgHYv7ekJE15IA9aOJcM7e90oeTqo+7HTcWfdu0qQqPWY5ujyMw/llas8tsXY85LFqRnr3gJ02bAscjc477+X+j/gkpFoN1QEmt terraform@demo.tld" + } + } + + agent_pool_profile { + name = "default" + count = "1" + vm_size = "Standard_DS2_v2" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } + + addon_profile { + kube_dashboard { + enabled = false + } + } +} +`, rInt, location, rInt, rInt, rInt, clientId, clientSecret) +} + func testAccAzureRMKubernetesCluster_upgrade(rInt int, location, clientId, clientSecret, version string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -1208,6 +1591,11 @@ resource "azurerm_subnet" "test" { resource_group_name = "${azurerm_resource_group.test.name}" virtual_network_name = "${azurerm_virtual_network.test.name}" address_prefix = "10.1.0.0/24" + + # TODO: remove in 2.0 + lifecycle { + ignore_changes = ["route_table_id"] + } } resource "azurerm_kubernetes_cluster" "test" { @@ -1463,6 +1851,149 @@ resource "azurerm_kubernetes_cluster" "test" { `, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt, clientId, clientSecret, networkPlugin, networkPolicy) } +func testAccAzureRMKubernetesCluster_standardLoadBalancer(rInt int, clientId string, clientSecret string, 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.1.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "Testing" + } +} + +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.1.0.0/24" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + kubernetes_version = "%s" + + + linux_profile { + admin_username = "acctestuser%d" + + ssh_key { + key_data = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqaZoyiz1qbdOQ8xEf6uEu1cCwYowo5FHtsBhqLoDnnp7KUTEBN+L2NxRIfQ781rxV6Iq5jSav6b2Q8z5KiseOlvKA/RF2wqU0UPYqQviQhLmW6THTpmrv/YkUCuzxDpsH7DUDhZcwySLKVVe0Qm3+5N2Ta6UYH3lsDf9R9wTP2K/+vAnflKebuypNlmocIvakFWoZda18FOmsOoIVXQ8HWFNCuw9ZCunMSN62QGamCe3dL5cXlkgHYv7ekJE15IA9aOJcM7e90oeTqo+7HTcWfdu0qQqPWY5ujyMw/llas8tsXY85LFqRnr3gJ02bAscjc477+X+j/gkpFoN1QEmt terraform@demo.tld" + } + } + + agent_pool_profile { + name = "default" + count = "2" + vm_size = "Standard_DS2_v2" + vnet_subnet_id = "${azurerm_subnet.test.id}" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } + + network_profile { + network_plugin = "azure" + load_balancer_sku = "Standard" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, currentKubernetesVersion, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_standardLoadBalancerComplete(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_route_table" "test" { + name = "akc-routetable-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + route { + name = "akc-route-%d" + address_prefix = "10.100.0.0/14" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "10.10.1.1" + } +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.1.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "Testing" + } +} + +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.1.0.0/24" + route_table_id = "${azurerm_route_table.test.id}" +} + +resource "azurerm_subnet_route_table_association" "test" { + subnet_id = "${azurerm_subnet.test.id}" + route_table_id = "${azurerm_route_table.test.id}" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + kubernetes_version = "%s" + + linux_profile { + admin_username = "acctestuser%d" + + ssh_key { + key_data = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqaZoyiz1qbdOQ8xEf6uEu1cCwYowo5FHtsBhqLoDnnp7KUTEBN+L2NxRIfQ781rxV6Iq5jSav6b2Q8z5KiseOlvKA/RF2wqU0UPYqQviQhLmW6THTpmrv/YkUCuzxDpsH7DUDhZcwySLKVVe0Qm3+5N2Ta6UYH3lsDf9R9wTP2K/+vAnflKebuypNlmocIvakFWoZda18FOmsOoIVXQ8HWFNCuw9ZCunMSN62QGamCe3dL5cXlkgHYv7ekJE15IA9aOJcM7e90oeTqo+7HTcWfdu0qQqPWY5ujyMw/llas8tsXY85LFqRnr3gJ02bAscjc477+X+j/gkpFoN1QEmt terraform@demo.tld" + } + } + + agent_pool_profile { + name = "default" + count = "2" + vm_size = "Standard_DS2_v2" + vnet_subnet_id = "${azurerm_subnet.test.id}" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } + + network_profile { + network_plugin = "azure" + dns_service_ip = "10.10.0.10" + docker_bridge_cidr = "172.18.0.1/16" + service_cidr = "10.10.0.0/16" + load_balancer_sku = "Standard" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, currentKubernetesVersion, rInt, clientId, clientSecret) +} + func testAccAzureRMKubernetesCluster_apiServerAuthorizedIPRanges(rInt int, clientId string, clientSecret string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -1488,9 +2019,9 @@ resource "azurerm_kubernetes_cluster" "test" { } api_server_authorized_ip_ranges = [ - "8.8.8.8/32", - "8.8.4.4/32", - "8.8.2.0/24", + "8.8.8.8/32", + "8.8.4.4/32", + "8.8.2.0/24", ] } `, rInt, location, rInt, rInt, clientId, clientSecret) @@ -1549,6 +2080,171 @@ resource "azurerm_kubernetes_cluster" "test" { vm_size = "Standard_DS2_v2" } + service_principal { + client_id = "%s" + client_secret = "%s" + } +} +`, rInt, location, rInt, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_autoscaleNoAvailabilityZones(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + + agent_pool_profile { + name = "pool1" + min_count = "1" + max_count = "2" + enable_auto_scaling = "true" + type = "VirtualMachineScaleSets" + vm_size = "Standard_DS2_v2" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } +} +`, rInt, location, rInt, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_autoscaleWithAvailabilityZones(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + kubernetes_version = "%s" + + agent_pool_profile { + name = "pool1" + min_count = "1" + max_count = "2" + enable_auto_scaling = "true" + type = "VirtualMachineScaleSets" + vm_size = "Standard_DS2_v2" + availability_zones = ["1", "2"] + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } + + network_profile { + network_plugin = "kubenet" + load_balancer_sku = "Standard" + } +} +`, rInt, location, rInt, rInt, olderKubernetesVersion, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_nodeTaints(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + + agent_pool_profile { + name = "default" + count = "1" + type = "VirtualMachineScaleSets" + vm_size = "Standard_DS2_v2" + } + + agent_pool_profile { + name = "pool1" + count = "1" + type = "VirtualMachineScaleSets" + vm_size = "Standard_DS2_v2" + node_taints = [ + "key=value:NoSchedule" + ] + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } +} +`, rInt, location, rInt, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_nodeResourceGroup(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + node_resource_group = "acctestRGAKS-%d" + + agent_pool_profile { + name = "default" + count = "1" + type = "VirtualMachineScaleSets" + vm_size = "Standard_DS2_v2" + } + + service_principal { + client_id = "%s" + client_secret = "%s" + } +} +`, rInt, location, rInt, rInt, rInt, clientId, clientSecret) +} + +func testAccAzureRMKubernetesCluster_enablePodSecurityPolicy(rInt int, clientId string, clientSecret string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + dns_prefix = "acctestaks%d" + enable_pod_security_policy = true + + role_based_access_control { + enabled = true + } + + agent_pool_profile { + name = "default" + count = "1" + vm_size = "Standard_DS2_v2" + } service_principal { client_id = "%s" diff --git a/azurerm/resource_arm_kusto_cluster.go b/azurerm/resource_arm_kusto_cluster.go new file mode 100644 index 000000000000..7b01f6e4053f --- /dev/null +++ b/azurerm/resource_arm_kusto_cluster.go @@ -0,0 +1,275 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2019-05-15/kusto" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmKustoCluster() *schema.Resource { + return &schema.Resource{ + Create: resourceArmKustoClusterCreateUpdate, + Read: resourceArmKustoClusterRead, + Update: resourceArmKustoClusterCreateUpdate, + Delete: resourceArmKustoClusterDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMKustoClusterName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "sku": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(kusto.DevNoSLAStandardD11V2), + string(kusto.StandardD11V2), + string(kusto.StandardD12V2), + string(kusto.StandardD13V2), + string(kusto.StandardD14V2), + string(kusto.StandardDS13V21TBPS), + string(kusto.StandardDS13V22TBPS), + string(kusto.StandardDS14V23TBPS), + string(kusto.StandardDS14V24TBPS), + string(kusto.StandardL16s), + string(kusto.StandardL4s), + string(kusto.StandardL8s), + }, false), + }, + + "capacity": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 1000), + }, + }, + }, + }, + + "uri": { + Type: schema.TypeString, + Computed: true, + }, + + "data_ingestion_uri": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.ClustersClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Azure Kusto Cluster creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + server, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(server.Response) { + return fmt.Errorf("Error checking for presence of existing Kusto Cluster %q (Resource Group %q): %s", name, resourceGroup, err) + } + } + + if server.ID != nil && *server.ID != "" { + return tf.ImportAsExistsError("azurerm_kusto_cluster", *server.ID) + } + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + + sku, err := expandKustoClusterSku(d) + if err != nil { + return err + } + + clusterProperties := kusto.ClusterProperties{} + + t := d.Get("tags").(map[string]interface{}) + + kustoCluster := kusto.Cluster{ + Name: &name, + Location: &location, + Sku: sku, + ClusterProperties: &clusterProperties, + Tags: tags.Expand(t), + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, kustoCluster) + if err != nil { + return fmt.Errorf("Error creating or updating Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + resp, getDetailsErr := client.Get(ctx, resourceGroup, name) + if getDetailsErr != nil { + return fmt.Errorf("Error retrieving Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read ID for Kusto Cluster %q (Resource Group %q)", name, resourceGroup) + } + + d.SetId(*resp.ID) + + return resourceArmKustoClusterRead(d, meta) +} + +func resourceArmKustoClusterRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.ClustersClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + name := id.Path["Clusters"] + + clusterResponse, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(clusterResponse.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + + if location := clusterResponse.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if err := d.Set("sku", flattenKustoClusterSku(clusterResponse.Sku)); err != nil { + return fmt.Errorf("Error setting `sku`: %+v", err) + } + + if clusterProperties := clusterResponse.ClusterProperties; clusterProperties != nil { + d.Set("uri", clusterProperties.URI) + d.Set("data_ingestion_uri", clusterProperties.DataIngestionURI) + } + + return tags.FlattenAndSet(d, clusterResponse.Tags) +} + +func resourceArmKustoClusterDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.ClustersClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["Clusters"] + + future, err := client.Delete(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error deleting Kusto Cluster %q (Resource Group %q): %+v", name, resGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of Kusto Cluster %q (Resource Group %q): %+v", name, resGroup, err) + } + + return nil +} + +func validateAzureRMKustoClusterName(v interface{}, k string) (warnings []string, errors []error) { + name := v.(string) + + if !regexp.MustCompile(`^[a-z][a-z0-9]+$`).MatchString(name) { + errors = append(errors, fmt.Errorf("%q must begin with a letter and may only contain alphanumeric characters: %q", k, name)) + } + + if len(name) < 4 || len(name) > 22 { + errors = append(errors, fmt.Errorf("%q must be (inclusive) between 4 and 22 characters long but is %d", k, len(name))) + } + + return warnings, errors +} + +func expandKustoClusterSku(d *schema.ResourceData) (*kusto.AzureSku, error) { + skuList := d.Get("sku").([]interface{}) + + sku := skuList[0].(map[string]interface{}) + name := sku["name"].(string) + + skuNamePrefixToTier := map[string]string{ + "Dev(No SLA)": "Basic", + "Standard": "Standard", + } + + skuNamePrefix := strings.Split(sku["name"].(string), "_")[0] + tier, ok := skuNamePrefixToTier[skuNamePrefix] + if !ok { + return nil, fmt.Errorf("sku name begins with invalid tier, possible are Dev(No SLA) and Standard but is: %q", skuNamePrefix) + } + capacity := sku["capacity"].(int) + + azureSku := &kusto.AzureSku{ + Name: kusto.AzureSkuName(name), + Tier: kusto.AzureSkuTier(tier), + Capacity: utils.Int32(int32(capacity)), + } + + return azureSku, nil +} + +func flattenKustoClusterSku(sku *kusto.AzureSku) []interface{} { + if sku == nil { + return []interface{}{} + } + + s := map[string]interface{}{ + "name": string(sku.Name), + } + + if sku.Capacity != nil { + s["capacity"] = int(*sku.Capacity) + } + + return []interface{}{s} +} diff --git a/azurerm/resource_arm_kusto_cluster_test.go b/azurerm/resource_arm_kusto_cluster_test.go new file mode 100644 index 000000000000..b9e213627de9 --- /dev/null +++ b/azurerm/resource_arm_kusto_cluster_test.go @@ -0,0 +1,247 @@ +package azurerm + +import ( + "fmt" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMKustoCluster_basic(t *testing.T) { + resourceName := "azurerm_kusto_cluster.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMKustoCluster_basic(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMKustoCluster_withTags(t *testing.T) { + resourceName := "azurerm_kusto_cluster.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + preConfig := testAccAzureRMKustoCluster_withTags(ri, rs, testLocation()) + postConfig := testAccAzureRMKustoCluster_withTagsUpdate(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoClusterDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.label", "test1"), + resource.TestCheckResourceAttr(resourceName, "tags.ENV", "prod"), + ), + }, + }, + }) +} + +func TestAccAzureRMKustoCluster_sku(t *testing.T) { + resourceName := "azurerm_kusto_cluster.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + preConfig := testAccAzureRMKustoCluster_basic(ri, rs, testLocation()) + postConfig := testAccAzureRMKustoCluster_skuUpdate(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoClusterDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "Dev(No SLA)_Standard_D11_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.capacity", "1"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "Standard_D11_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.capacity", "2"), + ), + }, + }, + }) +} + +func testAccAzureRMKustoCluster_basic(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} +`, rInt, location, rs) +} + +func testAccAzureRMKustoCluster_withTags(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } + + tags = { + label = "test" + } +} +`, rInt, location, rs) +} + +func testAccAzureRMKustoCluster_withTagsUpdate(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } + + tags = { + label = "test1" + ENV = "prod" + } +} +`, rInt, location, rs) +} + +func testAccAzureRMKustoCluster_skuUpdate(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Standard_D11_v2" + capacity = 2 + } +} +`, rInt, location, rs) +} + +func testCheckAzureRMKustoClusterDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).kusto.ClustersClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_kusto_cluster" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return nil + } + + return nil +} + +func testCheckAzureRMKustoClusterExists(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) + } + + kustoCluster := 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 Kusto Cluster: %s", kustoCluster) + } + + client := testAccProvider.Meta().(*ArmClient).kusto.ClustersClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, kustoCluster) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Kusto Cluster %q (resource group: %q) does not exist", kustoCluster, resourceGroup) + } + + return fmt.Errorf("Bad: Get on ClustersClient: %+v", err) + } + + return nil + } +} diff --git a/azurerm/resource_arm_kusto_database.go b/azurerm/resource_arm_kusto_database.go new file mode 100644 index 000000000000..60e4af4fe081 --- /dev/null +++ b/azurerm/resource_arm_kusto_database.go @@ -0,0 +1,221 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2019-05-15/kusto" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmKustoDatabase() *schema.Resource { + return &schema.Resource{ + Create: resourceArmKustoDatabaseCreateUpdate, + Read: resourceArmKustoDatabaseRead, + Update: resourceArmKustoDatabaseCreateUpdate, + Delete: resourceArmKustoDatabaseDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMKustoDatabaseName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "cluster_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureRMKustoClusterName, + }, + + "soft_delete_period": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.ISO8601Duration, + }, + + "hot_cache_period": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.ISO8601Duration, + }, + + "size": { + Type: schema.TypeFloat, + Computed: true, + }, + }, + } +} + +func resourceArmKustoDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.DatabasesClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Azure Kusto Database creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + clusterName := d.Get("cluster_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + server, err := client.Get(ctx, resourceGroup, clusterName, name) + if err != nil { + if !utils.ResponseWasNotFound(server.Response) { + return fmt.Errorf("Error checking for presence of existing Kusto Database %q (Resource Group %q, Cluster %q): %s", name, resourceGroup, clusterName, err) + } + } + + if server.ID != nil && *server.ID != "" { + return tf.ImportAsExistsError("azurerm_kusto_database", *server.ID) + } + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + + databaseProperties := expandKustoDatabaseProperties(d) + + kustoDatabase := kusto.Database{ + Name: &name, + Location: &location, + DatabaseProperties: databaseProperties, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, clusterName, name, kustoDatabase) + if err != nil { + return fmt.Errorf("Error creating or updating Kusto Cluster %q (Resource Group %q, Cluster %q): %+v", name, resourceGroup, clusterName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Kusto Cluster %q (Resource Group %q, Cluster %q): %+v", name, resourceGroup, clusterName, err) + } + + resp, getDetailsErr := client.Get(ctx, resourceGroup, clusterName, name) + if getDetailsErr != nil { + return fmt.Errorf("Error retrieving Kusto Cluster %q (Resource Group %q, Cluster %q): %+v", name, resourceGroup, clusterName, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read ID for Kusto Cluster %q (Resource Group %q, Cluster %q)", name, resourceGroup, clusterName) + } + + d.SetId(*resp.ID) + + return resourceArmKustoDatabaseRead(d, meta) +} + +func resourceArmKustoDatabaseRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.DatabasesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + clusterName := id.Path["Clusters"] + name := id.Path["Databases"] + + databaseResponse, err := client.Get(ctx, resourceGroup, clusterName, name) + + if err != nil { + if utils.ResponseWasNotFound(databaseResponse.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Kusto Database %q (Resource Group %q, Cluster %q): %+v", name, resourceGroup, clusterName, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + d.Set("cluster_name", clusterName) + + if location := databaseResponse.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if props := databaseResponse.DatabaseProperties; props != nil { + d.Set("hot_cache_period", props.HotCachePeriod) + d.Set("soft_delete_period", props.SoftDeletePeriod) + + if statistics := props.Statistics; statistics != nil { + d.Set("size", statistics.Size) + } + } + + return nil +} + +func resourceArmKustoDatabaseDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).kusto.DatabasesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + clusterName := id.Path["Clusters"] + name := id.Path["Databases"] + + future, err := client.Delete(ctx, resGroup, clusterName, name) + if err != nil { + return fmt.Errorf("Error deleting Kusto Database %q (Resource Group %q, Cluster %q): %+v", name, resGroup, clusterName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of Kusto Database %q (Resource Group %q, Cluster %q): %+v", name, resGroup, clusterName, err) + } + + return nil +} + +func validateAzureRMKustoDatabaseName(v interface{}, k string) (warnings []string, errors []error) { + name := v.(string) + + if regexp.MustCompile(`^[\s]+$`).MatchString(name) { + errors = append(errors, fmt.Errorf("%q must not consist of whitespaces only", k)) + } + + if !regexp.MustCompile(`^[a-zA-Z0-9\s.-]+$`).MatchString(name) { + errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters, whitespaces, dashes and dots: %q", k, name)) + } + + if len(name) > 260 { + errors = append(errors, fmt.Errorf("%q must be (inclusive) between 4 and 22 characters long but is %d", k, len(name))) + } + + return warnings, errors +} + +func expandKustoDatabaseProperties(d *schema.ResourceData) *kusto.DatabaseProperties { + databaseProperties := &kusto.DatabaseProperties{} + + if softDeletePeriod, ok := d.GetOk("soft_delete_period"); ok { + databaseProperties.SoftDeletePeriod = utils.String(softDeletePeriod.(string)) + } + + if hotCachePeriod, ok := d.GetOk("hot_cache_period"); ok { + databaseProperties.HotCachePeriod = utils.String(hotCachePeriod.(string)) + } + + return databaseProperties +} diff --git a/azurerm/resource_arm_kusto_database_test.go b/azurerm/resource_arm_kusto_database_test.go new file mode 100644 index 000000000000..cbe2bc04017e --- /dev/null +++ b/azurerm/resource_arm_kusto_database_test.go @@ -0,0 +1,302 @@ +package azurerm + +import ( + "fmt" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMKustoDatabase_basic(t *testing.T) { + resourceName := "azurerm_kusto_database.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMKustoDatabase_basic(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoDatabaseExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMKustoDatabase_softDeletePeriod(t *testing.T) { + resourceName := "azurerm_kusto_database.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + preConfig := testAccAzureRMKustoDatabase_softDeletePeriod(ri, rs, testLocation()) + postConfig := testAccAzureRMKustoDatabase_softDeletePeriodUpdate(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "soft_delete_period", "P7D"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "soft_delete_period", "P31D"), + ), + }, + }, + }) +} + +func TestAccAzureRMKustoDatabase_hotCachePeriod(t *testing.T) { + resourceName := "azurerm_kusto_database.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + preConfig := testAccAzureRMKustoDatabase_hotCachePeriod(ri, rs, testLocation()) + postConfig := testAccAzureRMKustoDatabase_hotCachePeriodUpdate(ri, rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKustoDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "hot_cache_period", "P7D"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "hot_cache_period", "P14DT12H"), + ), + }, + }, + }) +} + +func testAccAzureRMKustoDatabase_basic(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "cluster" { + name = "acctestkc%s" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} + +resource "azurerm_kusto_database" "test" { + name = "acctestkd-%d" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + cluster_name = azurerm_kusto_cluster.cluster.name +} +`, rInt, location, rs, rInt) +} + +func testAccAzureRMKustoDatabase_softDeletePeriod(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "cluster" { + name = "acctestkc%s" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} + +resource "azurerm_kusto_database" "test" { + name = "acctestkd-%d" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + cluster_name = azurerm_kusto_cluster.cluster.name + + soft_delete_period = "P7D" +} +`, rInt, location, rs, rInt) +} + +func testAccAzureRMKustoDatabase_softDeletePeriodUpdate(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "cluster" { + name = "acctestkc%s" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} + +resource "azurerm_kusto_database" "test" { + name = "acctestkd-%d" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + cluster_name = azurerm_kusto_cluster.cluster.name + + soft_delete_period = "P31D" +} +`, rInt, location, rs, rInt) +} + +func testAccAzureRMKustoDatabase_hotCachePeriod(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "cluster" { + name = "acctestkc%s" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} + +resource "azurerm_kusto_database" "test" { + name = "acctestkd-%d" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + cluster_name = azurerm_kusto_cluster.cluster.name + + hot_cache_period = "P7D" +} +`, rInt, location, rs, rInt) +} + +func testAccAzureRMKustoDatabase_hotCachePeriodUpdate(rInt int, rs string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "rg" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "cluster" { + name = "acctestkc%s" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } +} + +resource "azurerm_kusto_database" "test" { + name = "acctestkd-%d" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + cluster_name = azurerm_kusto_cluster.cluster.name + + hot_cache_period = "P14DT12H" +} +`, rInt, location, rs, rInt) +} + +func testCheckAzureRMKustoDatabaseDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).kusto.DatabasesClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_kusto_database" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + clusterName := rs.Primary.Attributes["cluster_name"] + name := rs.Primary.Attributes["name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, clusterName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return nil + } + + return nil +} + +func testCheckAzureRMKustoDatabaseExists(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) + } + + kustoDatabase := 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 Kusto Database: %s", kustoDatabase) + } + + clusterName, hasClusterName := rs.Primary.Attributes["cluster_name"] + if !hasClusterName { + return fmt.Errorf("Bad: no resource group found in state for Kusto Database: %s", kustoDatabase) + } + + client := testAccProvider.Meta().(*ArmClient).kusto.DatabasesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, clusterName, kustoDatabase) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Kusto Database %q (resource group: %q, cluster: %q) does not exist", kustoDatabase, resourceGroup, clusterName) + } + + return fmt.Errorf("Bad: Get on DatabasesClient: %+v", err) + } + + return nil + } +} diff --git a/azurerm/resource_arm_loadbalancer.go b/azurerm/resource_arm_loadbalancer.go index 469c446490f7..7aaa23cff99e 100644 --- a/azurerm/resource_arm_loadbalancer.go +++ b/azurerm/resource_arm_loadbalancer.go @@ -4,13 +4,16 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -81,6 +84,13 @@ func resourceArmLoadBalancer() *schema.Resource { ValidateFunc: azure.ValidateResourceIDOrEmpty, }, + "public_ip_prefix_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: azure.ValidateResourceIDOrEmpty, + }, + "private_ip_address_allocation": { Type: schema.TypeString, Optional: true, @@ -89,7 +99,7 @@ func resourceArmLoadBalancer() *schema.Resource { string(network.Dynamic), string(network.Static), }, true), - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, }, @@ -140,13 +150,13 @@ func resourceArmLoadBalancer() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmLoadBalancerCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ARM Load Balancer creation.") @@ -154,7 +164,7 @@ func resourceArmLoadBalancerCreateUpdate(d *schema.ResourceData, meta interface{ name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -171,8 +181,8 @@ func resourceArmLoadBalancerCreateUpdate(d *schema.ResourceData, meta interface{ sku := network.LoadBalancerSku{ Name: network.LoadBalancerSkuName(d.Get("sku").(string)), } - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) properties := network.LoadBalancerPropertiesFormat{} @@ -211,7 +221,7 @@ func resourceArmLoadBalancerCreateUpdate(d *schema.ResourceData, meta interface{ } func resourceArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -261,16 +271,14 @@ func resourceArmLoadBalancerRead(d *schema.ResourceData, meta interface{}) error } } - flattenAndSetTags(d, loadBalancer.Tags) - - return nil + return tags.FlattenAndSet(d, loadBalancer.Tags) } func resourceArmLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return fmt.Errorf("Error Parsing Azure Resource ID: %+v", err) } @@ -311,6 +319,12 @@ func expandAzureRmLoadBalancerFrontendIpConfigurations(d *schema.ResourceData) * } } + if v := data["public_ip_prefix_id"].(string); v != "" { + properties.PublicIPPrefix = &network.SubResource{ + ID: &v, + } + } + if v := data["subnet_id"].(string); v != "" { properties.Subnet = &network.Subnet{ ID: &v, @@ -365,6 +379,10 @@ func flattenLoadBalancerFrontendIpConfiguration(ipConfigs *[]network.FrontendIPC ipConfig["public_ip_address_id"] = *pip.ID } + if pip := props.PublicIPPrefix; pip != nil { + ipConfig["public_ip_prefix_id"] = *pip.ID + } + loadBalancingRules := make([]interface{}, 0) if rules := props.LoadBalancingRules; rules != nil { for _, rule := range *rules { @@ -378,7 +396,6 @@ func flattenLoadBalancerFrontendIpConfiguration(ipConfigs *[]network.FrontendIPC for _, rule := range *rules { inboundNatRules = append(inboundNatRules, *rule.ID) } - } ipConfig["inbound_nat_rules"] = schema.NewSet(schema.HashString, inboundNatRules) @@ -387,7 +404,6 @@ func flattenLoadBalancerFrontendIpConfiguration(ipConfigs *[]network.FrontendIPC for _, rule := range *rules { outboundRules = append(outboundRules, *rule.ID) } - } ipConfig["outbound_rules"] = schema.NewSet(schema.HashString, outboundRules) } diff --git a/azurerm/resource_arm_loadbalancer_backend_address_pool.go b/azurerm/resource_arm_loadbalancer_backend_address_pool.go index 86a17eebde7c..417afd07e2c8 100644 --- a/azurerm/resource_arm_loadbalancer_backend_address_pool.go +++ b/azurerm/resource_arm_loadbalancer_backend_address_pool.go @@ -4,11 +4,13 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -64,13 +66,13 @@ func resourceArmLoadBalancerBackendAddressPool() *schema.Resource { } func resourceArmLoadBalancerBackendAddressPoolCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) name := d.Get("name").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -86,7 +88,7 @@ func resourceArmLoadBalancerBackendAddressPoolCreate(d *schema.ResourceData, met existingPool, existingPoolIndex, exists := findLoadBalancerBackEndAddressPoolByName(loadBalancer, name) if exists { if name == *existingPool.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_backend_address_pool", *existingPool.ID) } @@ -135,7 +137,7 @@ func resourceArmLoadBalancerBackendAddressPoolCreate(d *schema.ResourceData, met } func resourceArmLoadBalancerBackendAddressPoolRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -185,12 +187,12 @@ func resourceArmLoadBalancerBackendAddressPoolRead(d *schema.ResourceData, meta } func resourceArmLoadBalancerBackendAddressPoolDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { diff --git a/azurerm/resource_arm_loadbalancer_backend_address_pool_test.go b/azurerm/resource_arm_loadbalancer_backend_address_pool_test.go index 3d035b4f7e9b..bd567ae3a40d 100644 --- a/azurerm/resource_arm_loadbalancer_backend_address_pool_test.go +++ b/azurerm/resource_arm_loadbalancer_backend_address_pool_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLoadBalancerBackEndAddressPool_basic(t *testing.T) { @@ -46,7 +48,7 @@ func TestAccAzureRMLoadBalancerBackEndAddressPool_basic(t *testing.T) { }) } func TestAccAzureRMLoadBalancerBackEndAddressPool_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -150,7 +152,7 @@ func testCheckAzureRMLoadBalancerBackEndAddressPoolNotExists(addressPoolName str func testCheckAzureRMLoadBalancerBackEndAddressPoolDisappears(addressPoolName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerBackEndAddressPoolByName(lb, addressPoolName) @@ -162,7 +164,7 @@ func testCheckAzureRMLoadBalancerBackEndAddressPoolDisappears(addressPoolName st pools := append(currentPools[:i], currentPools[i+1:]...) lb.LoadBalancerPropertiesFormat.BackendAddressPools = &pools - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } diff --git a/azurerm/resource_arm_loadbalancer_nat_pool.go b/azurerm/resource_arm_loadbalancer_nat_pool.go index d9c777d88008..38718b68a939 100644 --- a/azurerm/resource_arm_loadbalancer_nat_pool.go +++ b/azurerm/resource_arm_loadbalancer_nat_pool.go @@ -4,13 +4,16 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,7 +49,7 @@ func resourceArmLoadBalancerNatPool() *schema.Resource { "protocol": { Type: schema.TypeString, Required: true, - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.TransportProtocolAll), @@ -88,13 +91,13 @@ func resourceArmLoadBalancerNatPool() *schema.Resource { } func resourceArmLoadBalancerNatPoolCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) name := d.Get("name").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -116,7 +119,7 @@ func resourceArmLoadBalancerNatPoolCreateUpdate(d *schema.ResourceData, meta int existingNatPool, existingNatPoolIndex, exists := findLoadBalancerNatPoolByName(loadBalancer, name) if exists { if name == *existingNatPool.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_nat_pool", *existingNatPool.ID) } @@ -165,7 +168,7 @@ func resourceArmLoadBalancerNatPoolCreateUpdate(d *schema.ResourceData, meta int } func resourceArmLoadBalancerNatPoolRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -198,7 +201,7 @@ func resourceArmLoadBalancerNatPoolRead(d *schema.ResourceData, meta interface{} d.Set("backend_port", props.BackendPort) if feipConfig := props.FrontendIPConfiguration; feipConfig != nil { - fipID, err := parseAzureResourceID(*feipConfig.ID) + fipID, err := azure.ParseAzureResourceID(*feipConfig.ID) if err != nil { return err } @@ -212,12 +215,12 @@ func resourceArmLoadBalancerNatPoolRead(d *schema.ResourceData, meta interface{} } func resourceArmLoadBalancerNatPoolDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -263,7 +266,6 @@ func resourceArmLoadBalancerNatPoolDelete(d *schema.ResourceData, meta interface } func expandAzureRmLoadBalancerNatPool(d *schema.ResourceData, lb *network.LoadBalancer) (*network.InboundNatPool, error) { - properties := network.InboundNatPoolPropertiesFormat{ Protocol: network.TransportProtocol(d.Get("protocol").(string)), FrontendPortRangeStart: utils.Int32(int32(d.Get("frontend_port_start").(int))), diff --git a/azurerm/resource_arm_loadbalancer_nat_pool_test.go b/azurerm/resource_arm_loadbalancer_nat_pool_test.go index af8cae5822c1..d21ec9ef808c 100644 --- a/azurerm/resource_arm_loadbalancer_nat_pool_test.go +++ b/azurerm/resource_arm_loadbalancer_nat_pool_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLoadBalancerNatPool_basic(t *testing.T) { @@ -47,7 +49,7 @@ func TestAccAzureRMLoadBalancerNatPool_basic(t *testing.T) { } func TestAccAzureRMLoadBalancerNatPool_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -192,7 +194,7 @@ func testCheckAzureRMLoadBalancerNatPoolNotExists(natPoolName string, lb *networ func testCheckAzureRMLoadBalancerNatPoolDisappears(natPoolName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerNatPoolByName(lb, natPoolName) @@ -204,7 +206,7 @@ func testCheckAzureRMLoadBalancerNatPoolDisappears(natPoolName string, lb *netwo pools := append(currentPools[:i], currentPools[i+1:]...) lb.LoadBalancerPropertiesFormat.InboundNatPools = &pools - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } diff --git a/azurerm/resource_arm_loadbalancer_nat_rule.go b/azurerm/resource_arm_loadbalancer_nat_rule.go index 0bdb15306a10..ab0cafd26334 100644 --- a/azurerm/resource_arm_loadbalancer_nat_rule.go +++ b/azurerm/resource_arm_loadbalancer_nat_rule.go @@ -4,13 +4,16 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -47,7 +50,7 @@ func resourceArmLoadBalancerNatRule() *schema.Resource { "protocol": { Type: schema.TypeString, Required: true, - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.TransportProtocolAll), @@ -94,13 +97,13 @@ func resourceArmLoadBalancerNatRule() *schema.Resource { } func resourceArmLoadBalancerNatRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -122,7 +125,7 @@ func resourceArmLoadBalancerNatRuleCreateUpdate(d *schema.ResourceData, meta int existingNatRule, existingNatRuleIndex, exists := findLoadBalancerNatRuleByName(loadBalancer, name) if exists { if name == *existingNatRule.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_nat_rule", *existingNatRule.ID) } @@ -172,7 +175,7 @@ func resourceArmLoadBalancerNatRuleCreateUpdate(d *schema.ResourceData, meta int } func resourceArmLoadBalancerNatRuleRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -205,7 +208,7 @@ func resourceArmLoadBalancerNatRuleRead(d *schema.ResourceData, meta interface{} d.Set("enable_floating_ip", props.EnableFloatingIP) if ipconfiguration := props.FrontendIPConfiguration; ipconfiguration != nil { - fipID, err := parseAzureResourceID(*ipconfiguration.ID) + fipID, err := azure.ParseAzureResourceID(*ipconfiguration.ID) if err != nil { return err } @@ -223,12 +226,12 @@ func resourceArmLoadBalancerNatRuleRead(d *schema.ResourceData, meta interface{} } func resourceArmLoadBalancerNatRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -274,7 +277,6 @@ func resourceArmLoadBalancerNatRuleDelete(d *schema.ResourceData, meta interface } func expandAzureRmLoadBalancerNatRule(d *schema.ResourceData, lb *network.LoadBalancer) (*network.InboundNatRule, error) { - properties := network.InboundNatRulePropertiesFormat{ Protocol: network.TransportProtocol(d.Get("protocol").(string)), FrontendPort: utils.Int32(int32(d.Get("frontend_port").(int))), diff --git a/azurerm/resource_arm_loadbalancer_nat_rule_test.go b/azurerm/resource_arm_loadbalancer_nat_rule_test.go index 91e72b164d3d..2d6f1165472e 100644 --- a/azurerm/resource_arm_loadbalancer_nat_rule_test.go +++ b/azurerm/resource_arm_loadbalancer_nat_rule_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLoadBalancerNatRule_basic(t *testing.T) { @@ -47,7 +49,7 @@ func TestAccAzureRMLoadBalancerNatRule_basic(t *testing.T) { } func TestAccAzureRMLoadBalancerNatRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -252,7 +254,7 @@ func testCheckAzureRMLoadBalancerNatRuleNotExists(natRuleName string, lb *networ func testCheckAzureRMLoadBalancerNatRuleDisappears(natRuleName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerNatRuleByName(lb, natRuleName) @@ -264,7 +266,7 @@ func testCheckAzureRMLoadBalancerNatRuleDisappears(natRuleName string, lb *netwo rules := append(currentRules[:i], currentRules[i+1:]...) lb.LoadBalancerPropertiesFormat.InboundNatRules = &rules - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } diff --git a/azurerm/resource_arm_loadbalancer_outbound_rule.go b/azurerm/resource_arm_loadbalancer_outbound_rule.go index 484312441bcb..4c7709231783 100644 --- a/azurerm/resource_arm_loadbalancer_outbound_rule.go +++ b/azurerm/resource_arm_loadbalancer_outbound_rule.go @@ -4,12 +4,14 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -70,9 +72,9 @@ func resourceArmLoadBalancerOutboundRule() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(network.Protocol1All), - string(network.Protocol1TCP), - string(network.Protocol1UDP), + string(network.TransportProtocolAll), + string(network.TransportProtocolTCP), + string(network.TransportProtocolUDP), }, false), }, @@ -98,13 +100,13 @@ func resourceArmLoadBalancerOutboundRule() *schema.Resource { } func resourceArmLoadBalancerOutboundRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -130,7 +132,7 @@ func resourceArmLoadBalancerOutboundRuleCreateUpdate(d *schema.ResourceData, met existingOutboundRule, existingOutboundRuleIndex, exists := findLoadBalancerOutboundRuleByName(loadBalancer, name) if exists { if name == *existingOutboundRule.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_outbound_rule", *existingOutboundRule.ID) } @@ -182,7 +184,7 @@ func resourceArmLoadBalancerOutboundRuleCreateUpdate(d *schema.ResourceData, met } func resourceArmLoadBalancerOutboundRuleRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -218,7 +220,7 @@ func resourceArmLoadBalancerOutboundRuleRead(d *schema.ResourceData, meta interf continue } - feConfigId, err := parseAzureResourceID(*feConfig.ID) + feConfigId, err := azure.ParseAzureResourceID(*feConfig.ID) if err != nil { return nil } @@ -243,19 +245,18 @@ func resourceArmLoadBalancerOutboundRuleRead(d *schema.ResourceData, meta interf if properties.AllocatedOutboundPorts != nil { d.Set("allocated_outbound_ports", properties.AllocatedOutboundPorts) } - } return nil } func resourceArmLoadBalancerOutboundRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -301,9 +302,8 @@ func resourceArmLoadBalancerOutboundRuleDelete(d *schema.ResourceData, meta inte } func expandAzureRmLoadBalancerOutboundRule(d *schema.ResourceData, lb *network.LoadBalancer) (*network.OutboundRule, error) { - properties := network.OutboundRulePropertiesFormat{ - Protocol: network.Protocol1(d.Get("protocol").(string)), + Protocol: network.LoadBalancerOutboundRuleProtocol(d.Get("protocol").(string)), } feConfigs := d.Get("frontend_ip_configuration").([]interface{}) diff --git a/azurerm/resource_arm_loadbalancer_outbound_rule_test.go b/azurerm/resource_arm_loadbalancer_outbound_rule_test.go index c2101ffbff03..78b5df0954f4 100644 --- a/azurerm/resource_arm_loadbalancer_outbound_rule_test.go +++ b/azurerm/resource_arm_loadbalancer_outbound_rule_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLoadBalancerOutboundRule_basic(t *testing.T) { @@ -47,7 +49,7 @@ func TestAccAzureRMLoadBalancerOutboundRule_basic(t *testing.T) { } func TestAccAzureRMLoadBalancerOutboundRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -145,6 +147,41 @@ func TestAccAzureRMLoadBalancerOutboundRule_update(t *testing.T) { }) } +func TestAccAzureRMLoadBalancerOutboundRule_withPublicIPPrefix(t *testing.T) { + var lb network.LoadBalancer + ri := tf.AccRandTimeInt() + outboundRuleName := fmt.Sprintf("OutboundRule-%d", ri) + + subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") + outboundRuleId := fmt.Sprintf( + "/subscriptions/%s/resourceGroups/acctestRG-%d/providers/Microsoft.Network/loadBalancers/arm-test-loadbalancer-%d/outboundRules/%s", + subscriptionID, ri, ri, outboundRuleName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLoadBalancerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLoadBalancerOutboundRule_withPublicIPPrefix(ri, outboundRuleName, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLoadBalancerExists("azurerm_lb.test", &lb), + testCheckAzureRMLoadBalancerOutboundRuleExists(outboundRuleName, &lb), + resource.TestCheckResourceAttr( + "azurerm_lb_outbound_rule.test", "id", outboundRuleId), + ), + }, + { + ResourceName: "azurerm_lb.test", + ImportState: true, + ImportStateVerify: true, + // location is deprecated and was never actually used + ImportStateVerifyIgnore: []string{"location"}, + }, + }, + }) +} + func TestAccAzureRMLoadBalancerOutboundRule_disappears(t *testing.T) { var lb network.LoadBalancer ri := tf.AccRandTimeInt() @@ -190,7 +227,7 @@ func testCheckAzureRMLoadBalancerOutboundRuleNotExists(outboundRuleName string, func testCheckAzureRMLoadBalancerOutboundRuleDisappears(ruleName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerOutboundRuleByName(lb, ruleName) @@ -202,7 +239,7 @@ func testCheckAzureRMLoadBalancerOutboundRuleDisappears(ruleName string, lb *net rules := append(currentRules[:i], currentRules[i+1:]...) lb.LoadBalancerPropertiesFormat.OutboundRules = &rules - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } @@ -242,7 +279,6 @@ resource "azurerm_lb" "test" { resource_group_name = "${azurerm_resource_group.test.name}" sku = "Standard" - frontend_ip_configuration { name = "one-%d" public_ip_address_id = "${azurerm_public_ip.test.id}" @@ -256,12 +292,11 @@ resource "azurerm_lb_backend_address_pool" "test" { } resource "azurerm_lb_outbound_rule" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - loadbalancer_id = "${azurerm_lb.test.id}" - name = "%s" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" - protocol = "All" - + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + protocol = "All" frontend_ip_configuration { name = "one-%d" @@ -276,11 +311,11 @@ func testAccAzureRMLoadBalancerOutboundRule_requiresImport(rInt int, name string %s resource "azurerm_lb_outbound_rule" "import" { - name = "${azurerm_lb_outbound_rule.test.name}" - resource_group_name = "${azurerm_lb_outbound_rule.test.resource_group_name}" - loadbalancer_id = "${azurerm_lb_outbound_rule.test.loadbalancer_id}" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" - protocol = "All" + name = "${azurerm_lb_outbound_rule.test.name}" + resource_group_name = "${azurerm_lb_outbound_rule.test.resource_group_name}" + loadbalancer_id = "${azurerm_lb_outbound_rule.test.loadbalancer_id}" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + protocol = "All" frontend_ip_configuration { name = "${azurerm_lb_outbound_rule.test.frontend_ip_configuration.0.name}" @@ -371,11 +406,11 @@ resource "azurerm_lb_backend_address_pool" "test" { } resource "azurerm_lb_outbound_rule" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - loadbalancer_id = "${azurerm_lb.test.id}" - name = "%s" - protocol = "Tcp" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + protocol = "Tcp" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" frontend_ip_configuration { name = "fe1-%d" @@ -383,11 +418,11 @@ resource "azurerm_lb_outbound_rule" "test" { } resource "azurerm_lb_outbound_rule" "test2" { - resource_group_name = "${azurerm_resource_group.test.name}" - loadbalancer_id = "${azurerm_lb.test.id}" - name = "%s" - protocol = "Udp" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + protocol = "Udp" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" frontend_ip_configuration { name = "fe2-%d" @@ -443,11 +478,11 @@ resource "azurerm_lb_backend_address_pool" "test" { } resource "azurerm_lb_outbound_rule" "test" { - resource_group_name = "${azurerm_resource_group.test.name}" - loadbalancer_id = "${azurerm_lb.test.id}" - name = "%s" - protocol = "All" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + protocol = "All" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" frontend_ip_configuration { name = "fe1-%d" @@ -455,11 +490,11 @@ resource "azurerm_lb_outbound_rule" "test" { } resource "azurerm_lb_outbound_rule" "test2" { - resource_group_name = "${azurerm_resource_group.test.name}" - loadbalancer_id = "${azurerm_lb.test.id}" - name = "%s" - protocol = "All" - backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + protocol = "All" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" frontend_ip_configuration { name = "fe2-%d" @@ -467,3 +502,49 @@ resource "azurerm_lb_outbound_rule" "test2" { } `, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, outboundRuleName, rInt, outboundRule2Name, rInt) } + +func testAccAzureRMLoadBalancerOutboundRule_withPublicIPPrefix(rInt int, outboundRuleName string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_public_ip_prefix" "test" { + name = "test-ip-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + prefix_length = 31 +} + +resource "azurerm_lb" "test" { + name = "arm-test-loadbalancer-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + frontend_ip_configuration { + name = "one-%d" + public_ip_prefix_id = "${azurerm_public_ip_prefix.test.id}" + } +} + +resource "azurerm_lb_backend_address_pool" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "be-%d" +} + +resource "azurerm_lb_outbound_rule" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + backend_address_pool_id = "${azurerm_lb_backend_address_pool.test.id}" + protocol = "All" + + frontend_ip_configuration { + name = "one-%d" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, outboundRuleName, rInt) +} diff --git a/azurerm/resource_arm_loadbalancer_probe.go b/azurerm/resource_arm_loadbalancer_probe.go index c787d2c2ec8c..2aab4052052f 100644 --- a/azurerm/resource_arm_loadbalancer_probe.go +++ b/azurerm/resource_arm_loadbalancer_probe.go @@ -4,13 +4,16 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -47,7 +50,7 @@ func resourceArmLoadBalancerProbe() *schema.Resource { Type: schema.TypeString, Computed: true, Optional: true, - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.ProbeProtocolHTTP), @@ -94,13 +97,13 @@ func resourceArmLoadBalancerProbe() *schema.Resource { } func resourceArmLoadBalancerProbeCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -118,7 +121,7 @@ func resourceArmLoadBalancerProbeCreateUpdate(d *schema.ResourceData, meta inter existingProbe, existingProbeIndex, exists := findLoadBalancerProbeByName(loadBalancer, name) if exists { if name == *existingProbe.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_probe", *existingProbe.ID) } @@ -167,7 +170,7 @@ func resourceArmLoadBalancerProbeCreateUpdate(d *schema.ResourceData, meta inter } func resourceArmLoadBalancerProbeRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -217,12 +220,12 @@ func resourceArmLoadBalancerProbeRead(d *schema.ResourceData, meta interface{}) } func resourceArmLoadBalancerProbeDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -268,7 +271,6 @@ func resourceArmLoadBalancerProbeDelete(d *schema.ResourceData, meta interface{} } func expandAzureRmLoadBalancerProbe(d *schema.ResourceData) *network.Probe { - properties := network.ProbePropertiesFormat{ NumberOfProbes: utils.Int32(int32(d.Get("number_of_probes").(int))), IntervalInSeconds: utils.Int32(int32(d.Get("interval_in_seconds").(int))), diff --git a/azurerm/resource_arm_loadbalancer_probe_test.go b/azurerm/resource_arm_loadbalancer_probe_test.go index ca3355f652ed..dcc8fd968316 100644 --- a/azurerm/resource_arm_loadbalancer_probe_test.go +++ b/azurerm/resource_arm_loadbalancer_probe_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLoadBalancerProbe_basic(t *testing.T) { @@ -47,7 +49,7 @@ func TestAccAzureRMLoadBalancerProbe_basic(t *testing.T) { } func TestAccAzureRMLoadBalancerProbe_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -223,7 +225,7 @@ func testCheckAzureRMLoadBalancerProbeNotExists(natRuleName string, lb *network. func testCheckAzureRMLoadBalancerProbeDisappears(addressPoolName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerProbeByName(lb, addressPoolName) @@ -235,7 +237,7 @@ func testCheckAzureRMLoadBalancerProbeDisappears(addressPoolName string, lb *net probes := append(currentProbes[:i], currentProbes[i+1:]...) lb.LoadBalancerPropertiesFormat.Probes = &probes - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } diff --git a/azurerm/resource_arm_loadbalancer_rule.go b/azurerm/resource_arm_loadbalancer_rule.go index 394ac46c4530..4cb928f81e2c 100644 --- a/azurerm/resource_arm_loadbalancer_rule.go +++ b/azurerm/resource_arm_loadbalancer_rule.go @@ -5,13 +5,16 @@ import ( "log" "regexp" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -65,7 +68,7 @@ func resourceArmLoadBalancerRule() *schema.Resource { "protocol": { Type: schema.TypeString, Required: true, - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.TransportProtocolAll), @@ -98,6 +101,12 @@ func resourceArmLoadBalancerRule() *schema.Resource { Default: false, }, + "disable_outbound_snat": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "idle_timeout_in_minutes": { Type: schema.TypeInt, Optional: true, @@ -115,13 +124,13 @@ func resourceArmLoadBalancerRule() *schema.Resource { } func resourceArmLoadBalancerRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -143,7 +152,7 @@ func resourceArmLoadBalancerRuleCreateUpdate(d *schema.ResourceData, meta interf existingRule, existingRuleIndex, exists := findLoadBalancerRuleByName(loadBalancer, name) if exists { if name == *existingRule.Name { - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { return tf.ImportAsExistsError("azurerm_lb_rule", *existingRule.ID) } @@ -192,7 +201,7 @@ func resourceArmLoadBalancerRuleCreateUpdate(d *schema.ResourceData, meta interf } func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -222,6 +231,7 @@ func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) e d.Set("protocol", properties.Protocol) d.Set("frontend_port", properties.FrontendPort) d.Set("backend_port", properties.BackendPort) + d.Set("disable_outbound_snat", properties.DisableOutboundSnat) if properties.EnableFloatingIP != nil { d.Set("enable_floating_ip", properties.EnableFloatingIP) @@ -232,7 +242,7 @@ func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) e } if properties.FrontendIPConfiguration != nil { - fipID, err := parseAzureResourceID(*properties.FrontendIPConfiguration.ID) + fipID, err := azure.ParseAzureResourceID(*properties.FrontendIPConfiguration.ID) if err != nil { return err } @@ -258,12 +268,12 @@ func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) e } func resourceArmLoadBalancerRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).loadBalancerClient + client := meta.(*ArmClient).network.LoadBalancersClient ctx := meta.(*ArmClient).StopContext loadBalancerID := d.Get("loadbalancer_id").(string) - armMutexKV.Lock(loadBalancerID) - defer armMutexKV.Unlock(loadBalancerID) + locks.ByID(loadBalancerID) + defer locks.UnlockByID(loadBalancerID) loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) if err != nil { @@ -309,12 +319,12 @@ func resourceArmLoadBalancerRuleDelete(d *schema.ResourceData, meta interface{}) } func expandAzureRmLoadBalancerRule(d *schema.ResourceData, lb *network.LoadBalancer) (*network.LoadBalancingRule, error) { - properties := network.LoadBalancingRulePropertiesFormat{ - Protocol: network.TransportProtocol(d.Get("protocol").(string)), - FrontendPort: utils.Int32(int32(d.Get("frontend_port").(int))), - BackendPort: utils.Int32(int32(d.Get("backend_port").(int))), - EnableFloatingIP: utils.Bool(d.Get("enable_floating_ip").(bool)), + Protocol: network.TransportProtocol(d.Get("protocol").(string)), + FrontendPort: utils.Int32(int32(d.Get("frontend_port").(int))), + BackendPort: utils.Int32(int32(d.Get("backend_port").(int))), + EnableFloatingIP: utils.Bool(d.Get("enable_floating_ip").(bool)), + DisableOutboundSnat: utils.Bool(d.Get("disable_outbound_snat").(bool)), } if v, ok := d.GetOk("idle_timeout_in_minutes"); ok { diff --git a/azurerm/resource_arm_loadbalancer_rule_test.go b/azurerm/resource_arm_loadbalancer_rule_test.go index 2a1a4e20dc0b..566d2a193b75 100644 --- a/azurerm/resource_arm_loadbalancer_rule_test.go +++ b/azurerm/resource_arm_loadbalancer_rule_test.go @@ -5,11 +5,13 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestResourceAzureRMLoadBalancerRuleNameLabel_validation(t *testing.T) { @@ -103,8 +105,43 @@ func TestAccAzureRMLoadBalancerRule_basic(t *testing.T) { }) } +func TestAccAzureRMLoadBalancerRule_disableoutboundsnat(t *testing.T) { + var lb network.LoadBalancer + ri := tf.AccRandTimeInt() + lbRuleName := fmt.Sprintf("LbRule-%s", acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)) + + subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") + lbRule_id := fmt.Sprintf( + "/subscriptions/%s/resourceGroups/acctestRG-%d/providers/Microsoft.Network/loadBalancers/arm-test-loadbalancer-%d/loadBalancingRules/%s", + subscriptionID, ri, ri, lbRuleName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLoadBalancerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLoadBalancerRule_disableoutboundsnat(ri, lbRuleName, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLoadBalancerExists("azurerm_lb.test", &lb), + testCheckAzureRMLoadBalancerRuleExists(lbRuleName, &lb), + resource.TestCheckResourceAttr("azurerm_lb_rule.test", "id", lbRule_id), + resource.TestCheckResourceAttr("azurerm_lb_rule.test", "disable_outbound_snat", "true"), + ), + }, + { + ResourceName: "azurerm_lb.test", + ImportState: true, + ImportStateVerify: true, + // location is deprecated and was never actually used + ImportStateVerifyIgnore: []string{"location"}, + }, + }, + }) +} + func TestAccAzureRMLoadBalancerRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -290,7 +327,7 @@ func testCheckAzureRMLoadBalancerRuleNotExists(lbRuleName string, lb *network.Lo func testCheckAzureRMLoadBalancerRuleDisappears(ruleName string, lb *network.LoadBalancer) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, i, exists := findLoadBalancerRuleByName(lb, ruleName) @@ -302,7 +339,7 @@ func testCheckAzureRMLoadBalancerRuleDisappears(ruleName string, lb *network.Loa rules := append(currentRules[:i], currentRules[i+1:]...) lb.LoadBalancerPropertiesFormat.LoadBalancingRules = &rules - id, err := parseAzureResourceID(*lb.ID) + id, err := azure.ParseAzureResourceID(*lb.ID) if err != nil { return err } @@ -359,6 +396,47 @@ resource "azurerm_lb_rule" "test" { `, rInt, location, rInt, rInt, rInt, lbRuleName, rInt) } +func testAccAzureRMLoadBalancerRule_disableoutboundsnat(rInt int, lbRuleName string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_public_ip" "test" { + name = "test-ip-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_lb" "test" { + name = "arm-test-loadbalancer-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + frontend_ip_configuration { + name = "one-%d" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_lb_rule" "test" { + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + loadbalancer_id = "${azurerm_lb.test.id}" + name = "%s" + protocol = "Tcp" + frontend_port = 3389 + backend_port = 3389 + frontend_ip_configuration_name = "one-%d" + disable_outbound_snat = true +} +`, rInt, location, rInt, rInt, rInt, lbRuleName, rInt) +} + func testAccAzureRMLoadBalancerRule_requiresImport(rInt int, name string, location string) string { template := testAccAzureRMLoadBalancerRule_basic(rInt, name, location) return fmt.Sprintf(` diff --git a/azurerm/resource_arm_loadbalancer_test.go b/azurerm/resource_arm_loadbalancer_test.go index a03428e4de53..c000729b5040 100644 --- a/azurerm/resource_arm_loadbalancer_test.go +++ b/azurerm/resource_arm_loadbalancer_test.go @@ -5,10 +5,11 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestResourceAzureRMLoadBalancerPrivateIpAddressAllocation_validation(t *testing.T) { @@ -72,7 +73,7 @@ func TestAccAzureRMLoadBalancer_basic(t *testing.T) { } func TestAccAzureRMLoadBalancer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -123,7 +124,32 @@ func TestAccAzureRMLoadBalancer_standard(t *testing.T) { }, }) } +func TestAccAzureRMLoadBalancer_frontEndConfigPublicIPPrefix(t *testing.T) { + var lb network.LoadBalancer + ri := tf.AccRandTimeInt() + location := testLocation() + resourceName := "azurerm_lb.test" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLoadBalancerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLoadBalancer_frontEndConfigPublicIPPrefix(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLoadBalancerExists(resourceName, &lb), + resource.TestCheckResourceAttr(resourceName, "frontend_ip_configuration.#", "1"), + ), + }, + { + ResourceName: "azurerm_lb.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} func TestAccAzureRMLoadBalancer_frontEndConfig(t *testing.T) { var lb network.LoadBalancer resourceName := "azurerm_lb.test" @@ -231,7 +257,7 @@ func testCheckAzureRMLoadBalancerExists(resourceName string, lb *network.LoadBal return fmt.Errorf("Bad: no resource group found in state for loadbalancer: %s", loadBalancerName) } - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, loadBalancerName, "") @@ -250,7 +276,7 @@ func testCheckAzureRMLoadBalancerExists(resourceName string, lb *network.LoadBal } func testCheckAzureRMLoadBalancerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).loadBalancerClient + client := testAccProvider.Meta().(*ArmClient).network.LoadBalancersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -426,6 +452,34 @@ resource "azurerm_lb" "test" { `, rInt, location, rInt, rInt, rInt, rInt) } +func testAccAzureRMLoadBalancer_frontEndConfigPublicIPPrefix(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_public_ip_prefix" "test" { + name = "test-ip-prefix-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + prefix_length = 31 +} + +resource "azurerm_lb" "test" { + name = "acctest-loadbalancer-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + frontend_ip_configuration { + name = "prefix-%d" + public_ip_prefix_id = "${azurerm_public_ip_prefix.test.id}" + } +} +`, rInt, location, rInt, rInt, rInt) +} + func testAccAzureRMLoadBalancer_frontEndConfigRemoval(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_local_network_gateway.go b/azurerm/resource_arm_local_network_gateway.go index ca9bc772c5c1..307042f6b4fd 100644 --- a/azurerm/resource_arm_local_network_gateway.go +++ b/azurerm/resource_arm_local_network_gateway.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -70,19 +72,19 @@ func resourceArmLocalNetworkGateway() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmLocalNetworkGatewayCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).localNetConnClient + client := meta.(*ArmClient).network.LocalNetworkGatewaysClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -105,7 +107,7 @@ func resourceArmLocalNetworkGatewayCreateUpdate(d *schema.ResourceData, meta int return err } - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) gateway := network.LocalNetworkGateway{ Name: &name, @@ -117,7 +119,7 @@ func resourceArmLocalNetworkGatewayCreateUpdate(d *schema.ResourceData, meta int GatewayIPAddress: &ipAddress, BgpSettings: bgpSettings, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, name, gateway) @@ -143,7 +145,7 @@ func resourceArmLocalNetworkGatewayCreateUpdate(d *schema.ResourceData, meta int } func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).localNetConnClient + client := meta.(*ArmClient).network.LocalNetworkGatewaysClient ctx := meta.(*ArmClient).StopContext resGroup, name, err := resourceGroupAndLocalNetworkGatewayFromId(d.Id()) @@ -171,9 +173,7 @@ func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{} d.Set("gateway_address", props.GatewayIPAddress) if lnas := props.LocalNetworkAddressSpace; lnas != nil { - if prefixes := lnas.AddressPrefixes; prefixes != nil { - d.Set("address_space", *prefixes) - } + d.Set("address_space", lnas.AddressPrefixes) } flattenedSettings := flattenLocalNetworkGatewayBGPSettings(props.BgpSettings) if err := d.Set("bgp_settings", flattenedSettings); err != nil { @@ -181,13 +181,11 @@ func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{} } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).localNetConnClient + client := meta.(*ArmClient).network.LocalNetworkGatewaysClient ctx := meta.(*ArmClient).StopContext resGroup, name, err := resourceGroupAndLocalNetworkGatewayFromId(d.Id()) @@ -216,7 +214,7 @@ func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface } func resourceGroupAndLocalNetworkGatewayFromId(localNetworkGatewayId string) (string, string, error) { - id, err := parseAzureResourceID(localNetworkGatewayId) + id, err := azure.ParseAzureResourceID(localNetworkGatewayId) if err != nil { return "", "", err } diff --git a/azurerm/resource_arm_local_network_gateway_test.go b/azurerm/resource_arm_local_network_gateway_test.go index 152f35d1c70b..b09783a5277f 100644 --- a/azurerm/resource_arm_local_network_gateway_test.go +++ b/azurerm/resource_arm_local_network_gateway_test.go @@ -6,8 +6,10 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +40,7 @@ func TestAccAzureRMLocalNetworkGateway_basic(t *testing.T) { } func TestAccAzureRMLocalNetworkGateway_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -254,7 +256,7 @@ func testCheckAzureRMLocalNetworkGatewayExists(resourceName string) resource.Tes } // then, extract the name and the resource group: - id, err := parseAzureResourceID(res.Primary.ID) + id, err := azure.ParseAzureResourceID(res.Primary.ID) if err != nil { return err } @@ -262,7 +264,7 @@ func testCheckAzureRMLocalNetworkGatewayExists(resourceName string) resource.Tes resGrp := id.ResourceGroup // and finally, check that it exists on Azure: - client := testAccProvider.Meta().(*ArmClient).localNetConnClient + client := testAccProvider.Meta().(*ArmClient).network.LocalNetworkGatewaysClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resGrp, localNetName) @@ -287,7 +289,7 @@ func testCheckAzureRMLocalNetworkGatewayDisappears(resourceName string) resource } // then, extract the name and the resource group: - id, err := parseAzureResourceID(res.Primary.ID) + id, err := azure.ParseAzureResourceID(res.Primary.ID) if err != nil { return err } @@ -295,7 +297,7 @@ func testCheckAzureRMLocalNetworkGatewayDisappears(resourceName string) resource resourceGroup := id.ResourceGroup // and finally, check that it exists on Azure: - client := testAccProvider.Meta().(*ArmClient).localNetConnClient + client := testAccProvider.Meta().(*ArmClient).network.LocalNetworkGatewaysClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, localNetName) @@ -320,14 +322,14 @@ func testCheckAzureRMLocalNetworkGatewayDestroy(s *terraform.State) error { continue } - id, err := parseAzureResourceID(res.Primary.ID) + id, err := azure.ParseAzureResourceID(res.Primary.ID) if err != nil { return err } localNetName := id.Path["localNetworkGateways"] resourceGroup := id.ResourceGroup - client := testAccProvider.Meta().(*ArmClient).localNetConnClient + client := testAccProvider.Meta().(*ArmClient).network.LocalNetworkGatewaysClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, localNetName) diff --git a/azurerm/resource_arm_log_analytics_linked_service.go b/azurerm/resource_arm_log_analytics_linked_service.go index d667da59548e..85ffb26e7e81 100644 --- a/azurerm/resource_arm_log_analytics_linked_service.go +++ b/azurerm/resource_arm_log_analytics_linked_service.go @@ -10,6 +10,8 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -55,10 +57,13 @@ func resourceArmLogAnalyticsLinkedService() *schema.Resource { }, "linked_service_properties": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"resource_id"}, + MaxItems: 1, + Deprecated: "This property has been deprecated in favour of the 'resource_id' property and will be removed in version 2.0 of the provider", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "resource_id": { @@ -77,7 +82,7 @@ func resourceArmLogAnalyticsLinkedService() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -92,7 +97,7 @@ func resourceArmLogAnalyticsLinkedServiceCreateUpdate(d *schema.ResourceData, me workspaceName := d.Get("workspace_name").(string) lsName := d.Get("linked_service_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, workspaceName, lsName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -107,19 +112,19 @@ func resourceArmLogAnalyticsLinkedServiceCreateUpdate(d *schema.ResourceData, me resourceId := d.Get("resource_id").(string) if resourceId == "" { - props := d.Get("linked_service_properties").(map[string]interface{}) - resourceId = props["resource_id"].(string) + props := d.Get("linked_service_properties").([]interface{}) + resourceId = expandLogAnalyticsLinkedServiceProperties(props) if resourceId == "" { return fmt.Errorf("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block") } } - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := operationalinsights.LinkedService{ LinkedServiceProperties: &operationalinsights.LinkedServiceProperties{ ResourceID: utils.String(resourceId), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, resGroup, workspaceName, lsName, parameters); err != nil { @@ -143,7 +148,7 @@ func resourceArmLogAnalyticsLinkedServiceRead(d *schema.ResourceData, meta inter client := meta.(*ArmClient).logAnalytics.LinkedServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -175,15 +180,14 @@ func resourceArmLogAnalyticsLinkedServiceRead(d *schema.ResourceData, meta inter return fmt.Errorf("Error setting `linked_service_properties`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmLogAnalyticsLinkedServiceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.LinkedServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -204,6 +208,15 @@ func resourceArmLogAnalyticsLinkedServiceDelete(d *schema.ResourceData, meta int return nil } +func expandLogAnalyticsLinkedServiceProperties(input []interface{}) string { + if len(input) == 0 { + return "" + } + + props := input[0].(map[string]interface{}) + return props["resource_id"].(string) +} + func flattenLogAnalyticsLinkedServiceProperties(input *operationalinsights.LinkedServiceProperties) []interface{} { if input == nil { return []interface{}{} diff --git a/azurerm/resource_arm_log_analytics_linked_service_test.go b/azurerm/resource_arm_log_analytics_linked_service_test.go index d8bc53706ad4..36ba2435048b 100644 --- a/azurerm/resource_arm_log_analytics_linked_service_test.go +++ b/azurerm/resource_arm_log_analytics_linked_service_test.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" "net/http" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogAnalyticsLinkedService_basic(t *testing.T) { @@ -38,7 +40,7 @@ func TestAccAzureRMLogAnalyticsLinkedService_basic(t *testing.T) { } func TestAccAzureRMLogAnalyticsLinkedService_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -94,6 +96,48 @@ func TestAccAzureRMLogAnalyticsLinkedService_complete(t *testing.T) { }) } +// Deprecated - remove in 2.0 +func TestAccAzureRMLogAnalyticsLinkedService_noResourceID(t *testing.T) { + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsLinkedService_noResourceID(ri, testLocation()), + ExpectError: regexp.MustCompile("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block"), + }, + }, + }) +} + +// Deprecated - remove in 2.0 +func TestAccAzureRMLogAnalyticsLinkedService_linkedServiceProperties(t *testing.T) { + resourceName := "azurerm_log_analytics_linked_service.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsLinkedService_linkedServiceProperties(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLogAnalyticsLinkedServiceExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMLogAnalyticsLinkedServiceDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*ArmClient).logAnalytics.LinkedServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -196,6 +240,33 @@ resource "azurerm_log_analytics_linked_service" "test" { `, template) } +func testAccAzureRMLogAnalyticsLinkedService_noResourceID(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_log_analytics_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" +} +`, template) +} + +func testAccAzureRMLogAnalyticsLinkedService_linkedServiceProperties(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_log_analytics_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" + linked_service_properties { + resource_id = "${azurerm_automation_account.test.id}" + } +} +`, template) +} + func testAccAzureRMLogAnalyticsLinkedService_template(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_log_analytics_solution.go b/azurerm/resource_arm_log_analytics_solution.go index ac2da33091b4..84074fa1b4d2 100644 --- a/azurerm/resource_arm_log_analytics_solution.go +++ b/azurerm/resource_arm_log_analytics_solution.go @@ -10,6 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -94,7 +95,7 @@ func resourceArmLogAnalyticsSolutionCreateUpdate(d *schema.ResourceData, meta in name := fmt.Sprintf("%s(%s)", d.Get("solution_name").(string), d.Get("workspace_name").(string)) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -143,13 +144,12 @@ func resourceArmLogAnalyticsSolutionCreateUpdate(d *schema.ResourceData, meta in d.SetId(*solution.ID) return resourceArmLogAnalyticsSolutionRead(d, meta) - } func resourceArmLogAnalyticsSolutionRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.SolutionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -203,7 +203,7 @@ func resourceArmLogAnalyticsSolutionRead(d *schema.ResourceData, meta interface{ func resourceArmLogAnalyticsSolutionDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.SolutionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_log_analytics_solution_test.go b/azurerm/resource_arm_log_analytics_solution_test.go index d3f369576537..116103f0933c 100644 --- a/azurerm/resource_arm_log_analytics_solution_test.go +++ b/azurerm/resource_arm_log_analytics_solution_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogAnalyticsSolution_basicContainerMonitoring(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMLogAnalyticsSolution_basicContainerMonitoring(t *testing.T) { } func TestAccAzureRMLogAnalyticsSolution_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_log_analytics_workspace.go b/azurerm/resource_arm_log_analytics_workspace.go index 98be2b4407fa..5ea5bb95be98 100644 --- a/azurerm/resource_arm_log_analytics_workspace.go +++ b/azurerm/resource_arm_log_analytics_workspace.go @@ -11,6 +11,8 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -81,7 +83,7 @@ func resourceArmLogAnalyticsWorkspace() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -94,7 +96,7 @@ func resourceArmLogAnalyticsWorkspaceCreateUpdate(d *schema.ResourceData, meta i name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -115,12 +117,12 @@ func resourceArmLogAnalyticsWorkspaceCreateUpdate(d *schema.ResourceData, meta i retentionInDays := int32(d.Get("retention_in_days").(int)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := operationalinsights.Workspace{ Name: &name, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), WorkspaceProperties: &operationalinsights.WorkspaceProperties{ Sku: sku, RetentionInDays: &retentionInDays, @@ -148,13 +150,12 @@ func resourceArmLogAnalyticsWorkspaceCreateUpdate(d *schema.ResourceData, meta i d.SetId(*read.ID) return resourceArmLogAnalyticsWorkspaceRead(d, meta) - } func resourceArmLogAnalyticsWorkspaceRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.WorkspacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -191,14 +192,13 @@ func resourceArmLogAnalyticsWorkspaceRead(d *schema.ResourceData, meta interface d.Set("secondary_shared_key", sharedKeys.SecondarySharedKey) } - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmLogAnalyticsWorkspaceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.WorkspacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_log_analytics_workspace_linked_service.go b/azurerm/resource_arm_log_analytics_workspace_linked_service.go index 886dbdade2a9..2b0ee7e48ca0 100644 --- a/azurerm/resource_arm_log_analytics_workspace_linked_service.go +++ b/azurerm/resource_arm_log_analytics_workspace_linked_service.go @@ -10,6 +10,8 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -62,10 +64,13 @@ As such the existing 'azurerm_log_analytics_workspace_linked_service' resource i }, "linked_service_properties": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"resource_id"}, + MaxItems: 1, + Deprecated: "This property has been deprecated in favour of the 'resource_id' property and will be removed in version 2.0 of the provider", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "resource_id": { @@ -84,7 +89,7 @@ As such the existing 'azurerm_log_analytics_workspace_linked_service' resource i Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -99,7 +104,7 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceCreateUpdate(d *schema.Resourc workspaceName := d.Get("workspace_name").(string) lsName := d.Get("linked_service_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, workspaceName, lsName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -114,19 +119,19 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceCreateUpdate(d *schema.Resourc resourceId := d.Get("resource_id").(string) if resourceId == "" { - props := d.Get("linked_service_properties").(map[string]interface{}) - resourceId = props["resource_id"].(string) + props := d.Get("linked_service_properties").([]interface{}) + resourceId = expandLogAnalyticsWorkspaceLinkedServiceProperties(props) if resourceId == "" { return fmt.Errorf("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block") } } - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) parameters := operationalinsights.LinkedService{ LinkedServiceProperties: &operationalinsights.LinkedServiceProperties{ ResourceID: utils.String(resourceId), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, resGroup, workspaceName, lsName, parameters); err != nil { @@ -150,7 +155,7 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceRead(d *schema.ResourceData, m client := meta.(*ArmClient).logAnalytics.LinkedServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -182,15 +187,14 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceRead(d *schema.ResourceData, m return fmt.Errorf("Error setting `linked_service_properties`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmLogAnalyticsWorkspaceLinkedServiceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).logAnalytics.LinkedServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -211,6 +215,15 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceDelete(d *schema.ResourceData, return nil } +func expandLogAnalyticsWorkspaceLinkedServiceProperties(input []interface{}) string { + if len(input) == 0 { + return "" + } + + props := input[0].(map[string]interface{}) + return props["resource_id"].(string) +} + func flattenLogAnalyticsWorkspaceLinkedServiceProperties(input *operationalinsights.LinkedServiceProperties) []interface{} { if input == nil { return []interface{}{} diff --git a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go index 7fdcf9345fa2..91c27c131842 100644 --- a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go +++ b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" "net/http" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_basic(t *testing.T) { @@ -38,7 +40,7 @@ func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_basic(t *testing.T) { } func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -94,6 +96,48 @@ func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_complete(t *testing.T) { }) } +// Deprecated - remove in 2.0 +func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_noResourceID(t *testing.T) { + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsWorkspaceLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsWorkspaceLinkedService_noResourceID(ri, testLocation()), + ExpectError: regexp.MustCompile("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block"), + }, + }, + }) +} + +// Deprecated - remove in 2.0 +func TestAccAzureRMLogAnalyticsWorkspaceLinkedService_linkedServiceProperties(t *testing.T) { + resourceName := "azurerm_log_analytics_workspace_linked_service.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsWorkspaceLinkedService_linkedServiceProperties(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLogAnalyticsWorkspaceLinkedServiceExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMLogAnalyticsWorkspaceLinkedServiceDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*ArmClient).logAnalytics.LinkedServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -196,6 +240,31 @@ resource "azurerm_log_analytics_workspace_linked_service" "test" { `, template) } +func testAccAzureRMLogAnalyticsWorkspaceLinkedService_noResourceID(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsWorkspaceLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s +resource "azurerm_log_analytics_workspace_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" +} +`, template) +} + +func testAccAzureRMLogAnalyticsWorkspaceLinkedService_linkedServiceProperties(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsWorkspaceLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s +resource "azurerm_log_analytics_workspace_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" + linked_service_properties { + resource_id = "${azurerm_automation_account.test.id}" + } +} +`, template) +} + func testAccAzureRMLogAnalyticsWorkspaceLinkedService_template(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_log_analytics_workspace_test.go b/azurerm/resource_arm_log_analytics_workspace_test.go index 45ceaa754f71..7019cd7dd8b1 100644 --- a/azurerm/resource_arm_log_analytics_workspace_test.go +++ b/azurerm/resource_arm_log_analytics_workspace_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRmLogAnalyticsWorkspaceName_validation(t *testing.T) { @@ -77,7 +78,7 @@ func TestAccAzureRMLogAnalyticsWorkspace_basic(t *testing.T) { } func TestAccAzureRMLogAnalyticsWorkspace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -228,7 +229,7 @@ resource "azurerm_log_analytics_workspace" "test" { sku = "PerGB2018" retention_in_days = 30 - tags = { + tags = { Environment = "Test" } } diff --git a/azurerm/resource_arm_logic_app_action_custom.go b/azurerm/resource_arm_logic_app_action_custom.go index 2ab4a09fa20d..1c995990d4ca 100644 --- a/azurerm/resource_arm_logic_app_action_custom.go +++ b/azurerm/resource_arm_logic_app_action_custom.go @@ -64,7 +64,7 @@ func resourceArmLogicAppActionCustomCreateUpdate(d *schema.ResourceData, meta in } func resourceArmLogicAppActionCustomRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -102,7 +102,7 @@ func resourceArmLogicAppActionCustomRead(d *schema.ResourceData, meta interface{ } func resourceArmLogicAppActionCustomDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_logic_app_action_custom_test.go b/azurerm/resource_arm_logic_app_action_custom_test.go index 69b763d16007..82df31ed11d5 100644 --- a/azurerm/resource_arm_logic_app_action_custom_test.go +++ b/azurerm/resource_arm_logic_app_action_custom_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppActionCustom_basic(t *testing.T) { @@ -34,7 +35,7 @@ func TestAccAzureRMLogicAppActionCustom_basic(t *testing.T) { } func TestAccAzureRMLogicAppActionCustom_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_logic_app_action_http.go b/azurerm/resource_arm_logic_app_action_http.go index f98fdc9bbd92..d4a9fc3eb6c1 100644 --- a/azurerm/resource_arm_logic_app_action_http.go +++ b/azurerm/resource_arm_logic_app_action_http.go @@ -10,6 +10,7 @@ import ( "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/internal/tags" ) func resourceArmLogicAppActionHTTP() *schema.Resource { @@ -40,11 +41,11 @@ func resourceArmLogicAppActionHTTP() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(http.MethodDelete), - string(http.MethodGet), - string(http.MethodPatch), - string(http.MethodPost), - string(http.MethodPut), + http.MethodDelete, + http.MethodGet, + http.MethodPatch, + http.MethodPost, + http.MethodPut, }, false), }, @@ -61,6 +62,9 @@ func resourceArmLogicAppActionHTTP() *schema.Resource { "headers": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } @@ -99,7 +103,7 @@ func resourceArmLogicAppActionHTTPCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmLogicAppActionHTTPRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -162,7 +166,7 @@ func resourceArmLogicAppActionHTTPRead(d *schema.ResourceData, meta interface{}) } func resourceArmLogicAppActionHTTPDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -183,7 +187,7 @@ func expandLogicAppActionHttpHeaders(headersRaw map[string]interface{}) (*map[st headers := make(map[string]string) for i, v := range headersRaw { - value, err := tagValueToString(v) + value, err := tags.TagValueToString(v) if err != nil { return nil, err } diff --git a/azurerm/resource_arm_logic_app_action_http_test.go b/azurerm/resource_arm_logic_app_action_http_test.go index 1fc19711daab..efc226a2a83c 100644 --- a/azurerm/resource_arm_logic_app_action_http_test.go +++ b/azurerm/resource_arm_logic_app_action_http_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppActionHttp_basic(t *testing.T) { @@ -33,7 +34,7 @@ func TestAccAzureRMLogicAppActionHttp_basic(t *testing.T) { } func TestAccAzureRMLogicAppActionHttp_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_logic_app_trigger_custom.go b/azurerm/resource_arm_logic_app_trigger_custom.go index d4da1885d616..13002c017722 100644 --- a/azurerm/resource_arm_logic_app_trigger_custom.go +++ b/azurerm/resource_arm_logic_app_trigger_custom.go @@ -64,7 +64,7 @@ func resourceArmLogicAppTriggerCustomCreateUpdate(d *schema.ResourceData, meta i } func resourceArmLogicAppTriggerCustomRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -102,7 +102,7 @@ func resourceArmLogicAppTriggerCustomRead(d *schema.ResourceData, meta interface } func resourceArmLogicAppTriggerCustomDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_logic_app_trigger_custom_test.go b/azurerm/resource_arm_logic_app_trigger_custom_test.go index b415f772935e..513d9e2bea68 100644 --- a/azurerm/resource_arm_logic_app_trigger_custom_test.go +++ b/azurerm/resource_arm_logic_app_trigger_custom_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppTriggerCustom_basic(t *testing.T) { @@ -33,7 +34,7 @@ func TestAccAzureRMLogicAppTriggerCustom_basic(t *testing.T) { } func TestAccAzureRMLogicAppTriggerCustom_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_logic_app_trigger_http_request.go b/azurerm/resource_arm_logic_app_trigger_http_request.go index 97b43d492eb8..f0aedd323d2b 100644 --- a/azurerm/resource_arm_logic_app_trigger_http_request.go +++ b/azurerm/resource_arm_logic_app_trigger_http_request.go @@ -24,7 +24,6 @@ func resourceArmLogicAppTriggerHttpRequest() *schema.Resource { }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - relativePath := diff.Get("relative_path").(string) if relativePath != "" { method := diff.Get("method").(string) @@ -61,11 +60,11 @@ func resourceArmLogicAppTriggerHttpRequest() *schema.Resource { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - string(http.MethodDelete), - string(http.MethodGet), - string(http.MethodPatch), - string(http.MethodPost), - string(http.MethodPut), + http.MethodDelete, + http.MethodGet, + http.MethodPatch, + http.MethodPost, + http.MethodPut, }, false), }, @@ -113,7 +112,7 @@ func resourceArmLogicAppTriggerHttpRequestCreateUpdate(d *schema.ResourceData, m } func resourceArmLogicAppTriggerHttpRequestRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -169,7 +168,7 @@ func resourceArmLogicAppTriggerHttpRequestRead(d *schema.ResourceData, meta inte } func resourceArmLogicAppTriggerHttpRequestDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } 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 88113049622b..e3a5990734d5 100644 --- a/azurerm/resource_arm_logic_app_trigger_http_request_test.go +++ b/azurerm/resource_arm_logic_app_trigger_http_request_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppTriggerHttpRequest_basic(t *testing.T) { @@ -34,7 +35,7 @@ func TestAccAzureRMLogicAppTriggerHttpRequest_basic(t *testing.T) { } func TestAccAzureRMLogicAppTriggerHttpRequest_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_logic_app_trigger_recurrence.go b/azurerm/resource_arm_logic_app_trigger_recurrence.go index d8528ea64da3..69be9bbac218 100644 --- a/azurerm/resource_arm_logic_app_trigger_recurrence.go +++ b/azurerm/resource_arm_logic_app_trigger_recurrence.go @@ -74,7 +74,7 @@ func resourceArmLogicAppTriggerRecurrenceCreateUpdate(d *schema.ResourceData, me } func resourceArmLogicAppTriggerRecurrenceRead(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -121,7 +121,7 @@ func resourceArmLogicAppTriggerRecurrenceRead(d *schema.ResourceData, meta inter } func resourceArmLogicAppTriggerRecurrenceDelete(d *schema.ResourceData, meta interface{}) error { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_logic_app_trigger_recurrence_test.go b/azurerm/resource_arm_logic_app_trigger_recurrence_test.go index 40da797f0445..0f39613e770b 100644 --- a/azurerm/resource_arm_logic_app_trigger_recurrence_test.go +++ b/azurerm/resource_arm_logic_app_trigger_recurrence_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppTriggerRecurrence_month(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMLogicAppTriggerRecurrence_month(t *testing.T) { } func TestAccAzureRMLogicAppTriggerRecurrence_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_logic_app_workflow.go b/azurerm/resource_arm_logic_app_workflow.go index 886b8881c5f2..a17b092d6628 100644 --- a/azurerm/resource_arm_logic_app_workflow.go +++ b/azurerm/resource_arm_logic_app_workflow.go @@ -8,6 +8,9 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,6 +41,9 @@ func resourceArmLogicAppWorkflow() *schema.Resource { "parameters": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "workflow_schema": { @@ -54,7 +60,7 @@ func resourceArmLogicAppWorkflow() *schema.Resource { Default: "1.0.0.0", }, - "tags": tagsSchema(), + "tags": tags.Schema(), "access_endpoint": { Type: schema.TypeString, @@ -65,7 +71,7 @@ func resourceArmLogicAppWorkflow() *schema.Resource { } func resourceArmLogicAppWorkflowCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Logic App Workflow creation.") @@ -73,7 +79,7 @@ func resourceArmLogicAppWorkflowCreate(d *schema.ResourceData, meta interface{}) name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -91,7 +97,7 @@ func resourceArmLogicAppWorkflowCreate(d *schema.ResourceData, meta interface{}) workflowSchema := d.Get("workflow_schema").(string) workflowVersion := d.Get("workflow_version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := logic.Workflow{ Location: utils.String(location), @@ -104,7 +110,7 @@ func resourceArmLogicAppWorkflowCreate(d *schema.ResourceData, meta interface{}) }, Parameters: parameters, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, properties); err != nil { @@ -125,10 +131,10 @@ func resourceArmLogicAppWorkflowCreate(d *schema.ResourceData, meta interface{}) } func resourceArmLogicAppWorkflowUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -137,8 +143,8 @@ func resourceArmLogicAppWorkflowUpdate(d *schema.ResourceData, meta interface{}) name := id.Path["workflows"] // lock to prevent against Actions, Parameters or Triggers conflicting - azureRMLockByName(name, logicAppResourceName) - defer azureRMUnlockByName(name, logicAppResourceName) + locks.ByName(name, logicAppResourceName) + defer locks.UnlockByName(name, logicAppResourceName) read, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -156,7 +162,7 @@ func resourceArmLogicAppWorkflowUpdate(d *schema.ResourceData, meta interface{}) location := azure.NormalizeLocation(d.Get("location").(string)) parameters := expandLogicAppWorkflowParameters(d.Get("parameters").(map[string]interface{})) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := logic.Workflow{ Location: utils.String(location), @@ -164,7 +170,7 @@ func resourceArmLogicAppWorkflowUpdate(d *schema.ResourceData, meta interface{}) Definition: read.WorkflowProperties.Definition, Parameters: parameters, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err = client.CreateOrUpdate(ctx, resourceGroup, name, properties); err != nil { @@ -175,10 +181,10 @@ func resourceArmLogicAppWorkflowUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmLogicAppWorkflowRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -218,16 +224,14 @@ func resourceArmLogicAppWorkflowRead(d *schema.ResourceData, meta interface{}) e } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmLogicAppWorkflowDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).logicWorkflowsClient + client := meta.(*ArmClient).logic.WorkflowsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -235,8 +239,8 @@ func resourceArmLogicAppWorkflowDelete(d *schema.ResourceData, meta interface{}) name := id.Path["workflows"] // lock to prevent against Actions, Parameters or Triggers conflicting - azureRMLockByName(name, logicAppResourceName) - defer azureRMUnlockByName(name, logicAppResourceName) + locks.ByName(name, logicAppResourceName) + defer locks.UnlockByName(name, logicAppResourceName) resp, err := client.Delete(ctx, resourceGroup, name) if err != nil { diff --git a/azurerm/resource_arm_logic_app_workflow_test.go b/azurerm/resource_arm_logic_app_workflow_test.go index 4241bb9f5c89..b2c9930a7058 100644 --- a/azurerm/resource_arm_logic_app_workflow_test.go +++ b/azurerm/resource_arm_logic_app_workflow_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMLogicAppWorkflow_empty(t *testing.T) { @@ -37,7 +38,7 @@ func TestAccAzureRMLogicAppWorkflow_empty(t *testing.T) { } func TestAccAzureRMLogicAppWorkflow_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -117,7 +118,7 @@ func testCheckAzureRMLogicAppWorkflowExists(resourceName string) resource.TestCh return fmt.Errorf("Bad: no resource group found in state for Logic App Workflow: %s", workflowName) } - client := testAccProvider.Meta().(*ArmClient).logicWorkflowsClient + client := testAccProvider.Meta().(*ArmClient).logic.WorkflowsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, workflowName) @@ -134,7 +135,7 @@ func testCheckAzureRMLogicAppWorkflowExists(resourceName string) resource.TestCh } func testCheckAzureRMLogicAppWorkflowDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).logicWorkflowsClient + client := testAccProvider.Meta().(*ArmClient).logic.WorkflowsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_managed_disk.go b/azurerm/resource_arm_managed_disk.go index 38b3954cbfa6..27c3710c4e08 100644 --- a/azurerm/resource_arm_managed_disk.go +++ b/azurerm/resource_arm_managed_disk.go @@ -5,13 +5,15 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -99,9 +101,21 @@ func resourceArmManagedDisk() *schema.Resource { ValidateFunc: validateDiskSizeGB, }, + "disk_iops_read_write": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "disk_mbps_read_write": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "encryption_settings": encryptionSettingsSchema(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -116,7 +130,7 @@ func validateDiskSizeGB(v interface{}, _ string) (warnings []string, errors []er } func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).diskClient + client := meta.(*ArmClient).compute.DisksClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ARM Managed Disk creation.") @@ -124,7 +138,7 @@ func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{} name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -140,8 +154,8 @@ func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{} location := azure.NormalizeLocation(d.Get("location").(string)) storageAccountType := d.Get("storage_account_type").(string) osType := d.Get("os_type").(string) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) zones := azure.ExpandZones(d.Get("zones").([]interface{})) var skuName compute.DiskStorageAccountTypes @@ -173,6 +187,24 @@ func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{} createDisk.DiskProperties.DiskSizeGB = &diskSize } + if strings.EqualFold(storageAccountType, string(compute.UltraSSDLRS)) { + if d.HasChange("disk_iops_read_write") { + v := d.Get("disk_iops_read_write") + diskIOPS := int64(v.(int)) + createDisk.DiskProperties.DiskIOPSReadWrite = &diskIOPS + } + + if d.HasChange("disk_mbps_read_write") { + v := d.Get("disk_mbps_read_write") + diskMBps := int32(v.(int)) + createDisk.DiskProperties.DiskMBpsReadWrite = &diskMBps + } + } else { + if d.HasChange("disk_iops_read_write") || d.HasChange("disk_mbps_read_write") { + return fmt.Errorf("[ERROR] disk_iops_read_write and disk_mbps_read_write are only available for UltraSSD disks") + } + } + createOption := d.Get("create_option").(string) createDisk.CreationData = &compute.CreationData{ CreateOption: compute.DiskCreateOption(createOption), @@ -203,7 +235,7 @@ func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{} if v, ok := d.GetOk("encryption_settings"); ok { encryptionSettings := v.([]interface{}) settings := encryptionSettings[0].(map[string]interface{}) - createDisk.EncryptionSettings = expandManagedDiskEncryptionSettings(settings) + createDisk.EncryptionSettingsCollection = expandManagedDiskEncryptionSettings(settings) } future, err := client.CreateOrUpdate(ctx, resGroup, name, createDisk) @@ -229,10 +261,10 @@ func resourceArmManagedDiskCreateUpdate(d *schema.ResourceData, meta interface{} } func resourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).diskClient + client := meta.(*ArmClient).compute.DisksClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -261,35 +293,29 @@ func resourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) error } if props := resp.DiskProperties; props != nil { - if diskSize := props.DiskSizeGB; diskSize != nil { - d.Set("disk_size_gb", *diskSize) - } - if osType := props.OsType; osType != "" { - d.Set("os_type", string(osType)) - } + d.Set("disk_size_gb", props.DiskSizeGB) + d.Set("os_type", props.OsType) + d.Set("disk_iops_read_write", props.DiskIOPSReadWrite) + d.Set("disk_mbps_read_write", props.DiskMBpsReadWrite) } if resp.CreationData != nil { flattenAzureRmManagedDiskCreationData(d, resp.CreationData) } - if settings := resp.EncryptionSettings; settings != nil { - flattened := flattenManagedDiskEncryptionSettings(settings) - if err := d.Set("encryption_settings", flattened); err != nil { - return fmt.Errorf("Error setting encryption settings: %+v", err) - } + flattened := flattenManagedDiskEncryptionSettings(resp.EncryptionSettingsCollection) + if err := d.Set("encryption_settings", flattened); err != nil { + return fmt.Errorf("Error setting encryption settings: %+v", err) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmManagedDiskDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).diskClient + client := meta.(*ArmClient).compute.DisksClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -314,13 +340,9 @@ func resourceArmManagedDiskDelete(d *schema.ResourceData, meta interface{}) erro func flattenAzureRmManagedDiskCreationData(d *schema.ResourceData, creationData *compute.CreationData) { d.Set("create_option", string(creationData.CreateOption)) + d.Set("source_resource_id", creationData.SourceResourceID) + d.Set("source_uri", creationData.SourceURI) if ref := creationData.ImageReference; ref != nil { - d.Set("image_reference_id", *ref.ID) - } - if id := creationData.SourceResourceID; id != nil { - d.Set("source_resource_id", *id) - } - if creationData.SourceURI != nil { - d.Set("source_uri", *creationData.SourceURI) + d.Set("image_reference_id", ref.ID) } } diff --git a/azurerm/resource_arm_managed_disk_test.go b/azurerm/resource_arm_managed_disk_test.go index 439ae7f65e09..6802f4813dd6 100644 --- a/azurerm/resource_arm_managed_disk_test.go +++ b/azurerm/resource_arm_managed_disk_test.go @@ -5,11 +5,12 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMManagedDisk_empty(t *testing.T) { @@ -39,7 +40,7 @@ func TestAccAzureRMManagedDisk_empty(t *testing.T) { } func TestAccAzureRMManagedDisk_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -275,6 +276,93 @@ func TestAccAzureRMManagedDisk_importEmpty_withZone(t *testing.T) { }) } +func TestAccAzureRMManagedDisk_create_withUltraSSD(t *testing.T) { + resourceName := "azurerm_managed_disk.test" + ri := tf.AccRandTimeInt() + location := "eastus2" + var d compute.Disk + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagedDiskDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMManagedDisk_create_withUltraSSD(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagedDiskExists(resourceName, &d, true), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMManagedDisk_update_withUltraSSD(t *testing.T) { + resourceName := "azurerm_managed_disk.test" + ri := tf.AccRandTimeInt() + location := "eastus2" + var d compute.Disk + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagedDiskDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMManagedDisk_create_withUltraSSD(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagedDiskExists(resourceName, &d, true), + resource.TestCheckResourceAttr(resourceName, "disk_iops_read_write", "101"), + resource.TestCheckResourceAttr(resourceName, "disk_mbps_read_write", "10"), + ), + }, + { + Config: testAccAzureRMManagedDisk_update_withUltraSSD(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagedDiskExists(resourceName, &d, true), + resource.TestCheckResourceAttr(resourceName, "disk_iops_read_write", "102"), + resource.TestCheckResourceAttr(resourceName, "disk_mbps_read_write", "11"), + ), + }, + }, + }) +} + +func TestAccAzureRMManagedDisk_import_withUltraSSD(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_managed_disk.test" + ri := tf.AccRandTimeInt() + location := "eastus2" + var d compute.Disk + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMManagedDiskDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMManagedDisk_create_withUltraSSD(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMManagedDiskExists(resourceName, &d, true), + ), + }, + { + Config: testAccAzureRMManagedDisk_import_withUltraSSD(ri, location), + ExpectError: testRequiresImportError("azurerm_managed_disk"), + }, + }, + }) +} + func testCheckAzureRMManagedDiskExists(resourceName string, d *compute.Disk, shouldExist bool) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -288,7 +376,7 @@ func testCheckAzureRMManagedDiskExists(resourceName string, d *compute.Disk, sho return fmt.Errorf("Bad: no resource group found in state for disk: %s", dName) } - client := testAccProvider.Meta().(*ArmClient).diskClient + client := testAccProvider.Meta().(*ArmClient).compute.DisksClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, dName) @@ -310,7 +398,7 @@ func testCheckAzureRMManagedDiskExists(resourceName string, d *compute.Disk, sho } func testCheckAzureRMManagedDiskDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).diskClient + client := testAccProvider.Meta().(*ArmClient).compute.DisksClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -348,7 +436,7 @@ func testDeleteAzureRMVirtualMachine(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for virtual machine: %s", vmName) } - client := testAccProvider.Meta().(*ArmClient).vmClient + client := testAccProvider.Meta().(*ArmClient).compute.VMClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, vmName) @@ -700,3 +788,78 @@ resource "azurerm_managed_disk" "test" { } `, rInt, location, rString, rString, rString, rInt) } + +func testAccAzureRMManagedDisk_create_withUltraSSD(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "UltraSSD_LRS" + create_option = "Empty" + disk_size_gb = "4" + disk_iops_read_write = "101" + disk_mbps_read_write = "10" + zones = ["1"] + + tags = { + environment = "acctest" + cost-center = "ops" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagedDisk_update_withUltraSSD(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_managed_disk" "test" { + name = "acctestd-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "UltraSSD_LRS" + create_option = "Empty" + disk_size_gb = "4" + disk_iops_read_write = "102" + disk_mbps_read_write = "11" + zones = ["1"] + + tags = { + environment = "acctest" + cost-center = "ops" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMManagedDisk_import_withUltraSSD(rInt int, location string) string { + template := testAccAzureRMManagedDisk_create_withUltraSSD(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_managed_disk" "import" { + name = "${azurerm_managed_disk.test.name}" + location = "${azurerm_managed_disk.test.location}" + resource_group_name = "${azurerm_managed_disk.test.resource_group_name}" + storage_account_type = "UltraSSD_LRS" + create_option = "Empty" + disk_size_gb = "4" + disk_iops_read_write = "101" + disk_mbps_read_write = "10" + + tags = { + environment = "acctest" + cost-center = "ops" + } +} +`, template) +} diff --git a/azurerm/resource_arm_management_group.go b/azurerm/resource_arm_management_group.go index 3cda20326707..5881b6aae759 100644 --- a/azurerm/resource_arm_management_group.go +++ b/azurerm/resource_arm_management_group.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -56,8 +57,8 @@ func resourceArmManagementGroup() *schema.Resource { } func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementGroupsClient - subscriptionsClient := meta.(*ArmClient).managementGroupsSubscriptionClient + client := meta.(*ArmClient).managementGroups.GroupsClient + subscriptionsClient := meta.(*ArmClient).managementGroups.SubscriptionClient ctx := meta.(*ArmClient).StopContext armTenantID := meta.(*ArmClient).tenantId @@ -72,7 +73,7 @@ func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interfa } recurse := false - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, groupId, "children", &recurse, "", managementGroupCacheControl) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -156,7 +157,7 @@ func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementGroupsClient + client := meta.(*ArmClient).managementGroups.GroupsClient ctx := meta.(*ArmClient).StopContext id, err := parseManagementGroupId(d.Id()) @@ -196,15 +197,14 @@ func resourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) er } } d.Set("parent_management_group_id", parentId) - } return nil } func resourceArmManagementGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementGroupsClient - subscriptionsClient := meta.(*ArmClient).managementGroupsSubscriptionClient + client := meta.(*ArmClient).managementGroups.GroupsClient + subscriptionsClient := meta.(*ArmClient).managementGroups.SubscriptionClient ctx := meta.(*ArmClient).StopContext id, err := parseManagementGroupId(d.Id()) @@ -282,7 +282,7 @@ func flattenArmManagementGroupSubscriptionIds(input *[]managementgroups.ChildInf id, err := parseManagementGroupSubscriptionID(*child.ID) if err != nil { - return nil, fmt.Errorf("Unable to parse child subscription ID %+v", err) + return nil, fmt.Errorf("Unable to parse child Subscription ID %+v", err) } if id != nil { diff --git a/azurerm/resource_arm_management_group_test.go b/azurerm/resource_arm_management_group_test.go index 0f73fef6ee3e..8fd07043e99a 100644 --- a/azurerm/resource_arm_management_group_test.go +++ b/azurerm/resource_arm_management_group_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMManagementGroup_basic(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMManagementGroup_basic(t *testing.T) { } func TestAccAzureRMManagementGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -219,7 +220,7 @@ func testCheckAzureRMManagementGroupExists(resourceName string) resource.TestChe groupName := rs.Primary.Attributes["group_id"] - client := testAccProvider.Meta().(*ArmClient).managementGroupsClient + client := testAccProvider.Meta().(*ArmClient).managementGroups.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext recurse := false @@ -237,7 +238,7 @@ func testCheckAzureRMManagementGroupExists(resourceName string) resource.TestChe } func testCheckAzureRMManagementGroupDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).managementGroupsClient + client := testAccProvider.Meta().(*ArmClient).managementGroups.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_management_lock.go b/azurerm/resource_arm_management_lock.go index f8222082384a..80e8f04e6330 100644 --- a/azurerm/resource_arm_management_lock.go +++ b/azurerm/resource_arm_management_lock.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -57,14 +58,14 @@ func resourceArmManagementLock() *schema.Resource { } func resourceArmManagementLockCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementLocksClient + client := meta.(*ArmClient).resource.LocksClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Management Lock creation.") name := d.Get("name").(string) scope := d.Get("scope").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetByScope(ctx, scope, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -105,7 +106,7 @@ func resourceArmManagementLockCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmManagementLockRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementLocksClient + client := meta.(*ArmClient).resource.LocksClient ctx := meta.(*ArmClient).StopContext id, err := parseAzureRMLockId(d.Id()) @@ -134,7 +135,7 @@ func resourceArmManagementLockRead(d *schema.ResourceData, meta interface{}) err } func resourceArmManagementLockDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).managementLocksClient + client := meta.(*ArmClient).resource.LocksClient ctx := meta.(*ArmClient).StopContext id, err := parseAzureRMLockId(d.Id()) diff --git a/azurerm/resource_arm_management_lock_test.go b/azurerm/resource_arm_management_lock_test.go index e3e406dbd044..86222d63a9b6 100644 --- a/azurerm/resource_arm_management_lock_test.go +++ b/azurerm/resource_arm_management_lock_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -67,7 +68,7 @@ func TestAccAzureRMManagementLock_resourceGroupReadOnlyBasic(t *testing.T) { } func TestAccAzureRMManagementLock_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -296,7 +297,7 @@ func testCheckAzureRMManagementLockExists(resourceName string) resource.TestChec name := rs.Primary.Attributes["name"] scope := rs.Primary.Attributes["scope"] - client := testAccProvider.Meta().(*ArmClient).managementLocksClient + client := testAccProvider.Meta().(*ArmClient).resource.LocksClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.GetByScope(ctx, scope, name) @@ -313,7 +314,7 @@ func testCheckAzureRMManagementLockExists(resourceName string) resource.TestChec } func testCheckAzureRMManagementLockDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).managementLocksClient + client := testAccProvider.Meta().(*ArmClient).resource.LocksClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_maps_account.go b/azurerm/resource_arm_maps_account.go new file mode 100644 index 000000000000..3ae74ca2aba0 --- /dev/null +++ b/azurerm/resource_arm_maps_account.go @@ -0,0 +1,176 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/maps/mgmt/2018-05-01/maps" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + mapsint "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maps" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmMapsAccount() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMapsAccountCreateUpdate, + Read: resourceArmMapsAccountRead, + Update: resourceArmMapsAccountCreateUpdate, + Delete: resourceArmMapsAccountDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: mapsint.ValidateName(), + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "sku_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "s0", + "s1", + }, false), + }, + + "tags": tags.Schema(), + + "x_ms_client_id": { + Type: schema.TypeString, + Computed: true, + }, + + "primary_access_key": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + + "secondary_access_key": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + } +} + +func resourceArmMapsAccountCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).maps.AccountsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM Maps Account creation.") + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + t := d.Get("tags").(map[string]interface{}) + sku := d.Get("sku_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Maps Account %q (Resource Group %q): %+v", name, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_maps_account", *existing.ID) + } + } + + parameters := maps.AccountCreateParameters{ + Location: utils.String("global"), + Sku: &maps.Sku{ + Name: &sku, + }, + Tags: tags.Expand(t), + } + + if _, err := client.CreateOrUpdate(ctx, resGroup, name, parameters); err != nil { + return fmt.Errorf("Error creating/updating Maps Account %q (Resource Group %q) %+v", name, resGroup, err) + } + + read, err := client.Get(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Maps Account %q (Resource Group %q) %+v", name, resGroup, err) + } + + if read.ID == nil { + return fmt.Errorf("Cannot read Maps Account %q (Resource Group %q) ID", name, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmMapsAccountRead(d, meta) +} + +func resourceArmMapsAccountRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).maps.AccountsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["accounts"] + + resp, err := client.Get(ctx, resGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Maps Account %q (Resource Group %q): %+v", name, resGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + if sku := resp.Sku; sku != nil { + d.Set("sku_name", sku.Name) + } + if props := resp.Properties; props != nil { + d.Set("x_ms_client_id", props.XMsClientID) + } + + keysResp, err := client.ListKeys(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error making Read Access Keys request on Maps Account %q (Resource Group %q): %+v", name, resGroup, err) + } + d.Set("primary_access_key", keysResp.PrimaryKey) + d.Set("secondary_access_key", keysResp.SecondaryKey) + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmMapsAccountDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).maps.AccountsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["accounts"] + + if _, err := client.Delete(ctx, resGroup, name); err != nil { + return fmt.Errorf("Error deleting Maps Account %q (Resource Group %q): %+v", name, resGroup, err) + } + + return nil +} diff --git a/azurerm/resource_arm_maps_account_test.go b/azurerm/resource_arm_maps_account_test.go new file mode 100644 index 000000000000..17c304ba4bb6 --- /dev/null +++ b/azurerm/resource_arm_maps_account_test.go @@ -0,0 +1,208 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMapsAccount_basic(t *testing.T) { + resourceName := "azurerm_maps_account.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMapsAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMapsAccount_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "name"), + resource.TestCheckResourceAttrSet(resourceName, "x_ms_client_id"), + resource.TestCheckResourceAttrSet(resourceName, "primary_access_key"), + resource.TestCheckResourceAttrSet(resourceName, "secondary_access_key"), + resource.TestCheckResourceAttr(resourceName, "sku_name", "s0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMMapsAccount_sku(t *testing.T) { + resourceName := "azurerm_maps_account.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMapsAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMapsAccount_sku(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "name"), + resource.TestCheckResourceAttrSet(resourceName, "x_ms_client_id"), + resource.TestCheckResourceAttrSet(resourceName, "primary_access_key"), + resource.TestCheckResourceAttrSet(resourceName, "secondary_access_key"), + resource.TestCheckResourceAttr(resourceName, "sku_name", "s1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMMapsAccount_tags(t *testing.T) { + resourceName := "azurerm_maps_account.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMapsAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMapsAccount_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMapsAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMMapsAccount_tags(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMapsAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "testing"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMMapsAccountExists(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) + } + + mapsAccountName := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).maps.AccountsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, mapsAccountName) + if err != nil { + return fmt.Errorf("Bad: Get on MapsAccountClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Maps Account %q (resource group: %q) does not exist", mapsAccountName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMMapsAccountDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).maps.AccountsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_maps_account" { + 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 fmt.Errorf("Error retrieving Maps Account %q (Resource Group %q): %s", name, resourceGroup, err) + } + } + + return nil +} + +func testAccAzureRMMapsAccount_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_maps_account" "test" { + name = "accMapsAccount-%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "s0" +} +`, rInt, location, rInt) +} + +func testAccAzureRMMapsAccount_sku(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_maps_account" "test" { + name = "accMapsAccount-%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "s1" +} +`, rInt, location, rInt) +} + +func testAccAzureRMMapsAccount_tags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_maps_account" "test" { + name = "accMapsAccount-%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "s0" + + tags = { + environment = "testing" + } +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_mariadb_configuration.go b/azurerm/resource_arm_mariadb_configuration.go new file mode 100644 index 000000000000..21d5f4b2e982 --- /dev/null +++ b/azurerm/resource_arm_mariadb_configuration.go @@ -0,0 +1,155 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "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 resourceArmMariaDbConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMariaDbConfigurationCreateUpdate, + Read: resourceArmMariaDbConfigurationRead, + Delete: resourceArmMariaDbConfigurationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[-a-zA-Z0-9]{3,50}$"), + "server_name must be 3 - 50 characters long, and contain only letters, numbers and hyphens", + ), + }, + + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceArmMariaDbConfigurationCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.ConfigurationsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM MariaDb Configuration creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + serverName := d.Get("server_name").(string) + value := d.Get("value").(string) + + properties := mariadb.Configuration{ + ConfigurationProperties: &mariadb.ConfigurationProperties{ + Value: utils.String(value), + }, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, properties) + if err != nil { + return fmt.Errorf("Error issuing create/update request for MariaDb Configuration %s (resource group %s, server name %s): %v", name, resourceGroup, serverName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for create/update of MariaDb Configuration %s (resource group %s, server name %s): %v", name, resourceGroup, serverName, err) + } + + read, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error issuing get request for MariaDb Configuration %s (resource group %s, server name %s): %v", name, resourceGroup, serverName, err) + } + if read.ID == nil { + return fmt.Errorf("Cannot read MariaDb Configuration %s (resource group %s, server name %s) ID", name, resourceGroup, serverName) + } + + d.SetId(*read.ID) + + return resourceArmMariaDbConfigurationRead(d, meta) +} + +func resourceArmMariaDbConfigurationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.ConfigurationsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["configurations"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[WARN] MariaDb Configuration %q was not found (resource group %q)", name, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Azure MariaDb Configuration %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("server_name", serverName) + d.Set("resource_group_name", resourceGroup) + d.Set("value", resp.ConfigurationProperties.Value) + + return nil +} + +func resourceArmMariaDbConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.ConfigurationsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["configurations"] + + // "delete" = resetting this to the default value + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error retrieving MariaDb Configuration %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + properties := mariadb.Configuration{ + ConfigurationProperties: &mariadb.ConfigurationProperties{ + // we can alternatively set `source: "system-default"` + Value: resp.DefaultValue, + }, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, properties) + if err != nil { + return err + } + + return future.WaitForCompletionRef(ctx, client.Client) +} diff --git a/azurerm/resource_arm_mariadb_configuration_test.go b/azurerm/resource_arm_mariadb_configuration_test.go new file mode 100644 index 000000000000..fce5b21a72f9 --- /dev/null +++ b/azurerm/resource_arm_mariadb_configuration_test.go @@ -0,0 +1,251 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMariaDbConfiguration_characterSetServer(t *testing.T) { + resourceName := "azurerm_mariadb_configuration.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDbConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDbConfiguration_characterSetServer(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDbConfigurationValue(resourceName, "hebrew"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMMariaDbConfiguration_empty(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + // "delete" resets back to the default value + testCheckAzureRMMariaDbConfigurationValueReset(ri, "character_set_server"), + ), + }, + }, + }) +} + +func TestAccAzureRMMariaDbConfiguration_interactiveTimeout(t *testing.T) { + resourceName := "azurerm_mariadb_configuration.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDbConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDbConfiguration_interactiveTimeout(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDbConfigurationValue(resourceName, "30"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMMariaDbConfiguration_empty(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + // "delete" resets back to the default value + testCheckAzureRMMariaDbConfigurationValueReset(ri, "interactive_timeout"), + ), + }, + }, + }) +} + +func TestAccAzureRMMariaDbConfiguration_logSlowAdminStatements(t *testing.T) { + resourceName := "azurerm_mariadb_configuration.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDbConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDbConfiguration_logSlowAdminStatements(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDbConfigurationValue(resourceName, "on"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMMariaDbConfiguration_empty(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + // "delete" resets back to the default value + testCheckAzureRMMariaDbConfigurationValueReset(ri, "log_slow_admin_statements"), + ), + }, + }, + }) +} + +func testCheckAzureRMMariaDbConfigurationValue(resourceName string, value 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"] + serverName := rs.Primary.Attributes["server_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for MariaDb Configuration: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).mariadb.ConfigurationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: MariaDb Configuration %q (server %q resource group: %q) does not exist", name, serverName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on mariadbConfigurationsClient: %+v", err) + } + + if *resp.Value != value { + return fmt.Errorf("MariaDb Configuration wasn't set. Expected '%s' - got '%s': \n%+v", value, *resp.Value, resp) + } + + return nil + } +} + +func testCheckAzureRMMariaDbConfigurationValueReset(rInt int, configurationName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceGroup := fmt.Sprintf("acctestRG-%d", rInt) + serverName := fmt.Sprintf("acctestmariadbsvr-%d", rInt) + + client := testAccProvider.Meta().(*ArmClient).mariadb.ConfigurationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, configurationName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: MariaDb Configuration %q (server %q resource group: %q) does not exist", configurationName, serverName, resourceGroup) + } + return fmt.Errorf("Bad: Get on mariadbConfigurationsClient: %+v", err) + } + + actualValue := *resp.Value + defaultValue := *resp.DefaultValue + + if defaultValue != actualValue { + return fmt.Errorf("MariaDb Configuration wasn't set to the default value. Expected '%s' - got '%s': \n%+v", defaultValue, actualValue, resp) + } + + return nil + } +} + +func testCheckAzureRMMariaDbConfigurationDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).mariadb.ConfigurationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_mariadb_configuration" { + continue + } + + name := rs.Primary.Attributes["name"] + serverName := rs.Primary.Attributes["server_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + } + + return nil +} + +func testAccAzureRMMariaDbConfiguration_characterSetServer(rInt int, location string) string { + return testAccAzureRMMariaDbConfiguration_template(rInt, location, "character_set_server", "hebrew") +} + +func testAccAzureRMMariaDbConfiguration_interactiveTimeout(rInt int, location string) string { + return testAccAzureRMMariaDbConfiguration_template(rInt, location, "interactive_timeout", "30") +} + +func testAccAzureRMMariaDbConfiguration_logSlowAdminStatements(rInt int, location string) string { + return testAccAzureRMMariaDbConfiguration_template(rInt, location, "log_slow_admin_statements", "on") +} + +func testAccAzureRMMariaDbConfiguration_template(rInt int, location string, name string, value string) string { + server := testAccAzureRMMariaDbConfiguration_empty(rInt, location) + config := fmt.Sprintf(` +resource "azurerm_mariadb_configuration" "test" { + name = "%s" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + value = "%s" +} +`, name, value) + return server + config +} + +func testAccAzureRMMariaDbConfiguration_empty(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + ssl_enforcement = "Enabled" +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_mariadb_database.go b/azurerm/resource_arm_mariadb_database.go index 0d403c201f56..563b81abc068 100644 --- a/azurerm/resource_arm_mariadb_database.go +++ b/azurerm/resource_arm_mariadb_database.go @@ -11,6 +11,8 @@ import ( "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -60,14 +62,14 @@ func resourceArmMariaDbDatabase() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateCollation(), + ValidateFunc: validate.DatabaseCollation, }, }, } } func resourceArmMariaDbDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbDatabasesClient + client := meta.(*ArmClient).mariadb.DatabasesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MariaDB database creation") @@ -76,7 +78,7 @@ func resourceArmMariaDbDatabaseCreateUpdate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) serverName := d.Get("server_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -122,10 +124,10 @@ func resourceArmMariaDbDatabaseCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmMariaDbDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbDatabasesClient + client := meta.(*ArmClient).mariadb.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return fmt.Errorf("cannot parse MariaDB database %q ID:\n%+v", d.Id(), err) } @@ -157,10 +159,10 @@ func resourceArmMariaDbDatabaseRead(d *schema.ResourceData, meta interface{}) er } func resourceArmMariaDbDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbDatabasesClient + client := meta.(*ArmClient).mariadb.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return fmt.Errorf("cannot parse MariaDB database %q ID:\n%+v", d.Id(), err) } diff --git a/azurerm/resource_arm_mariadb_database_test.go b/azurerm/resource_arm_mariadb_database_test.go index 77fad770c357..4789c83ff335 100644 --- a/azurerm/resource_arm_mariadb_database_test.go +++ b/azurerm/resource_arm_mariadb_database_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMMariaDbDatabase_basic(t *testing.T) { } func TestAccAzureRMMariaDbDatabase_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -81,7 +82,7 @@ func testCheckAzureRMMariaDbDatabaseExists(resourceName string) resource.TestChe return fmt.Errorf("bad: no resource group found in state for MariaDB database: %q", name) } - client := testAccProvider.Meta().(*ArmClient).mariadbDatabasesClient + client := testAccProvider.Meta().(*ArmClient).mariadb.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -97,7 +98,7 @@ func testCheckAzureRMMariaDbDatabaseExists(resourceName string) resource.TestChe } func testCheckAzureRMMariaDbDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mariadbDatabasesClient + client := testAccProvider.Meta().(*ArmClient).mariadb.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_mariadb_firewall_rule.go b/azurerm/resource_arm_mariadb_firewall_rule.go new file mode 100644 index 000000000000..d020188b74b4 --- /dev/null +++ b/azurerm/resource_arm_mariadb_firewall_rule.go @@ -0,0 +1,160 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmMariaDBFirewallRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMariaDBFirewallRuleCreateUpdate, + Read: resourceArmMariaDBFirewallRuleRead, + Update: resourceArmMariaDBFirewallRuleCreateUpdate, + Delete: resourceArmMariaDBFirewallRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.MariaDBFirewallRuleName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.MariaDBServerName, + }, + + "start_ip_address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.IPv4Address, + }, + + "end_ip_address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.IPv4Address, + }, + }, + } +} + +func resourceArmMariaDBFirewallRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.FirewallRulesClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM MariaDB Firewall Rule creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + serverName := d.Get("server_name").(string) + startIPAddress := d.Get("start_ip_address").(string) + endIPAddress := d.Get("end_ip_address").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing MariaDB Firewall Rule %q (resource group %q, server name %q): %v", name, resourceGroup, serverName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_mariadb_firewall_rule", *existing.ID) + } + } + + properties := mariadb.FirewallRule{ + FirewallRuleProperties: &mariadb.FirewallRuleProperties{ + StartIPAddress: utils.String(startIPAddress), + EndIPAddress: utils.String(endIPAddress), + }, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, properties) + if err != nil { + return fmt.Errorf("Error issuing create/update request for MariaDB Firewall Rule %q (resource group %q, server name %q): %v", name, resourceGroup, serverName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting onf create/update future for MariaDB Firewall Rule %q (resource group %q, server name %q): %v", name, resourceGroup, serverName, err) + } + + read, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error issuing get request for MariaDB Firewall Rule %q (resource group %q, server name %q): %v", name, resourceGroup, serverName, err) + } + if read.ID == nil { + return fmt.Errorf("Cannot read MariaDB Firewall Rule %q (Gesource Group %q) ID", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmMariaDBFirewallRuleRead(d, meta) +} + +func resourceArmMariaDBFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.FirewallRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["firewallRules"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Azure MariaDB Firewall Rule %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + d.Set("start_ip_address", resp.StartIPAddress) + d.Set("end_ip_address", resp.EndIPAddress) + + return nil +} + +func resourceArmMariaDBFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.FirewallRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["firewallRules"] + + future, err := client.Delete(ctx, resourceGroup, serverName, name) + if err != nil { + return err + } + + return future.WaitForCompletionRef(ctx, client.Client) +} diff --git a/azurerm/resource_arm_mariadb_firewall_rule_test.go b/azurerm/resource_arm_mariadb_firewall_rule_test.go new file mode 100644 index 000000000000..aa87486040d5 --- /dev/null +++ b/azurerm/resource_arm_mariadb_firewall_rule_test.go @@ -0,0 +1,176 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMariaDBFirewallRule_basic(t *testing.T) { + resourceName := "azurerm_mariadb_firewall_rule.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDBFirewallRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBFirewallRuleExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMMariaDBFirewallRule_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_mariadb_firewall_rule.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDBFirewallRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBFirewallRuleExists(resourceName), + ), + }, + { + Config: testAccAzureRMMariaDBFirewallRule_requiresImport(ri, testLocation()), + ExpectError: testRequiresImportError("azurerm_mariadb_firewall_rule"), + }, + }, + }) +} + +func testCheckAzureRMMariaDBFirewallRuleExists(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"] + serverName := rs.Primary.Attributes["server_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for MariaDB Firewall Rule: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).mariadb.FirewallRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: MariaDB Firewall Rule %q (server %q resource group: %q) does not exist", name, serverName, resourceGroup) + } + return fmt.Errorf("Bad: Get on mariadbFirewallRulesClient: %s", err) + } + + return nil + } +} + +func testCheckAzureRMMariaDBFirewallRuleDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).mariadb.DatabasesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_mariadb_firewall_rule" { + continue + } + + name := rs.Primary.Attributes["name"] + serverName := rs.Primary.Attributes["server_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return fmt.Errorf("MariaDB Firewall Rule still exists:\n%#v", resp) + } + } + + return nil +} + +func testAccAzureRMMariaDBFirewallRule_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + ssl_enforcement = "Enabled" +} + +resource "azurerm_mariadb_firewall_rule" "test" { + name = "acctestfwrule-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMMariaDBFirewallRule_requiresImport(rInt int, location string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_mariadb_firewall_rule" "import" { + name = "${azurerm_mariadb_firewall_rule.test.name}" + resource_group_name = "${azurerm_mariadb_firewall_rule.test.resource_group_name}" + server_name = "${azurerm_mariadb_firewall_rule.test.server_name}" + start_ip_address = "${azurerm_mariadb_firewall_rule.test.start_ip_address}" + end_ip_address = "${azurerm_mariadb_firewall_rule.test.end_ip_address}" +} +`, testAccAzureRMMariaDBFirewallRule_basic(rInt, location)) +} diff --git a/azurerm/resource_arm_mariadb_server.go b/azurerm/resource_arm_mariadb_server.go index c1826cc6f74d..a3bb07340cb4 100644 --- a/azurerm/resource_arm_mariadb_server.go +++ b/azurerm/resource_arm_mariadb_server.go @@ -10,6 +10,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" "github.com/hashicorp/terraform/helper/schema" @@ -121,6 +123,7 @@ func resourceArmMariaDbServer() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ "10.2", + "10.3", }, false), }, @@ -150,6 +153,16 @@ func resourceArmMariaDbServer() *schema.Resource { string(mariadb.Disabled), }, false), }, + + "auto_grow": { + Type: schema.TypeString, + Optional: true, + Default: string(mariadb.StorageAutogrowEnabled), + ValidateFunc: validation.StringInSlice([]string{ + string(mariadb.StorageAutogrowEnabled), + string(mariadb.StorageAutogrowDisabled), + }, false), + }, }, }, }, @@ -168,13 +181,13 @@ func resourceArmMariaDbServer() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmMariaDbServerCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbServersClient + client := meta.(*ArmClient).mariadb.ServersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MariaDB Server creation.") @@ -182,7 +195,7 @@ func resourceArmMariaDbServerCreateUpdate(d *schema.ResourceData, meta interface name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -200,7 +213,7 @@ func resourceArmMariaDbServerCreateUpdate(d *schema.ResourceData, meta interface adminLoginPassword := d.Get("administrator_login_password").(string) sslEnforcement := d.Get("ssl_enforcement").(string) version := d.Get("version").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) sku := expandAzureRmMariaDbServerSku(d) storageProfile := expandAzureRmMariaDbStorageProfile(d) @@ -268,7 +281,7 @@ func resourceArmMariaDbServerCreateUpdate(d *schema.ResourceData, meta interface CreateMode: mariadb.CreateModeDefault, }, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, properties) @@ -295,10 +308,10 @@ func resourceArmMariaDbServerCreateUpdate(d *schema.ResourceData, meta interface } func resourceArmMariaDbServerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbServersClient + client := meta.(*ArmClient).mariadb.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -339,16 +352,14 @@ func resourceArmMariaDbServerRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error setting `sku`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMariaDbServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mariadbServersClient + client := meta.(*ArmClient).mariadb.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -399,11 +410,13 @@ func expandAzureRmMariaDbStorageProfile(d *schema.ResourceData) *mariadb.Storage backupRetentionDays := storageprofile["backup_retention_days"].(int) geoRedundantBackup := storageprofile["geo_redundant_backup"].(string) storageMB := storageprofile["storage_mb"].(int) + autoGrow := storageprofile["auto_grow"].(string) return &mariadb.StorageProfile{ BackupRetentionDays: utils.Int32(int32(backupRetentionDays)), GeoRedundantBackup: mariadb.GeoRedundantBackup(geoRedundantBackup), StorageMB: utils.Int32(int32(storageMB)), + StorageAutogrow: mariadb.StorageAutogrow(autoGrow), } } @@ -448,5 +461,7 @@ func flattenMariaDbStorageProfile(storage *mariadb.StorageProfile) []interface{} values["geo_redundant_backup"] = string(storage.GeoRedundantBackup) + values["auto_grow"] = string(storage.StorageAutogrow) + return []interface{}{values} } diff --git a/azurerm/resource_arm_mariadb_server_test.go b/azurerm/resource_arm_mariadb_server_test.go index f1112aac062c..8d4e8acbfc0b 100644 --- a/azurerm/resource_arm_mariadb_server_test.go +++ b/azurerm/resource_arm_mariadb_server_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -42,7 +43,7 @@ func TestAccAzureRMMariaDbServer_basic(t *testing.T) { } func TestAccAzureRMMariaDbServer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -261,7 +262,35 @@ func TestAccAzureRMMariaDbServer_updateSKU(t *testing.T) { }) } -// +func TestAccAzureRMMariaDbServer_storageAutogrow(t *testing.T) { + resourceName := "azurerm_mariadb_server.test" + ri := tf.AccRandTimeInt() + location := testLocation() + config := testAccAzureRMMariaDbServer_basic(ri, location) + updatedConfig := testAccAzureRMMariaDbServer_storageAutogrowUpdated(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDbServerDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDbServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Enabled"), + ), + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDbServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Disabled"), + ), + }, + }, + }) +} func testCheckAzureRMMariaDbServerExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -277,7 +306,7 @@ func testCheckAzureRMMariaDbServerExists(resourceName string) resource.TestCheck return fmt.Errorf("Bad: no resource group found in state for MariaDB Server: %s", name) } - client := testAccProvider.Meta().(*ArmClient).mariadbServersClient + client := testAccProvider.Meta().(*ArmClient).mariadb.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -294,7 +323,7 @@ func testCheckAzureRMMariaDbServerExists(resourceName string) resource.TestCheck } func testCheckAzureRMMariaDbServerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mariadbServersClient + client := testAccProvider.Meta().(*ArmClient).mariadb.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -581,3 +610,37 @@ resource "azurerm_mariadb_server" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMMariaDbServer_storageAutogrowUpdated(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "B_Gen5_2" + capacity = 2 + tier = "Basic" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + auto_grow = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + ssl_enforcement = "Enabled" +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_mariadb_virtual_network_rule.go b/azurerm/resource_arm_mariadb_virtual_network_rule.go new file mode 100644 index 000000000000..653cb0c1a3aa --- /dev/null +++ b/azurerm/resource_arm_mariadb_virtual_network_rule.go @@ -0,0 +1,243 @@ +package azurerm + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmMariaDbVirtualNetworkRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMariaDbVirtualNetworkRuleCreateUpdate, + Read: resourceArmMariaDbVirtualNetworkRuleRead, + Update: resourceArmMariaDbVirtualNetworkRuleCreateUpdate, + Delete: resourceArmMariaDbVirtualNetworkRuleDelete, + 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": azure.SchemaResourceGroupName(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "subnet_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + } +} + +func resourceArmMariaDbVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.VirtualNetworkRulesClient + 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) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_mariadb_virtual_network_rule", *existing.ID) + } + } + + // 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).network.SubnetsClient + subnetParsedId, err := azure.ParseAzureResourceID(subnetId) + if err != nil { + return err + } + + 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 MariaDb Virtual Network Rule: Subnet %q (Virtual Network %q / Resource Group %q) must contain a Service Endpoint for `Microsoft.Sql`", subnetName, virtualNetwork, subnetResourceGroup) + } + + parameters := mariadb.VirtualNetworkRule{ + VirtualNetworkRuleProperties: &mariadb.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetID: utils.String(subnetId), + IgnoreMissingVnetServiceEndpoint: utils.Bool(false), + }, + } + + if _, err = client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters); err != nil { + return fmt.Errorf("Error creating MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + //Wait for the provisioning state to become ready + log.Printf("[DEBUG] Waiting for MariaDb Virtual Network Rule %q (MariaDb 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: MariaDbVirtualNetworkStateStatusCodeRefreshFunc(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 MariaDb Virtual Network Rule %q (MariaDb 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 MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmMariaDbVirtualNetworkRuleRead(d, meta) +} + +func resourceArmMariaDbVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.VirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.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 MariaDb Virtual Network Rule %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading MariaDb Virtual Network Rule: %q (MariaDb 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 resourceArmMariaDbVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mariadb.VirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.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 MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deletion of MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + } + + return nil +} + +func MariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *mariadb.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 MariaDb Virtual Network Rule %q (MariaDb 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 MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + if props := resp.VirtualNetworkRuleProperties; props != nil { + log.Printf("[DEBUG] Retrieving MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q) returned Status %s", resourceGroup, serverName, name, props.State) + return resp, string(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 MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q) returned empty VirtualNetworkRuleProperties", resourceGroup, serverName, name) + return resp, "Unknown", nil + } +} diff --git a/azurerm/resource_arm_mariadb_virtual_network_rule_test.go b/azurerm/resource_arm_mariadb_virtual_network_rule_test.go new file mode 100644 index 000000000000..6159d23accb9 --- /dev/null +++ b/azurerm/resource_arm_mariadb_virtual_network_rule_test.go @@ -0,0 +1,517 @@ +package azurerm + +import ( + "fmt" + "regexp" + "testing" + + "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMariaDBVirtualNetworkRule_basic(t *testing.T) { + resourceName := "azurerm_mariadb_virtual_network_rule.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDBVirtualNetworkRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMMariaDBVirtualNetworkRule_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_mariadb_virtual_network_rule.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMariaDBVirtualNetworkRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName), + ), + }, + { + Config: testAccAzureRMMariaDBVirtualNetworkRule_requiresImport(ri, testLocation()), + ExpectError: testRequiresImportError("azurerm_mariadb_virtual_network_rule"), + }, + }, + }) +} + +func TestAccAzureRMMariaDBVirtualNetworkRule_switchSubnets(t *testing.T) { + resourceName := "azurerm_mariadb_virtual_network_rule.test" + ri := tf.AccRandTimeInt() + + preConfig := testAccAzureRMMariaDBVirtualNetworkRule_subnetSwitchPre(ri, testLocation()) + postConfig := testAccAzureRMMariaDBVirtualNetworkRule_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.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", preConfigRegex), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", postConfigRegex), + ), + }, + }, + }) +} + +func TestAccAzureRMMariaDBVirtualNetworkRule_disappears(t *testing.T) { + resourceName := "azurerm_mariadb_virtual_network_rule.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMMariaDBVirtualNetworkRule_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName), + testCheckAzureRMMariaDBVirtualNetworkRuleDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMMariaDBVirtualNetworkRule_multipleSubnets(t *testing.T) { + resourceName1 := "azurerm_mariadb_virtual_network_rule.rule1" + resourceName2 := "azurerm_mariadb_virtual_network_rule.rule2" + resourceName3 := "azurerm_mariadb_virtual_network_rule.rule3" + ri := tf.AccRandTimeInt() + config := testAccAzureRMMariaDBVirtualNetworkRule_multipleSubnets(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMariaDBVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName1), + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName2), + testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName3), + ), + }, + }, + }) +} + +func testCheckAzureRMMariaDBVirtualNetworkRuleExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mariadb.VirtualNetworkRulesClient + 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: MariaDB Virtual Network Rule %q (Server %q / Resource Group %q) was not found", ruleName, serverName, resourceGroup) + } + + return err + } + + return nil + } +} + +func testCheckAzureRMMariaDBVirtualNetworkRuleDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_mariadb_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).mariadb.VirtualNetworkRulesClient + 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: MariaDB Firewall Rule %q (Server %q / Resource Group %q) still exists: %+v", ruleName, serverName, resourceGroup, resp) + } + + return nil +} + +func testCheckAzureRMMariaDBVirtualNetworkRuleDisappears(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) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mariadb.VirtualNetworkRulesClient + 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 MariaDB Virtual Network Rule: %+v", err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + //Same deal as before. Just in case. + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MariaDB Virtual Network Rule: %+v", err) + } + + return nil + } +} + +func testAccAzureRMMariaDBVirtualNetworkRule_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_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + 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_mariadb_virtual_network_rule" "test" { + name = "acctestmariadbvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + subnet_id = "${azurerm_subnet.test.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMariaDBVirtualNetworkRule_requiresImport(rInt int, location string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_mariadb_virtual_network_rule" "import" { + name = "${azurerm_mariadb_virtual_network_rule.test.name}" + resource_group_name = "${azurerm_mariadb_virtual_network_rule.test.resource_group_name}" + server_name = "${azurerm_mariadb_virtual_network_rule.test.server_name}" + subnet_id = "${azurerm_mariadb_virtual_network_rule.test.subnet_id}" +} +`, testAccAzureRMMariaDBVirtualNetworkRule_basic(rInt, location)) +} + +func testAccAzureRMMariaDBVirtualNetworkRule_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_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + 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_mariadb_virtual_network_rule" "test" { + name = "acctestmariadbvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + subnet_id = "${azurerm_subnet.test1.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMariaDBVirtualNetworkRule_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_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + 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_mariadb_virtual_network_rule" "test" { + name = "acctestmariadbvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + subnet_id = "${azurerm_subnet.test2.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMariaDBVirtualNetworkRule_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_mariadb_server" "test" { + name = "acctestmariadbsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "10.2" + 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_mariadb_virtual_network_rule" "rule1" { + name = "acctestmariadbvnetrule1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet1.id}" +} + +resource "azurerm_mariadb_virtual_network_rule" "rule2" { + name = "acctestmariadbvnetrule2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet2.id}" +} + +resource "azurerm_mariadb_virtual_network_rule" "rule3" { + name = "acctestmariadbvnetrule3%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mariadb_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_marketplace_agreement.go b/azurerm/resource_arm_marketplace_agreement.go new file mode 100644 index 000000000000..94826d1ec13f --- /dev/null +++ b/azurerm/resource_arm_marketplace_agreement.go @@ -0,0 +1,169 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmMarketplaceAgreement() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMarketplaceAgreementCreateUpdate, + Read: resourceArmMarketplaceAgreementRead, + Delete: resourceArmMarketplaceAgreementDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "offer": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "plan": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "publisher": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "license_text_link": { + Type: schema.TypeString, + Computed: true, + }, + + "privacy_policy_link": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmMarketplaceAgreementCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.MarketplaceAgreementsClient + ctx := meta.(*ArmClient).StopContext + + offer := d.Get("offer").(string) + plan := d.Get("plan").(string) + publisher := d.Get("publisher").(string) + + log.Printf("[DEBUG] Retrieving the Marketplace Terms for Publisher %q / Offer %q / Plan %q", publisher, offer, plan) + + if features.ShouldResourcesBeImported() { + agreement, err := client.Get(ctx, publisher, offer, plan) + if err != nil { + if !utils.ResponseWasNotFound(agreement.Response) { + return fmt.Errorf("Error retrieving agreement for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + } + + accepted := false + if props := agreement.AgreementProperties; props != nil { + if acc := props.Accepted; acc != nil { + accepted = *acc + } + } + + if accepted { + return tf.ImportAsExistsError("azurerm_marketplace_agreement", *agreement.ID) + } + } + + terms, err := client.Get(ctx, publisher, offer, plan) + if err != nil { + return fmt.Errorf("Error retrieving agreement for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + if terms.AgreementProperties == nil { + return fmt.Errorf("Error retrieving agreement for Publisher %q / Offer %q / Plan %q: AgreementProperties was nil", publisher, offer, plan) + } + + terms.AgreementProperties.Accepted = utils.Bool(true) + + log.Printf("[DEBUG] Accepting the Marketplace Terms for Publisher %q / Offer %q / Plan %q", publisher, offer, plan) + if _, err := client.Create(ctx, publisher, offer, plan, terms); err != nil { + return fmt.Errorf("Error accepting Terms for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + log.Printf("[DEBUG] Accepted the Marketplace Terms for Publisher %q / Offer %q / Plan %q", publisher, offer, plan) + + agreement, err := client.GetAgreement(ctx, publisher, offer, plan) + if err != nil { + return fmt.Errorf("Error retrieving agreement for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + + d.SetId(*agreement.ID) + + return resourceArmMarketplaceAgreementRead(d, meta) +} + +func resourceArmMarketplaceAgreementRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.MarketplaceAgreementsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + publisher := id.Path["agreements"] + offer := id.Path["offers"] + plan := id.Path["plans"] + + resp, err := client.Get(ctx, publisher, offer, plan) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Agreement was not found for Publisher %q / Offer %q / Plan %q", publisher, offer, plan) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving agreement for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + + d.Set("publisher", publisher) + d.Set("offer", offer) + d.Set("plan", plan) + + if props := resp.AgreementProperties; props != nil { + d.Set("license_text_link", props.LicenseTextLink) + d.Set("privacy_policy_link", props.PrivacyPolicyLink) + } + + return nil +} + +func resourceArmMarketplaceAgreementDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.MarketplaceAgreementsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + publisher := id.Path["agreements"] + offer := id.Path["offers"] + plan := id.Path["plans"] + + if _, err := client.Cancel(ctx, publisher, offer, plan); err != nil { + return fmt.Errorf("Error cancelling agreement for Publisher %q / Offer %q / Plan %q: %s", publisher, offer, plan, err) + } + + return nil +} diff --git a/azurerm/resource_arm_marketplace_agreement_test.go b/azurerm/resource_arm_marketplace_agreement_test.go new file mode 100644 index 000000000000..405687990637 --- /dev/null +++ b/azurerm/resource_arm_marketplace_agreement_test.go @@ -0,0 +1,157 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMarketplaceAgreement(t *testing.T) { + testCases := map[string]map[string]func(t *testing.T){ + "basic": { + "basic": testAccAzureRMMarketplaceAgreement_basic, + "requiresImport": testAccAzureRMMarketplaceAgreement_requiresImport, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } + }) + } +} + +func testAccAzureRMMarketplaceAgreement_basic(t *testing.T) { + resourceName := "azurerm_marketplace_agreement.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMarketplaceAgreementDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMarketplaceAgreement_basicConfig(), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMarketplaceAgreementExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "license_text_link"), + resource.TestCheckResourceAttrSet(resourceName, "privacy_policy_link"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMMarketplaceAgreement_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_marketplace_agreement.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMarketplaceAgreementDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMarketplaceAgreement_basicConfig(), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMarketplaceAgreementExists(resourceName), + ), + }, + { + Config: testAccAzureRMMarketplaceAgreement_requiresImportConfig(), + ExpectError: testRequiresImportError("azurerm_marketplace_agreement"), + }, + }, + }) +} + +func testCheckAzureRMMarketplaceAgreementExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + offer := rs.Primary.Attributes["offer"] + plan := rs.Primary.Attributes["plan"] + publisher := rs.Primary.Attributes["publisher"] + + client := testAccProvider.Meta().(*ArmClient).compute.MarketplaceAgreementsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, publisher, offer, plan) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Marketplace Agreement for Publisher %q / Offer %q / Plan %q does not exist", publisher, offer, plan) + } + return fmt.Errorf("Bad: Get on MarketplaceAgreementsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMMarketplaceAgreementDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_marketplace_agreement" { + continue + } + + offer := rs.Primary.Attributes["offer"] + plan := rs.Primary.Attributes["plan"] + publisher := rs.Primary.Attributes["publisher"] + + client := testAccProvider.Meta().(*ArmClient).compute.MarketplaceAgreementsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, publisher, offer, plan) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Marketplace Agreement still exists:\n%#v", resp) + } + } + } + + return nil +} + +func testAccAzureRMMarketplaceAgreement_basicConfig() string { + return fmt.Sprintf(` +resource "azurerm_marketplace_agreement" "test" { + publisher = "barracudanetworks" + offer = "waf" + plan = "hourly" +} +`) +} + +func testAccAzureRMMarketplaceAgreement_requiresImportConfig() string { + template := testAccAzureRMMarketplaceAgreement_basicConfig() + return fmt.Sprintf(` +%s + +resource "azurerm_marketplace_agreement" "import" { + publisher = azurerm_marketplace_agreement.test.publisher + offer = azurerm_marketplace_agreement.test.offer + plan = azurerm_marketplace_agreement.test.plan +} +`, template) +} diff --git a/azurerm/resource_arm_media_services_account.go b/azurerm/resource_arm_media_services_account.go index ee4e3b6de458..747d593d1b43 100644 --- a/azurerm/resource_arm_media_services_account.go +++ b/azurerm/resource_arm_media_services_account.go @@ -10,6 +10,7 @@ import ( "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/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -60,7 +61,7 @@ func resourceArmMediaServicesAccount() *schema.Resource { // TODO: support Tags when this bug is fixed: // https://github.com/Azure/azure-rest-api-specs/issues/5249 - //"tags": tagsSchema(), + //"tags": tags.Schema(), }, } } @@ -96,14 +97,14 @@ func resourceArmMediaServicesAccountCreateUpdate(d *schema.ResourceData, meta in } d.SetId(*service.ID) - return nil + return resourceArmMediaServicesAccountRead(d, meta) } func resourceArmMediaServicesAccountRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).media.ServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -135,16 +136,14 @@ func resourceArmMediaServicesAccountRead(d *schema.ResourceData, meta interface{ } } - //flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMediaServicesAccountDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).media.ServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_media_services_account_test.go b/azurerm/resource_arm_media_services_account_test.go index 8791a6e56fb6..7bb8f127c68f 100644 --- a/azurerm/resource_arm_media_services_account_test.go +++ b/azurerm/resource_arm_media_services_account_test.go @@ -86,7 +86,6 @@ func TestAccAzureRMMediaServicesAccount_multiplePrimaries(t *testing.T) { func testCheckAzureRMMediaServicesAccountExists(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 { diff --git a/azurerm/resource_arm_metric_alertrule.go b/azurerm/resource_arm_metric_alertrule.go index 9aebdf4f5851..824a5518d107 100644 --- a/azurerm/resource_arm_metric_alertrule.go +++ b/azurerm/resource_arm_metric_alertrule.go @@ -9,7 +9,11 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -66,7 +70,7 @@ As such the existing 'azurerm_metric_alertrule' resource is deprecated and will "operator": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(insights.ConditionOperatorGreaterThan), string(insights.ConditionOperatorGreaterThanOrEqual), @@ -83,13 +87,13 @@ As such the existing 'azurerm_metric_alertrule' resource is deprecated and will "period": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "aggregation": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(insights.TimeAggregationOperatorAverage), string(insights.TimeAggregationOperatorLast), @@ -153,13 +157,16 @@ As such the existing 'azurerm_metric_alertrule' resource is deprecated and will Optional: true, Computed: true, ValidateFunc: validateMetricAlertRuleTags, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } } func resourceArmMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Alert Rule creation.") @@ -167,7 +174,7 @@ func resourceArmMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interfa name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -181,7 +188,7 @@ func resourceArmMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interfa } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) alertRule, err := expandAzureRmMetricThresholdAlertRule(d) if err != nil { @@ -191,7 +198,7 @@ func resourceArmMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interfa alertRuleResource := insights.AlertRuleResource{ Name: &name, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), AlertRule: alertRule, } @@ -213,7 +220,7 @@ func resourceArmMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmMetricAlertRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext resourceGroup, name, err := resourceGroupAndAlertRuleNameFromId(d.Id()) @@ -247,7 +254,7 @@ func resourceArmMetricAlertRuleRead(d *schema.ResourceData, meta interface{}) er if ruleCondition != nil { if thresholdRuleCondition, ok := ruleCondition.AsThresholdRuleCondition(); ok && thresholdRuleCondition != nil { d.Set("operator", string(thresholdRuleCondition.Operator)) - d.Set("threshold", *thresholdRuleCondition.Threshold) + d.Set("threshold", thresholdRuleCondition.Threshold) d.Set("period", thresholdRuleCondition.WindowSize) d.Set("aggregation", string(thresholdRuleCondition.TimeAggregation)) @@ -304,15 +311,13 @@ func resourceArmMetricAlertRuleRead(d *schema.ResourceData, meta interface{}) er } // Return a new tag map filtered by the specified tag names. - tagMap := filterTags(resp.Tags, "$type") - - flattenAndSetTags(d, tagMap) + tagMap := tags.Filter(resp.Tags, "$type") - return nil + return tags.FlattenAndSet(d, tagMap) } func resourceArmMetricAlertRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext resourceGroup, name, err := resourceGroupAndAlertRuleNameFromId(d.Id()) @@ -433,7 +438,7 @@ func expandAzureRmMetricThresholdAlertRule(d *schema.ResourceData) (*insights.Al func validateMetricAlertRuleTags(v interface{}, f string) (warnings []string, errors []error) { // Normal validation required by any AzureRM resource. - warnings, errors = validateAzureRMTags(v, f) + warnings, errors = tags.Validate(v, f) tagsMap := v.(map[string]interface{}) @@ -447,7 +452,7 @@ func validateMetricAlertRuleTags(v interface{}, f string) (warnings []string, er } func resourceGroupAndAlertRuleNameFromId(alertRuleId string) (string, string, error) { - id, err := parseAzureResourceID(alertRuleId) + id, err := azure.ParseAzureResourceID(alertRuleId) if err != nil { return "", "", err } diff --git a/azurerm/resource_arm_metric_alertrule_test.go b/azurerm/resource_arm_metric_alertrule_test.go index 1fece9470fc9..c37b57aa0077 100644 --- a/azurerm/resource_arm_metric_alertrule_test.go +++ b/azurerm/resource_arm_metric_alertrule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -105,7 +106,7 @@ func TestAccAzureRMMetricAlertRule_virtualMachineCpu(t *testing.T) { } func TestAccAzureRMMetricAlertRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -113,7 +114,6 @@ func TestAccAzureRMMetricAlertRule_requiresImport(t *testing.T) { resourceName := "azurerm_metric_alertrule.test" ri := tf.AccRandTimeInt() location := testLocation() - enabled := true resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -121,13 +121,13 @@ func TestAccAzureRMMetricAlertRule_requiresImport(t *testing.T) { CheckDestroy: testCheckAzureRMMetricAlertRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMetricAlertRule_virtualMachineCpu(ri, location, enabled), + Config: testAccAzureRMMetricAlertRule_virtualMachineCpu(ri, location, true), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMetricAlertRuleExists(resourceName), ), }, { - Config: testAccAzureRMMetricAlertRule_requiresImport(ri, location, enabled), + Config: testAccAzureRMMetricAlertRule_requiresImport(ri, location, true), ExpectError: testRequiresImportError("azurerm_metric_alertrule"), }, }, @@ -169,7 +169,7 @@ func testCheckAzureRMMetricAlertRuleExists(resourceName string) resource.TestChe return fmt.Errorf("Bad: no resource group found in state for Alert Rule: %s", name) } - client := testAccProvider.Meta().(*ArmClient).monitorAlertRulesClient + client := testAccProvider.Meta().(*ArmClient).monitor.AlertRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -186,7 +186,7 @@ func testCheckAzureRMMetricAlertRuleExists(resourceName string) resource.TestChe } func testCheckAzureRMMetricAlertRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).monitorAlertRulesClient + client := testAccProvider.Meta().(*ArmClient).monitor.AlertRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_monitor_action_group.go b/azurerm/resource_arm_monitor_action_group.go index 9574dd344c77..6b9fae589fc5 100644 --- a/azurerm/resource_arm_monitor_action_group.go +++ b/azurerm/resource_arm_monitor_action_group.go @@ -10,6 +10,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -107,19 +109,19 @@ func resourceArmMonitorActionGroup() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmMonitorActionGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActionGroupsClient + client := meta.(*ArmClient).monitor.ActionGroupsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -139,8 +141,8 @@ func resourceArmMonitorActionGroupCreateUpdate(d *schema.ResourceData, meta inte smsReceiversRaw := d.Get("sms_receiver").([]interface{}) webhookReceiversRaw := d.Get("webhook_receiver").([]interface{}) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := insights.ActionGroupResource{ Location: utils.String(azure.NormalizeLocation("Global")), @@ -172,10 +174,10 @@ func resourceArmMonitorActionGroupCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmMonitorActionGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActionGroupsClient + client := meta.(*ArmClient).monitor.ActionGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -211,16 +213,14 @@ func resourceArmMonitorActionGroupRead(d *schema.ResourceData, meta interface{}) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMonitorActionGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActionGroupsClient + client := meta.(*ArmClient).monitor.ActionGroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_monitor_action_group_test.go b/azurerm/resource_arm_monitor_action_group_test.go index 7308b8f43452..0fca63fd8d9e 100644 --- a/azurerm/resource_arm_monitor_action_group_test.go +++ b/azurerm/resource_arm_monitor_action_group_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMMonitorActionGroup_basic(t *testing.T) { @@ -40,7 +41,7 @@ func TestAccAzureRMMonitorActionGroup_basic(t *testing.T) { } func TestAccAzureRMMonitorActionGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -491,7 +492,7 @@ resource "azurerm_monitor_action_group" "test" { } func testCheckAzureRMMonitorActionGroupDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).monitorActionGroupsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.ActionGroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -530,7 +531,7 @@ func testCheckAzureRMMonitorActionGroupExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Action Group Instance: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).monitorActionGroupsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.ActionGroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_monitor_activity_log_alert.go b/azurerm/resource_arm_monitor_activity_log_alert.go index c8a62b8980ec..36bce65dfcfa 100644 --- a/azurerm/resource_arm_monitor_activity_log_alert.go +++ b/azurerm/resource_arm_monitor_activity_log_alert.go @@ -14,6 +14,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -148,19 +150,19 @@ func resourceArmMonitorActivityLogAlert() *schema.Resource { Default: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmMonitorActivityLogAlertCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActivityLogAlertsClient + client := meta.(*ArmClient).monitor.ActivityLogAlertsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -179,8 +181,8 @@ func resourceArmMonitorActivityLogAlertCreateUpdate(d *schema.ResourceData, meta criteriaRaw := d.Get("criteria").([]interface{}) actionRaw := d.Get("action").(*schema.Set).List() - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := insights.ActivityLogAlertResource{ Location: utils.String(azure.NormalizeLocation("Global")), @@ -211,10 +213,10 @@ func resourceArmMonitorActivityLogAlertCreateUpdate(d *schema.ResourceData, meta } func resourceArmMonitorActivityLogAlertRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActivityLogAlertsClient + client := meta.(*ArmClient).monitor.ActivityLogAlertsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -246,16 +248,14 @@ func resourceArmMonitorActivityLogAlertRead(d *schema.ResourceData, meta interfa return fmt.Errorf("Error setting `action`: %+v", err) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMonitorActivityLogAlertDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorActivityLogAlertsClient + client := meta.(*ArmClient).monitor.ActivityLogAlertsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_monitor_activity_log_alert_test.go b/azurerm/resource_arm_monitor_activity_log_alert_test.go index df378c8b1a92..3063c4eda561 100644 --- a/azurerm/resource_arm_monitor_activity_log_alert_test.go +++ b/azurerm/resource_arm_monitor_activity_log_alert_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMMonitorActivityLogAlert_basic(t *testing.T) { @@ -43,7 +44,7 @@ func TestAccAzureRMMonitorActivityLogAlert_basic(t *testing.T) { } func TestAccAzureRMMonitorActivityLogAlert_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -355,7 +356,7 @@ resource "azurerm_monitor_activity_log_alert" "test" { } func testCheckAzureRMMonitorActivityLogAlertDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).monitorActivityLogAlertsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.ActivityLogAlertsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -394,7 +395,7 @@ func testCheckAzureRMMonitorActivityLogAlertExists(resourceName string) resource return fmt.Errorf("Bad: no resource group found in state for Activity Log Alert Instance: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).monitorActivityLogAlertsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.ActivityLogAlertsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_monitor_autoscale_setting.go b/azurerm/resource_arm_monitor_autoscale_setting.go index b6478a1d0bec..d6b461a76577 100644 --- a/azurerm/resource_arm_monitor_autoscale_setting.go +++ b/azurerm/resource_arm_monitor_autoscale_setting.go @@ -12,8 +12,11 @@ import ( "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -112,7 +115,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { "time_grain": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "statistic": { Type: schema.TypeString, @@ -123,12 +126,12 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { string(insights.MetricStatisticTypeMin), string(insights.MetricStatisticTypeSum), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "time_window": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "time_aggregation": { Type: schema.TypeString, @@ -140,7 +143,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { string(insights.TimeAggregationTypeMinimum), string(insights.TimeAggregationTypeTotal), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "operator": { Type: schema.TypeString, @@ -153,7 +156,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { string(insights.LessThanOrEqual), string(insights.NotEquals), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "threshold": { Type: schema.TypeFloat, @@ -175,7 +178,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { string(insights.ScaleDirectionDecrease), string(insights.ScaleDirectionIncrease), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "type": { Type: schema.TypeString, @@ -185,7 +188,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { string(insights.ExactCount), string(insights.PercentChangeCount), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "value": { Type: schema.TypeInt, @@ -195,7 +198,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { "cooldown": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, }, }, @@ -218,12 +221,12 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { "start": { Type: schema.TypeString, Required: true, - ValidateFunc: validateRFC3339Date, + ValidateFunc: validate.RFC3339Time, }, "end": { Type: schema.TypeString, Required: true, - ValidateFunc: validateRFC3339Date, + ValidateFunc: validate.RFC3339Time, }, }, }, @@ -254,7 +257,7 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { "Saturday", "Sunday", }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, "hours": { @@ -327,6 +330,9 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { "properties": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, }, @@ -335,19 +341,19 @@ func resourceArmMonitorAutoScaleSetting() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmMonitorAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -373,8 +379,8 @@ func resourceArmMonitorAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta return fmt.Errorf("Error expanding `profile`: %+v", err) } - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := insights.AutoscaleSettingResource{ Location: utils.String(location), @@ -405,10 +411,10 @@ func resourceArmMonitorAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta } func resourceArmMonitorAutoScaleSettingRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -449,17 +455,15 @@ func resourceArmMonitorAutoScaleSettingRead(d *schema.ResourceData, meta interfa } // Return a new tag map filtered by the specified tag names. - tagMap := filterTags(resp.Tags, "$type") - flattenAndSetTags(d, tagMap) - - return nil + tagMap := tags.Filter(resp.Tags, "$type") + return tags.FlattenAndSet(d, tagMap) } func resourceArmMonitorAutoScaleSettingDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).autoscaleSettingsClient + client := meta.(*ArmClient).monitor.AutoscaleSettingsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -858,7 +862,6 @@ func flattenAzureRmMonitorAutoScaleSettingRecurrence(input *insights.Recurrence) result := make(map[string]interface{}) if schedule := input.Schedule; schedule != nil { - if timezone := schedule.TimeZone; timezone != nil { result["timezone"] = *timezone } diff --git a/azurerm/resource_arm_monitor_autoscale_setting_test.go b/azurerm/resource_arm_monitor_autoscale_setting_test.go index a827e088e604..8c3e0c64de78 100644 --- a/azurerm/resource_arm_monitor_autoscale_setting_test.go +++ b/azurerm/resource_arm_monitor_autoscale_setting_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMMonitorAutoScaleSetting_basic(t *testing.T) { @@ -43,7 +44,7 @@ func TestAccAzureRMMonitorAutoScaleSetting_basic(t *testing.T) { } func TestAccAzureRMMonitorAutoScaleSetting_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -332,7 +333,7 @@ func testCheckAzureRMMonitorAutoScaleSettingExists(resourceName string) resource return fmt.Errorf("Bad: no resource group found in state for Monitor AutoScale Setting: %s", autoscaleSettingName) } - conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.AutoscaleSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, autoscaleSettingName) @@ -349,7 +350,7 @@ func testCheckAzureRMMonitorAutoScaleSettingExists(resourceName string) resource } func testCheckAzureRMMonitorAutoScaleSettingDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.AutoscaleSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_monitor_diagnostic_setting.go b/azurerm/resource_arm_monitor_diagnostic_setting.go index ba1a98812452..143d182bfb3b 100644 --- a/azurerm/resource_arm_monitor_diagnostic_setting.go +++ b/azurerm/resource_arm_monitor_diagnostic_setting.go @@ -15,6 +15,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -73,6 +74,13 @@ func resourceArmMonitorDiagnosticSetting() *schema.Resource { ValidateFunc: azure.ValidateResourceID, }, + "log_analytics_destination_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: false, + ValidateFunc: validation.StringInSlice([]string{"Dedicated"}, false), + }, + "log": { Type: schema.TypeSet, Optional: true, @@ -155,14 +163,14 @@ func resourceArmMonitorDiagnosticSetting() *schema.Resource { } func resourceArmMonitorDiagnosticSettingCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorDiagnosticSettingsClient + client := meta.(*ArmClient).monitor.DiagnosticSettingsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ARM Diagnostic Settings.") name := d.Get("name").(string) actualResourceId := d.Get("target_resource_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, actualResourceId, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -234,6 +242,14 @@ func resourceArmMonitorDiagnosticSettingCreateUpdate(d *schema.ResourceData, met valid = true } + if v := d.Get("log_analytics_destination_type").(string); v != "" { + if workspaceId != "" { + properties.DiagnosticSettings.LogAnalyticsDestinationType = &v + } else { + return fmt.Errorf("`log_analytics_workspace_id` must be set for `log_analytics_destination_type` to be used") + } + } + if !valid { return fmt.Errorf("Either a `eventhub_authorization_rule_id`, `log_analytics_workspace_id` or `storage_account_id` must be set") } @@ -258,7 +274,7 @@ func resourceArmMonitorDiagnosticSettingCreateUpdate(d *schema.ResourceData, met } func resourceArmMonitorDiagnosticSettingRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorDiagnosticSettingsClient + client := meta.(*ArmClient).monitor.DiagnosticSettingsClient ctx := meta.(*ArmClient).StopContext id, err := parseMonitorDiagnosticId(d.Id()) @@ -287,6 +303,8 @@ func resourceArmMonitorDiagnosticSettingRead(d *schema.ResourceData, meta interf d.Set("log_analytics_workspace_id", resp.WorkspaceID) d.Set("storage_account_id", resp.StorageAccountID) + d.Set("log_analytics_destination_type", resp.LogAnalyticsDestinationType) + if err := d.Set("log", flattenMonitorDiagnosticLogs(resp.Logs)); err != nil { return fmt.Errorf("Error setting `log`: %+v", err) } @@ -299,7 +317,7 @@ func resourceArmMonitorDiagnosticSettingRead(d *schema.ResourceData, meta interf } func resourceArmMonitorDiagnosticSettingDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorDiagnosticSettingsClient + client := meta.(*ArmClient).monitor.DiagnosticSettingsClient ctx := meta.(*ArmClient).StopContext id, err := parseMonitorDiagnosticId(d.Id()) @@ -332,7 +350,7 @@ func resourceArmMonitorDiagnosticSettingDelete(d *schema.ResourceData, meta inte return nil } -func monitorDiagnosticSettingDeletedRefreshFunc(ctx context.Context, client insights.DiagnosticSettingsClient, targetResourceId string, name string) resource.StateRefreshFunc { +func monitorDiagnosticSettingDeletedRefreshFunc(ctx context.Context, client *insights.DiagnosticSettingsClient, targetResourceId string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, targetResourceId, name) if err != nil { diff --git a/azurerm/resource_arm_monitor_diagnostic_setting_test.go b/azurerm/resource_arm_monitor_diagnostic_setting_test.go index 33a694fc8d46..92092d29153b 100644 --- a/azurerm/resource_arm_monitor_diagnostic_setting_test.go +++ b/azurerm/resource_arm_monitor_diagnostic_setting_test.go @@ -8,6 +8,7 @@ import ( "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/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,7 +44,7 @@ func TestAccAzureRMMonitorDiagnosticSetting_eventhub(t *testing.T) { } func TestAccAzureRMMonitorDiagnosticSetting_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -101,6 +102,39 @@ func TestAccAzureRMMonitorDiagnosticSetting_logAnalyticsWorkspace(t *testing.T) }) } +func TestAccAzureRMMonitorDiagnosticSetting_logAnalyticsWorkspaceDedicated(t *testing.T) { + resourceName := "azurerm_monitor_diagnostic_setting.test" + ri := acctest.RandIntRange(10000, 99999) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorDiagnosticSettingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorDiagnosticSetting_logAnalyticsWorkspaceDedicated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorDiagnosticSettingExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "log_analytics_workspace_id"), + resource.TestCheckResourceAttr(resourceName, "log_analytics_destination_type", "Dedicated"), + resource.TestCheckResourceAttr(resourceName, "log.#", "3"), + resource.TestCheckResourceAttr(resourceName, "log.3188484811.category", "ActivityRuns"), + resource.TestCheckResourceAttr(resourceName, "log.595859111.category", "PipelineRuns"), + resource.TestCheckResourceAttr(resourceName, "log.2542277390.category", "TriggerRuns"), + resource.TestCheckResourceAttr(resourceName, "metric.#", "1"), + resource.TestCheckResourceAttr(resourceName, "metric.4109484471.category", "AllMetrics"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMMonitorDiagnosticSetting_storageAccount(t *testing.T) { resourceName := "azurerm_monitor_diagnostic_setting.test" ri := acctest.RandIntRange(10000, 99999) @@ -139,7 +173,7 @@ func testCheckAzureRMMonitorDiagnosticSettingExists(resourceName string) resourc return fmt.Errorf("Not found: %q", resourceName) } - client := testAccProvider.Meta().(*ArmClient).monitorDiagnosticSettingsClient + client := testAccProvider.Meta().(*ArmClient).monitor.DiagnosticSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext name := rs.Primary.Attributes["name"] actualResourceId := rs.Primary.Attributes["target_resource_id"] @@ -159,7 +193,7 @@ func testCheckAzureRMMonitorDiagnosticSettingExists(resourceName string) resourc } func testCheckAzureRMMonitorDiagnosticSettingDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).monitorDiagnosticSettingsClient + client := testAccProvider.Meta().(*ArmClient).monitor.DiagnosticSettingsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -338,6 +372,74 @@ resource "azurerm_monitor_diagnostic_setting" "test" { `, rInt, location, rInt, rInt, rInt) } +func testAccAzureRMMonitorDiagnosticSetting_logAnalyticsWorkspaceDedicated(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctest%d" + location = "%s" +} + +resource "azurerm_log_analytics_workspace" "test" { + name = "acctestlaw%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "PerGB2018" + retention_in_days = 30 +} + +resource "azurerm_data_factory" "test" { + name = "acctestdf%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "acctestds%d" + target_resource_id = "${azurerm_data_factory.test.id}" + log_analytics_workspace_id = "${azurerm_log_analytics_workspace.test.id}" + + log_analytics_destination_type = "Dedicated" + + log { + category = "ActivityRuns" + + retention_policy { + enabled = false + } + } + + log { + category = "PipelineRuns" + enabled = false + + retention_policy { + enabled = false + } + } + + log { + category = "TriggerRuns" + enabled = false + + retention_policy { + enabled = false + } + } + + metric { + category = "AllMetrics" + enabled = false + + retention_policy { + enabled = false + } + } +} +`, rInt, location, rInt, rInt, rInt) +} + func testAccAzureRMMonitorDiagnosticSetting_storageAccount(rInt int, location string) string { return fmt.Sprintf(` data "azurerm_client_config" "current" {} diff --git a/azurerm/resource_arm_monitor_log_profile.go b/azurerm/resource_arm_monitor_log_profile.go index c1ff5ec7e26e..46bbaa62f576 100644 --- a/azurerm/resource_arm_monitor_log_profile.go +++ b/azurerm/resource_arm_monitor_log_profile.go @@ -10,8 +10,10 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -59,7 +61,7 @@ func resourceArmMonitorLogProfile() *schema.Resource { MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, Set: schema.HashString, }, @@ -86,11 +88,11 @@ func resourceArmMonitorLogProfile() *schema.Resource { } func resourceArmLogProfileCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorLogProfilesClient + client := meta.(*ArmClient).monitor.LogProfilesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -148,7 +150,7 @@ func resourceArmLogProfileCreateUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmLogProfileRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorLogProfilesClient + client := meta.(*ArmClient).monitor.LogProfilesClient ctx := meta.(*ArmClient).StopContext name, err := parseLogProfileNameFromID(d.Id()) @@ -185,7 +187,7 @@ func resourceArmLogProfileRead(d *schema.ResourceData, meta interface{}) error { } func resourceArmLogProfileDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorLogProfilesClient + client := meta.(*ArmClient).monitor.LogProfilesClient ctx := meta.(*ArmClient).StopContext name, err := parseLogProfileNameFromID(d.Id()) @@ -267,7 +269,7 @@ func flattenAzureRmLogProfileRetentionPolicy(input *insights.RetentionPolicy) [] func retryLogProfilesClientGet(name string, meta interface{}) func() *resource.RetryError { return func() *resource.RetryError { - client := meta.(*ArmClient).monitorLogProfilesClient + client := meta.(*ArmClient).monitor.LogProfilesClient ctx := meta.(*ArmClient).StopContext if _, err := client.Get(ctx, name); err != nil { diff --git a/azurerm/resource_arm_monitor_log_profile_test.go b/azurerm/resource_arm_monitor_log_profile_test.go index 62ded7e47a83..64a3bef915d9 100644 --- a/azurerm/resource_arm_monitor_log_profile_test.go +++ b/azurerm/resource_arm_monitor_log_profile_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -72,7 +73,7 @@ func testAccAzureRMMonitorLogProfile_basic(t *testing.T) { } func testAccAzureRMMonitorLogProfile_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -165,7 +166,7 @@ func testAccAzureRMMonitorLogProfile_disappears(t *testing.T) { } func testCheckAzureRMLogProfileDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).monitorLogProfilesClient + client := testAccProvider.Meta().(*ArmClient).monitor.LogProfilesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -193,7 +194,7 @@ func testCheckAzureRMLogProfileExists(resourceName string) resource.TestCheckFun return fmt.Errorf("Not found: %s", resourceName) } - client := testAccProvider.Meta().(*ArmClient).monitorLogProfilesClient + client := testAccProvider.Meta().(*ArmClient).monitor.LogProfilesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext name := rs.Primary.Attributes["name"] @@ -220,7 +221,7 @@ func testCheckAzureRMLogProfileDisappears(resourceName string) resource.TestChec name := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).monitorLogProfilesClient + client := testAccProvider.Meta().(*ArmClient).monitor.LogProfilesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, name); err != nil { diff --git a/azurerm/resource_arm_monitor_metric_alert.go b/azurerm/resource_arm_monitor_metric_alert.go index 523cfd483d3a..a7ec6d26e9e1 100644 --- a/azurerm/resource_arm_monitor_metric_alert.go +++ b/azurerm/resource_arm_monitor_metric_alert.go @@ -13,6 +13,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,6 +42,8 @@ func resourceArmMonitorMetricAlert() *schema.Resource { // TODO: Multiple resource IDs (Remove MaxItems) support is missing in SDK // Issue to track: https://github.com/Azure/azure-sdk-for-go/issues/2920 // But to prevent potential state migration in the future, let's stick to use Set now + + //lintignore:S018 "scopes": { Type: schema.TypeSet, Required: true, @@ -203,19 +207,19 @@ func resourceArmMonitorMetricAlert() *schema.Resource { }, false), }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorMetricAlertsClient + client := meta.(*ArmClient).monitor.MetricAlertsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -238,8 +242,8 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte criteriaRaw := d.Get("criteria").([]interface{}) actionRaw := d.Get("action").(*schema.Set).List() - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := insights.MetricAlertResource{ Location: utils.String(azure.NormalizeLocation("Global")), @@ -274,10 +278,10 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorMetricAlertsClient + client := meta.(*ArmClient).monitor.MetricAlertsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -313,16 +317,14 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error setting `action`: %+v", err) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMonitorMetricAlertDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorMetricAlertsClient + client := meta.(*ArmClient).monitor.MetricAlertsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_monitor_metric_alert_test.go b/azurerm/resource_arm_monitor_metric_alert_test.go index 30d89e47aa63..42b387dbef8f 100644 --- a/azurerm/resource_arm_monitor_metric_alert_test.go +++ b/azurerm/resource_arm_monitor_metric_alert_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMMonitorMetricAlert_basic(t *testing.T) { @@ -48,7 +49,7 @@ func TestAccAzureRMMonitorMetricAlert_basic(t *testing.T) { } func TestAccAzureRMMonitorMetricAlert_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -356,7 +357,7 @@ resource "azurerm_monitor_metric_alert" "test" { } func testCheckAzureRMMonitorMetricAlertDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).monitorMetricAlertsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.MetricAlertsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_monitor_metric_alert" { @@ -391,7 +392,7 @@ func testCheckAzureRMMonitorMetricAlertExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Metric Alert Instance: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).monitorMetricAlertsClient + conn := testAccProvider.Meta().(*ArmClient).monitor.MetricAlertsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_monitor_metric_alertrule.go b/azurerm/resource_arm_monitor_metric_alertrule.go index 4db13f24ee3a..09a0ea0f9802 100644 --- a/azurerm/resource_arm_monitor_metric_alertrule.go +++ b/azurerm/resource_arm_monitor_metric_alertrule.go @@ -9,7 +9,11 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -59,7 +63,7 @@ func resourceArmMonitorMetricAlertRule() *schema.Resource { "operator": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(insights.ConditionOperatorGreaterThan), string(insights.ConditionOperatorGreaterThanOrEqual), @@ -76,13 +80,13 @@ func resourceArmMonitorMetricAlertRule() *schema.Resource { "period": { Type: schema.TypeString, Required: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "aggregation": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(insights.TimeAggregationOperatorAverage), string(insights.TimeAggregationOperatorLast), @@ -146,13 +150,16 @@ func resourceArmMonitorMetricAlertRule() *schema.Resource { Optional: true, Computed: true, ValidateFunc: validateMetricAlertRuleTags, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, } } func resourceArmMonitorMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Alert Rule creation.") @@ -160,7 +167,7 @@ func resourceArmMonitorMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -174,7 +181,7 @@ func resourceArmMonitorMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) alertRule, err := expandAzureRmMonitorMetricThresholdAlertRule(d) if err != nil { @@ -184,7 +191,7 @@ func resourceArmMonitorMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta alertRuleResource := insights.AlertRuleResource{ Name: &name, Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), AlertRule: alertRule, } @@ -206,10 +213,10 @@ func resourceArmMonitorMetricAlertRuleCreateUpdate(d *schema.ResourceData, meta } func resourceArmMonitorMetricAlertRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -242,7 +249,7 @@ func resourceArmMonitorMetricAlertRuleRead(d *schema.ResourceData, meta interfac if ruleCondition != nil { if thresholdRuleCondition, ok := ruleCondition.AsThresholdRuleCondition(); ok && thresholdRuleCondition != nil { d.Set("operator", string(thresholdRuleCondition.Operator)) - d.Set("threshold", *thresholdRuleCondition.Threshold) + d.Set("threshold", thresholdRuleCondition.Threshold) d.Set("period", thresholdRuleCondition.WindowSize) d.Set("aggregation", string(thresholdRuleCondition.TimeAggregation)) @@ -299,18 +306,16 @@ func resourceArmMonitorMetricAlertRuleRead(d *schema.ResourceData, meta interfac } // Return a new tag map filtered by the specified tag names. - tagMap := filterTags(resp.Tags, "$type") - - flattenAndSetTags(d, tagMap) + tagMap := tags.Filter(resp.Tags, "$type") - return nil + return tags.FlattenAndSet(d, tagMap) } func resourceArmMonitorMetricAlertRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).monitorAlertRulesClient + client := meta.(*ArmClient).monitor.AlertRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -430,7 +435,7 @@ func expandAzureRmMonitorMetricThresholdAlertRule(d *schema.ResourceData) (*insi func validateMonitorMetricAlertRuleTags(v interface{}, f string) (warnings []string, errors []error) { // Normal validation required by any AzureRM resource. - warnings, errors = validateAzureRMTags(v, f) + warnings, errors = tags.Validate(v, f) tagsMap := v.(map[string]interface{}) diff --git a/azurerm/resource_arm_monitor_metric_alertrule_test.go b/azurerm/resource_arm_monitor_metric_alertrule_test.go index b0feadac65f6..8b0151c6251d 100644 --- a/azurerm/resource_arm_monitor_metric_alertrule_test.go +++ b/azurerm/resource_arm_monitor_metric_alertrule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -105,7 +106,7 @@ func TestAccAzureRMMonitorMetricAlertRule_virtualMachineCpu(t *testing.T) { } func TestAccAzureRMMonitorMetricAlertRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -113,7 +114,6 @@ func TestAccAzureRMMonitorMetricAlertRule_requiresImport(t *testing.T) { resourceName := "azurerm_monitor_metric_alertrule.test" ri := tf.AccRandTimeInt() location := testLocation() - enabled := true resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -121,13 +121,13 @@ func TestAccAzureRMMonitorMetricAlertRule_requiresImport(t *testing.T) { CheckDestroy: testCheckAzureRMMonitorMetricAlertRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMonitorMetricAlertRule_virtualMachineCpu(ri, location, enabled), + Config: testAccAzureRMMonitorMetricAlertRule_virtualMachineCpu(ri, location, true), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertRuleExists(resourceName), ), }, { - Config: testAccAzureRMMonitorMetricAlertRule_requiresImport(ri, location, enabled), + Config: testAccAzureRMMonitorMetricAlertRule_requiresImport(ri, location, true), ExpectError: testRequiresImportError("azurerm_monitor_metric_alertrule"), }, }, @@ -169,7 +169,7 @@ func testCheckAzureRMMonitorMetricAlertRuleExists(resourceName string) resource. return fmt.Errorf("Bad: no resource group found in state for Alert Rule: %s", name) } - client := testAccProvider.Meta().(*ArmClient).monitorAlertRulesClient + client := testAccProvider.Meta().(*ArmClient).monitor.AlertRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -186,7 +186,7 @@ func testCheckAzureRMMonitorMetricAlertRuleExists(resourceName string) resource. } func testCheckAzureRMMonitorMetricAlertRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).monitorAlertRulesClient + client := testAccProvider.Meta().(*ArmClient).monitor.AlertRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_mssql_elasticpool.go b/azurerm/resource_arm_mssql_elasticpool.go index 9ac51bca01de..0364fc061d1c 100644 --- a/azurerm/resource_arm_mssql_elasticpool.go +++ b/azurerm/resource_arm_mssql_elasticpool.go @@ -12,6 +12,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -179,11 +181,10 @@ func resourceArmMsSqlElasticPool() *schema.Resource { Optional: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - if err := azure.MSSQLElasticPoolValidateSKU(diff); err != nil { return err } @@ -194,7 +195,7 @@ func resourceArmMsSqlElasticPool() *schema.Resource { } func resourceArmMsSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).msSqlElasticPoolsClient + client := meta.(*ArmClient).mssql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for MSSQL ElasticPool creation.") @@ -203,7 +204,7 @@ func resourceArmMsSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interf serverName := d.Get("server_name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, serverName, elasticPoolName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -218,13 +219,13 @@ func resourceArmMsSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interf location := azure.NormalizeLocation(d.Get("location").(string)) sku := expandAzureRmMsSqlElasticPoolSku(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) elasticPool := sql.ElasticPool{ Name: &elasticPoolName, Location: &location, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), ElasticPoolProperties: &sql.ElasticPoolProperties{ PerDatabaseSettings: expandAzureRmMsSqlElasticPoolPerDatabaseSettings(d), }, @@ -268,7 +269,7 @@ func resourceArmMsSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interf } func resourceArmMsSqlElasticPoolRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).msSqlElasticPoolsClient + client := meta.(*ArmClient).mssql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext resGroup, serverName, name, err := parseArmMsSqlElasticPoolId(d.Id()) @@ -319,13 +320,11 @@ func resourceArmMsSqlElasticPoolRead(d *schema.ResourceData, meta interface{}) e } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMsSqlElasticPoolDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).msSqlElasticPoolsClient + client := meta.(*ArmClient).mssql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext resGroup, serverName, name, err := parseArmSqlElasticPoolId(d.Id()) @@ -338,7 +337,7 @@ func resourceArmMsSqlElasticPoolDelete(d *schema.ResourceData, meta interface{}) } func parseArmMsSqlElasticPoolId(sqlElasticPoolId string) (string, string, string, error) { - id, err := parseAzureResourceID(sqlElasticPoolId) + id, err := azure.ParseAzureResourceID(sqlElasticPoolId) if err != nil { return "", "", "", fmt.Errorf("[ERROR] Unable to parse MsSQL ElasticPool ID %q: %+v", sqlElasticPoolId, err) } diff --git a/azurerm/resource_arm_mssql_elasticpool_test.go b/azurerm/resource_arm_mssql_elasticpool_test.go index d080b00149cd..5b34e79b40eb 100644 --- a/azurerm/resource_arm_mssql_elasticpool_test.go +++ b/azurerm/resource_arm_mssql_elasticpool_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) // TODO: add import tests @@ -38,7 +39,7 @@ func TestAccAzureRMMsSqlElasticPool_basic_DTU(t *testing.T) { } func TestAccAzureRMMsSqlElasticPool_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -299,7 +300,7 @@ func testCheckAzureRMMsSqlElasticPoolExists(resourceName string) resource.TestCh serverName := rs.Primary.Attributes["server_name"] poolName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).msSqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).mssql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, poolName) @@ -316,7 +317,7 @@ func testCheckAzureRMMsSqlElasticPoolExists(resourceName string) resource.TestCh } func testCheckAzureRMMsSqlElasticPoolDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).msSqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).mssql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -353,7 +354,7 @@ func testCheckAzureRMMsSqlElasticPoolDisappears(resourceName string) resource.Te serverName := rs.Primary.Attributes["server_name"] poolName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).msSqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).mssql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, resourceGroup, serverName, poolName); err != nil { diff --git a/azurerm/resource_arm_mysql_configuration.go b/azurerm/resource_arm_mysql_configuration.go index 179018b70867..7a9f6b2d1567 100644 --- a/azurerm/resource_arm_mysql_configuration.go +++ b/azurerm/resource_arm_mysql_configuration.go @@ -15,6 +15,7 @@ func resourceArmMySQLConfiguration() *schema.Resource { Create: resourceArmMySQLConfigurationCreate, Read: resourceArmMySQLConfigurationRead, Delete: resourceArmMySQLConfigurationDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -29,9 +30,10 @@ func resourceArmMySQLConfiguration() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "server_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateMySqlServerName, }, "value": { @@ -44,7 +46,7 @@ func resourceArmMySQLConfiguration() *schema.Resource { } func resourceArmMySQLConfigurationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlConfigurationsClient + client := meta.(*ArmClient).mysql.ConfigurationsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MySQL Configuration creation.") @@ -83,10 +85,10 @@ func resourceArmMySQLConfigurationCreate(d *schema.ResourceData, meta interface{ } func resourceArmMySQLConfigurationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlConfigurationsClient + client := meta.(*ArmClient).mysql.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -114,10 +116,10 @@ func resourceArmMySQLConfigurationRead(d *schema.ResourceData, meta interface{}) } func resourceArmMySQLConfigurationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlConfigurationsClient + client := meta.(*ArmClient).mysql.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_mysql_configuration_test.go b/azurerm/resource_arm_mysql_configuration_test.go index 5f3b96e15921..52afe5bef5d8 100644 --- a/azurerm/resource_arm_mysql_configuration_test.go +++ b/azurerm/resource_arm_mysql_configuration_test.go @@ -118,7 +118,7 @@ func testCheckAzureRMMySQLConfigurationValue(resourceName string, value string) return fmt.Errorf("Bad: no resource group found in state for MySQL Configuration: %s", name) } - client := testAccProvider.Meta().(*ArmClient).mysqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).mysql.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -140,11 +140,10 @@ func testCheckAzureRMMySQLConfigurationValue(resourceName string, value string) func testCheckAzureRMMySQLConfigurationValueReset(rInt int, configurationName string) resource.TestCheckFunc { return func(s *terraform.State) error { - resourceGroup := fmt.Sprintf("acctestRG-%d", rInt) serverName := fmt.Sprintf("acctestmysqlsvr-%d", rInt) - client := testAccProvider.Meta().(*ArmClient).mysqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).mysql.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, configurationName) @@ -167,7 +166,7 @@ func testCheckAzureRMMySQLConfigurationValueReset(rInt int, configurationName st } func testCheckAzureRMMySQLConfigurationDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mysqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).mysql.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_mysql_database.go b/azurerm/resource_arm_mysql_database.go index 031489499a11..c117d4aac7ef 100644 --- a/azurerm/resource_arm_mysql_database.go +++ b/azurerm/resource_arm_mysql_database.go @@ -7,7 +7,9 @@ import ( "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" "github.com/hashicorp/terraform/helper/schema" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -16,6 +18,7 @@ func resourceArmMySqlDatabase() *schema.Resource { Create: resourceArmMySqlDatabaseCreate, Read: resourceArmMySqlDatabaseRead, Delete: resourceArmMySqlDatabaseDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -30,15 +33,16 @@ func resourceArmMySqlDatabase() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "server_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateMySqlServerName, }, "charset": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ForceNew: true, }, @@ -52,7 +56,7 @@ func resourceArmMySqlDatabase() *schema.Resource { } func resourceArmMySqlDatabaseCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlDatabasesClient + client := meta.(*ArmClient).mysql.DatabasesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MySQL Database creation.") @@ -64,7 +68,7 @@ func resourceArmMySqlDatabaseCreate(d *schema.ResourceData, meta interface{}) er charset := d.Get("charset").(string) collation := d.Get("collation").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -107,10 +111,10 @@ func resourceArmMySqlDatabaseCreate(d *schema.ResourceData, meta interface{}) er } func resourceArmMySqlDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlDatabasesClient + client := meta.(*ArmClient).mysql.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -138,10 +142,10 @@ func resourceArmMySqlDatabaseRead(d *schema.ResourceData, meta interface{}) erro } func resourceArmMySqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlDatabasesClient + client := meta.(*ArmClient).mysql.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_mysql_database_test.go b/azurerm/resource_arm_mysql_database_test.go index 920e89c8d599..9aabbb60e56e 100644 --- a/azurerm/resource_arm_mysql_database_test.go +++ b/azurerm/resource_arm_mysql_database_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMMySQLDatabase_basic(t *testing.T) { } func TestAccAzureRMMySQLDatabase_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -129,7 +130,7 @@ func testCheckAzureRMMySQLDatabaseExists(resourceName string) resource.TestCheck return fmt.Errorf("Bad: no resource group found in state for MySQL Database: %s", name) } - client := testAccProvider.Meta().(*ArmClient).mysqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).mysql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -145,7 +146,7 @@ func testCheckAzureRMMySQLDatabaseExists(resourceName string) resource.TestCheck } func testCheckAzureRMMySQLDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mysqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).mysql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_mysql_firewall_rule.go b/azurerm/resource_arm_mysql_firewall_rule.go index b52eb281d68b..eb0a2ee5d174 100644 --- a/azurerm/resource_arm_mysql_firewall_rule.go +++ b/azurerm/resource_arm_mysql_firewall_rule.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -17,6 +18,7 @@ func resourceArmMySqlFirewallRule() *schema.Resource { Read: resourceArmMySqlFirewallRuleRead, Update: resourceArmMySqlFirewallRuleCreateUpdate, Delete: resourceArmMySqlFirewallRuleDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -31,9 +33,10 @@ func resourceArmMySqlFirewallRule() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "server_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateMySqlServerName, }, "start_ip_address": { @@ -50,7 +53,7 @@ func resourceArmMySqlFirewallRule() *schema.Resource { } func resourceArmMySqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlFirewallRulesClient + client := meta.(*ArmClient).mysql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MySQL Firewall Rule creation.") @@ -61,7 +64,7 @@ func resourceArmMySqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta inter startIPAddress := d.Get("start_ip_address").(string) endIPAddress := d.Get("end_ip_address").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -104,10 +107,10 @@ func resourceArmMySqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta inter } func resourceArmMySqlFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlFirewallRulesClient + client := meta.(*ArmClient).mysql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -134,10 +137,10 @@ func resourceArmMySqlFirewallRuleRead(d *schema.ResourceData, meta interface{}) } func resourceArmMySqlFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlFirewallRulesClient + client := meta.(*ArmClient).mysql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_mysql_firewall_rule_test.go b/azurerm/resource_arm_mysql_firewall_rule_test.go index 795cf0f82a8a..9113bae23279 100644 --- a/azurerm/resource_arm_mysql_firewall_rule_test.go +++ b/azurerm/resource_arm_mysql_firewall_rule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMMySQLFirewallRule_basic(t *testing.T) { } func TestAccAzureRMMySQLFirewallRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -77,7 +78,7 @@ func testCheckAzureRMMySQLFirewallRuleExists(resourceName string) resource.TestC return fmt.Errorf("Bad: no resource group found in state for MySQL Firewall Rule: %s", name) } - client := testAccProvider.Meta().(*ArmClient).mysqlFirewallRulesClient + client := testAccProvider.Meta().(*ArmClient).mysql.FirewallRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -93,7 +94,7 @@ func testCheckAzureRMMySQLFirewallRuleExists(resourceName string) resource.TestC } func testCheckAzureRMMySQLFirewallRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mysqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).mysql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_mysql_server.go b/azurerm/resource_arm_mysql_server.go index abd45396529c..db2a09d93935 100644 --- a/azurerm/resource_arm_mysql_server.go +++ b/azurerm/resource_arm_mysql_server.go @@ -12,6 +12,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -21,15 +23,17 @@ func resourceArmMySqlServer() *schema.Resource { Read: resourceArmMySqlServerRead, Update: resourceArmMySqlServerUpdate, Delete: resourceArmMySqlServerDelete, + 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: azure.ValidateMySqlServerName, }, "location": azure.SchemaLocation(), @@ -156,6 +160,15 @@ func resourceArmMySqlServer() *schema.Resource { }, true), DiffSuppressFunc: suppress.CaseDifference, }, + "auto_grow": { + Type: schema.TypeString, + Optional: true, + Default: string(mysql.StorageAutogrowEnabled), + ValidateFunc: validation.StringInSlice([]string{ + string(mysql.StorageAutogrowEnabled), + string(mysql.StorageAutogrowDisabled), + }, false), + }, }, }, }, @@ -175,11 +188,10 @@ func resourceArmMySqlServer() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - tier, _ := diff.GetOk("sku.0.tier") storageMB, _ := diff.GetOk("storage_profile.0.storage_mb") @@ -193,7 +205,7 @@ func resourceArmMySqlServer() *schema.Resource { } func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlServersClient + client := meta.(*ArmClient).mysql.ServersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MySQL Server creation.") @@ -207,9 +219,9 @@ func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) erro sslEnforcement := d.Get("ssl_enforcement").(string) version := d.Get("version").(string) createMode := "Default" - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -236,7 +248,7 @@ func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) erro CreateMode: mysql.CreateMode(createMode), }, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, properties) @@ -263,7 +275,7 @@ func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) erro } func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlServersClient + client := meta.(*ArmClient).mysql.ServersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM MySQL Server update.") @@ -276,7 +288,7 @@ func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) erro version := d.Get("version").(string) sku := expandMySQLServerSku(d) storageProfile := expandMySQLStorageProfile(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := mysql.ServerUpdateParameters{ ServerUpdateParametersProperties: &mysql.ServerUpdateParametersProperties{ @@ -286,7 +298,7 @@ func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) erro SslEnforcement: mysql.SslEnforcementEnum(sslEnforcement), }, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Update(ctx, resourceGroup, name, properties) @@ -313,10 +325,10 @@ func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) erro } func resourceArmMySqlServerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlServersClient + client := meta.(*ArmClient).mysql.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -352,19 +364,17 @@ func resourceArmMySqlServerRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error setting `storage_profile`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - // Computed d.Set("fqdn", resp.FullyQualifiedDomainName) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmMySqlServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlServersClient + client := meta.(*ArmClient).mysql.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -407,11 +417,13 @@ func expandMySQLStorageProfile(d *schema.ResourceData) *mysql.StorageProfile { backupRetentionDays := storageprofile["backup_retention_days"].(int) geoRedundantBackup := storageprofile["geo_redundant_backup"].(string) storageMB := storageprofile["storage_mb"].(int) + autoGrow := storageprofile["auto_grow"].(string) return &mysql.StorageProfile{ BackupRetentionDays: utils.Int32(int32(backupRetentionDays)), GeoRedundantBackup: mysql.GeoRedundantBackup(geoRedundantBackup), StorageMB: utils.Int32(int32(storageMB)), + StorageAutogrow: mysql.StorageAutogrow(autoGrow), } } @@ -448,5 +460,7 @@ func flattenMySQLStorageProfile(resp *mysql.StorageProfile) []interface{} { values["geo_redundant_backup"] = string(resp.GeoRedundantBackup) + values["auto_grow"] = string(resp.StorageAutogrow) + return []interface{}{values} } diff --git a/azurerm/resource_arm_mysql_server_test.go b/azurerm/resource_arm_mysql_server_test.go index e82401bff04a..58c114640174 100644 --- a/azurerm/resource_arm_mysql_server_test.go +++ b/azurerm/resource_arm_mysql_server_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMMySQLServer_basicFiveSix(t *testing.T) { } func TestAccAzureRMMySQLServer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -66,7 +67,7 @@ func TestAccAzureRMMySQLServer_requiresImport(t *testing.T) { } func TestAccAzureRMMySQLServer_basicFiveSeven(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -237,6 +238,36 @@ func TestAccAzureRMMySQLServer_updateSKU(t *testing.T) { }) } +func TestAccAzureRMMySQLServer_storageAutogrow(t *testing.T) { + resourceName := "azurerm_mysql_server.test" + ri := tf.AccRandTimeInt() + location := testLocation() + config := testAccAzureRMMySQLServer_basicFiveSeven(ri, location) + updatedConfig := testAccAzureRMMySQLServer_autogrow(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Enabled"), + ), + }, + { + Config: updatedConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Disabled"), + ), + }, + }, + }) +} + // func testCheckAzureRMMySQLServerExists(resourceName string) resource.TestCheckFunc { @@ -253,7 +284,7 @@ func testCheckAzureRMMySQLServerExists(resourceName string) resource.TestCheckFu return fmt.Errorf("Bad: no resource group found in state for MySQL Server: %s", name) } - client := testAccProvider.Meta().(*ArmClient).mysqlServersClient + client := testAccProvider.Meta().(*ArmClient).mysql.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -270,7 +301,7 @@ func testCheckAzureRMMySQLServerExists(resourceName string) resource.TestCheckFu } func testCheckAzureRMMySQLServerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).mysqlServersClient + client := testAccProvider.Meta().(*ArmClient).mysql.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -521,3 +552,37 @@ resource "azurerm_mysql_server" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMMySQLServer_autogrow(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + auto_grow = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.7" + ssl_enforcement = "Enabled" +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_mysql_virtual_network_rule.go b/azurerm/resource_arm_mysql_virtual_network_rule.go index 5ee478f90dac..4a5a62bf7433 100644 --- a/azurerm/resource_arm_mysql_virtual_network_rule.go +++ b/azurerm/resource_arm_mysql_virtual_network_rule.go @@ -14,6 +14,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -54,7 +55,7 @@ func resourceArmMySqlVirtualNetworkRule() *schema.Resource { } func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + client := meta.(*ArmClient).mysql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -62,7 +63,7 @@ func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta resourceGroup := d.Get("resource_group_name").(string) subnetId := d.Get("subnet_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -77,8 +78,8 @@ func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta // 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) + subnetsClient := meta.(*ArmClient).network.SubnetsClient + subnetParsedId, err := azure.ParseAzureResourceID(subnetId) if err != nil { return err } @@ -152,10 +153,10 @@ func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta } func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + client := meta.(*ArmClient).mysql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -187,10 +188,10 @@ func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interfa } func resourceArmMySqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + client := meta.(*ArmClient).mysql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -217,7 +218,7 @@ func resourceArmMySqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta inter return nil } -func mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client mysql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { +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) diff --git a/azurerm/resource_arm_mysql_virtual_network_rule_test.go b/azurerm/resource_arm_mysql_virtual_network_rule_test.go index 3cafbbc1caa6..6bb1aba338e0 100644 --- a/azurerm/resource_arm_mysql_virtual_network_rule_test.go +++ b/azurerm/resource_arm_mysql_virtual_network_rule_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func TestAccAzureRMMySqlVirtualNetworkRule_basic(t *testing.T) { } func TestAccAzureRMMySqlVirtualNetworkRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -150,7 +151,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName string) resource serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).mysql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -176,7 +177,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleDestroy(s *terraform.State) error { serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).mysql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -206,7 +207,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleDisappears(resourceName string) reso serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).mysql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, serverName, ruleName) diff --git a/azurerm/resource_arm_network_connection_monitor.go b/azurerm/resource_arm_network_connection_monitor.go index 0afd134d50a5..120257d8f590 100644 --- a/azurerm/resource_arm_network_connection_monitor.go +++ b/azurerm/resource_arm_network_connection_monitor.go @@ -3,7 +3,9 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -106,13 +108,13 @@ func resourceArmNetworkConnectionMonitor() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkConnectionMonitorCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).connectionMonitorsClient + client := meta.(*ArmClient).network.ConnectionMonitorsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -132,7 +134,7 @@ func resourceArmNetworkConnectionMonitorCreateUpdate(d *schema.ResourceData, met return err } - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, watcherName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -145,11 +147,11 @@ func resourceArmNetworkConnectionMonitorCreateUpdate(d *schema.ResourceData, met } } - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := network.ConnectionMonitor{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), ConnectionMonitorParameters: &network.ConnectionMonitorParameters{ Source: source, Destination: dest, @@ -181,10 +183,10 @@ func resourceArmNetworkConnectionMonitorCreateUpdate(d *schema.ResourceData, met } func resourceArmNetworkConnectionMonitorRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).connectionMonitorsClient + client := meta.(*ArmClient).network.ConnectionMonitorsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -223,16 +225,14 @@ func resourceArmNetworkConnectionMonitorRead(d *schema.ResourceData, meta interf } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmNetworkConnectionMonitorDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).connectionMonitorsClient + client := meta.(*ArmClient).network.ConnectionMonitorsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_network_connection_monitor_test.go b/azurerm/resource_arm_network_connection_monitor_test.go index 61fc8444dc1e..6fc13bce9ce5 100644 --- a/azurerm/resource_arm_network_connection_monitor_test.go +++ b/azurerm/resource_arm_network_connection_monitor_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func testAccAzureRMNetworkConnectionMonitor_addressBasic(t *testing.T) { @@ -43,7 +44,7 @@ func testAccAzureRMNetworkConnectionMonitor_addressBasic(t *testing.T) { } func testAccAzureRMNetworkConnectionMonitor_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -70,7 +71,6 @@ func testAccAzureRMNetworkConnectionMonitor_requiresImport(t *testing.T) { }, }, }) - } func testAccAzureRMNetworkConnectionMonitor_addressComplete(t *testing.T) { @@ -319,7 +319,7 @@ func testAccAzureRMNetworkConnectionMonitor_conflictingDestinations(t *testing.T func testCheckAzureRMNetworkConnectionMonitorExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).connectionMonitorsClient + client := testAccProvider.Meta().(*ArmClient).network.ConnectionMonitorsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext rs, ok := s.RootModule().Resources[resourceName] @@ -345,7 +345,7 @@ func testCheckAzureRMNetworkConnectionMonitorExists(resourceName string) resourc } func testCheckAzureRMNetworkConnectionMonitorDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).connectionMonitorsClient + client := testAccProvider.Meta().(*ArmClient).network.ConnectionMonitorsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -523,7 +523,7 @@ resource "azurerm_network_connection_monitor" "test" { destination { address = "terraform.io" - port = 80 + port = 80 } depends_on = ["azurerm_virtual_machine_extension.src"] @@ -662,9 +662,9 @@ resource "azurerm_network_connection_monitor" "test" { } destination { - address = "terraform.io" - virtual_machine_id = "${azurerm_virtual_machine.src.id}" - port = 80 + address = "terraform.io" + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + port = 80 } depends_on = ["azurerm_virtual_machine_extension.src"] @@ -689,7 +689,7 @@ resource "azurerm_network_connection_monitor" "import" { destination { address = "terraform.io" - port = 80 + port = 80 } depends_on = ["azurerm_virtual_machine_extension.src"] diff --git a/azurerm/resource_arm_network_ddos_protection_plan.go b/azurerm/resource_arm_network_ddos_protection_plan.go index 50735fa96944..109e24918d60 100644 --- a/azurerm/resource_arm_network_ddos_protection_plan.go +++ b/azurerm/resource_arm_network_ddos_protection_plan.go @@ -4,10 +4,13 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,13 +46,13 @@ func resourceArmNetworkDDoSProtectionPlan() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkDDoSProtectionPlanCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ddosProtectionPlanClient + client := meta.(*ArmClient).network.DDOSProtectionPlansClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for DDoS protection plan creation") @@ -57,7 +60,7 @@ func resourceArmNetworkDDoSProtectionPlanCreateUpdate(d *schema.ResourceData, me name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -71,22 +74,22 @@ func resourceArmNetworkDDoSProtectionPlanCreateUpdate(d *schema.ResourceData, me } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) vnetsToLock, err := expandArmNetworkDDoSProtectionPlanVnetNames(d) if err != nil { return fmt.Errorf("Error extracting names of Virtual Network: %+v", err) } - azureRMLockByName(name, azureNetworkDDoSProtectionPlanResourceName) - defer azureRMUnlockByName(name, azureNetworkDDoSProtectionPlanResourceName) + locks.ByName(name, azureNetworkDDoSProtectionPlanResourceName) + defer locks.UnlockByName(name, azureNetworkDDoSProtectionPlanResourceName) - azureRMLockMultipleByName(vnetsToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) + locks.MultipleByName(vnetsToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) parameters := network.DdosProtectionPlan{ Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) @@ -113,15 +116,15 @@ func resourceArmNetworkDDoSProtectionPlanCreateUpdate(d *schema.ResourceData, me } func resourceArmNetworkDDoSProtectionPlanRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ddosProtectionPlanClient + client := meta.(*ArmClient).network.DDOSProtectionPlansClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } resourceGroup := id.ResourceGroup - name := id.Path["NetworkDDoSProtectionPlans"] + name := id.Path["ddosProtectionPlans"] plan, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -147,21 +150,19 @@ func resourceArmNetworkDDoSProtectionPlanRead(d *schema.ResourceData, meta inter } } - flattenAndSetTags(d, plan.Tags) - - return nil + return tags.FlattenAndSet(d, plan.Tags) } func resourceArmNetworkDDoSProtectionPlanDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ddosProtectionPlanClient + client := meta.(*ArmClient).network.DDOSProtectionPlansClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } resourceGroup := id.ResourceGroup - name := id.Path["NetworkDDoSProtectionPlans"] + name := id.Path["ddosProtectionPlans"] read, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -179,11 +180,11 @@ func resourceArmNetworkDDoSProtectionPlanDelete(d *schema.ResourceData, meta int return fmt.Errorf("Error extracting names of Virtual Network: %+v", err) } - azureRMLockByName(name, azureNetworkDDoSProtectionPlanResourceName) - defer azureRMUnlockByName(name, azureNetworkDDoSProtectionPlanResourceName) + locks.ByName(name, azureNetworkDDoSProtectionPlanResourceName) + defer locks.UnlockByName(name, azureNetworkDDoSProtectionPlanResourceName) - azureRMLockMultipleByName(vnetsToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) + locks.MultipleByName(vnetsToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -202,7 +203,7 @@ func expandArmNetworkDDoSProtectionPlanVnetNames(d *schema.ResourceData) (*[]str vnetNames := make([]string, 0) for _, vnetID := range vnetIDs { - vnetResourceID, err := parseAzureResourceID(vnetID.(string)) + vnetResourceID, err := azure.ParseAzureResourceID(vnetID.(string)) if err != nil { return nil, err } diff --git a/azurerm/resource_arm_network_ddos_protection_plan_test.go b/azurerm/resource_arm_network_ddos_protection_plan_test.go index bc52dc085f41..e8360fc3586d 100644 --- a/azurerm/resource_arm_network_ddos_protection_plan_test.go +++ b/azurerm/resource_arm_network_ddos_protection_plan_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -20,6 +21,9 @@ func TestAccAzureRMNetworkDDoSProtectionPlan(t *testing.T) { "withTags": testAccAzureRMNetworkDDoSProtectionPlan_withTags, "disappears": testAccAzureRMNetworkDDoSProtectionPlan_disappears, }, + "datasource": { + "basic": testAccAzureRMNetworkDDoSProtectionPlanDataSource_basic, + }, "deprecated": { "basic": testAccAzureRMDDoSProtectionPlan_basic, "requiresImport": testAccAzureRMDDoSProtectionPlan_requiresImport, @@ -66,7 +70,7 @@ func testAccAzureRMNetworkDDoSProtectionPlan_basic(t *testing.T) { } func testAccAzureRMNetworkDDoSProtectionPlan_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -154,7 +158,7 @@ func testAccAzureRMNetworkDDoSProtectionPlan_disappears(t *testing.T) { func testCheckAzureRMNetworkDDoSProtectionPlanExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).ddosProtectionPlanClient + client := testAccProvider.Meta().(*ArmClient).network.DDOSProtectionPlansClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API @@ -196,7 +200,7 @@ func testCheckAzureRMNetworkDDoSProtectionPlanDisappears(resourceName string) re return fmt.Errorf("Bad: no resource group found in state for DDoS Protection Plan: %q", name) } - client := testAccProvider.Meta().(*ArmClient).ddosProtectionPlanClient + client := testAccProvider.Meta().(*ArmClient).network.DDOSProtectionPlansClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -212,7 +216,7 @@ func testCheckAzureRMNetworkDDoSProtectionPlanDisappears(resourceName string) re } func testCheckAzureRMNetworkDDoSProtectionPlanDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).ddosProtectionPlanClient + client := testAccProvider.Meta().(*ArmClient).network.DDOSProtectionPlansClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_network_interface.go b/azurerm/resource_arm_network_interface.go index 0a79b5fd6789..12a07b389219 100644 --- a/azurerm/resource_arm_network_interface.go +++ b/azurerm/resource_arm_network_interface.go @@ -5,13 +5,17 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -86,7 +90,6 @@ func resourceArmNetworkInterface() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(network.IPv4), - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(network.IPv4), string(network.IPv6), @@ -101,7 +104,7 @@ func resourceArmNetworkInterface() *schema.Resource { string(network.Dynamic), string(network.Static), }, true), - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, DiffSuppressFunc: suppress.CaseDifference, }, @@ -238,13 +241,13 @@ func resourceArmNetworkInterface() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Network Interface creation.") @@ -252,7 +255,7 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -268,15 +271,15 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf location := azure.NormalizeLocation(d.Get("location").(string)) enableIpForwarding := d.Get("enable_ip_forwarding").(bool) enableAcceleratedNetworking := d.Get("enable_accelerated_networking").(bool) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := network.InterfacePropertiesFormat{ EnableIPForwarding: &enableIpForwarding, EnableAcceleratedNetworking: &enableAcceleratedNetworking, } - azureRMLockByName(name, networkInterfaceResourceName) - defer azureRMUnlockByName(name, networkInterfaceResourceName) + locks.ByName(name, networkInterfaceResourceName) + defer locks.UnlockByName(name, networkInterfaceResourceName) if v, ok := d.GetOk("network_security_group_id"); ok { nsgId := v.(string) @@ -284,13 +287,15 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf ID: &nsgId, } - networkSecurityGroupName, err := parseNetworkSecurityGroupName(nsgId) + parsedNsgID, err := azure.ParseAzureResourceID(nsgId) if err != nil { - return err + return fmt.Errorf("Error parsing Network Security Group ID %q: %+v", nsgId, err) } - azureRMLockByName(networkSecurityGroupName, networkSecurityGroupResourceName) - defer azureRMUnlockByName(networkSecurityGroupName, networkSecurityGroupResourceName) + networkSecurityGroupName := parsedNsgID.Path["networkSecurityGroups"] + + locks.ByName(networkSecurityGroupName, networkSecurityGroupResourceName) + defer locks.UnlockByName(networkSecurityGroupName, networkSecurityGroupResourceName) } dns, hasDns := d.GetOk("dns_servers") @@ -321,11 +326,11 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf return fmt.Errorf("Error Building list of Network Interface IP Configurations: %+v", sgErr) } - azureRMLockMultipleByName(subnetnToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(subnetnToLock, subnetResourceName) + locks.MultipleByName(subnetnToLock, subnetResourceName) + defer locks.UnlockMultipleByName(subnetnToLock, subnetResourceName) - azureRMLockMultipleByName(vnnToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnnToLock, virtualNetworkResourceName) + locks.MultipleByName(vnnToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnnToLock, virtualNetworkResourceName) if len(ipConfigs) > 0 { properties.IPConfigurations = &ipConfigs @@ -335,7 +340,7 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf Name: &name, Location: &location, InterfacePropertiesFormat: &properties, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, name, iface) @@ -361,10 +366,10 @@ func resourceArmNetworkInterfaceCreateUpdate(d *schema.ResourceData, meta interf } func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -387,7 +392,6 @@ func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e } if props := resp.InterfacePropertiesFormat; props != nil { - d.Set("mac_address", props.MacAddress) addresses := make([]interface{}, 0) if configs := props.IPConfigurations; configs != nil { @@ -395,7 +399,7 @@ func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e if ipProps := config.InterfaceIPConfigurationPropertiesFormat; ipProps != nil { if v := ipProps.PrivateIPAddress; v != nil { if i == 0 { - d.Set("private_ip_address", *v) + d.Set("private_ip_address", v) } addresses = append(addresses, *v) } @@ -445,34 +449,34 @@ func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e d.Set("enable_accelerated_networking", resp.EnableAcceleratedNetworking) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } resGroup := id.ResourceGroup name := id.Path["networkInterfaces"] - azureRMLockByName(name, networkInterfaceResourceName) - defer azureRMUnlockByName(name, networkInterfaceResourceName) + locks.ByName(name, networkInterfaceResourceName) + defer locks.UnlockByName(name, networkInterfaceResourceName) if v, ok := d.GetOk("network_security_group_id"); ok { networkSecurityGroupId := v.(string) - networkSecurityGroupName, err2 := parseNetworkSecurityGroupName(networkSecurityGroupId) - if err2 != nil { - return err2 + parsedNsgID, err := azure.ParseAzureResourceID(networkSecurityGroupId) + if err != nil { + return fmt.Errorf("Error parsing Network Security Group ID %q: %+v", networkSecurityGroupId, err) } - azureRMLockByName(networkSecurityGroupName, networkSecurityGroupResourceName) - defer azureRMUnlockByName(networkSecurityGroupName, networkSecurityGroupResourceName) + networkSecurityGroupName := parsedNsgID.Path["networkSecurityGroups"] + + locks.ByName(networkSecurityGroupName, networkSecurityGroupResourceName) + defer locks.UnlockByName(networkSecurityGroupName, networkSecurityGroupResourceName) } configs := d.Get("ip_configuration").([]interface{}) @@ -484,7 +488,7 @@ func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) subnet_id := data["subnet_id"].(string) if subnet_id != "" { - subnetId, err2 := parseAzureResourceID(subnet_id) + subnetId, err2 := azure.ParseAzureResourceID(subnet_id) if err2 != nil { return err2 } @@ -500,11 +504,11 @@ func resourceArmNetworkInterfaceDelete(d *schema.ResourceData, meta interface{}) } } - azureRMLockMultipleByName(&subnetNamesToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(&subnetNamesToLock, subnetResourceName) + locks.MultipleByName(&subnetNamesToLock, subnetResourceName) + defer locks.UnlockMultipleByName(&subnetNamesToLock, subnetResourceName) - azureRMLockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + locks.MultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) future, err := client.Delete(ctx, resGroup, name) if err != nil { @@ -614,7 +618,7 @@ func expandAzureRmNetworkInterfaceIpConfigurations(d *schema.ResourceData) ([]ne ID: &subnet_id, } - subnetId, err := parseAzureResourceID(subnet_id) + subnetId, err := azure.ParseAzureResourceID(subnet_id) if err != nil { return []network.InterfaceIPConfiguration{}, nil, nil, err } diff --git a/azurerm/resource_arm_network_interface_application_gateway_association.go b/azurerm/resource_arm_network_interface_application_gateway_association.go index e49903cf8dd8..cfd52630939a 100644 --- a/azurerm/resource_arm_network_interface_application_gateway_association.go +++ b/azurerm/resource_arm_network_interface_application_gateway_association.go @@ -5,11 +5,13 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -48,7 +50,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociation( } func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Network Interface <-> Application Gateway Backend Address Pool Association creation.") @@ -57,7 +59,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationC ipConfigurationName := d.Get("ip_configuration_name").(string) backendAddressPoolId := d.Get("backend_address_pool_id").(string) - id, err := parseAzureResourceID(networkInterfaceId) + id, err := azure.ParseAzureResourceID(networkInterfaceId) if err != nil { return err } @@ -65,8 +67,8 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationC networkInterfaceName := id.Path["networkInterfaces"] resourceGroup := id.ResourceGroup - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { @@ -106,7 +108,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationC for _, existingPool := range *p.ApplicationGatewayBackendAddressPools { if id := existingPool.ID; id != nil { if *id == backendAddressPoolId { - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { return tf.ImportAsExistsError("azurerm_network_interface_application_gateway_backend_address_pool_association", resourceId) } @@ -141,7 +143,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationC } func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -149,7 +151,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationR return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{backendAddressPoolId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -210,15 +212,13 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationR d.Set("backend_address_pool_id", backendAddressPoolId) d.Set("ip_configuration_name", ipConfigurationName) - if id := read.ID; id != nil { - d.Set("network_interface_id", *id) - } + d.Set("network_interface_id", read.ID) return nil } func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -226,7 +226,7 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationD return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{backendAddressPoolId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -236,8 +236,8 @@ func resourceArmNetworkInterfaceApplicationGatewayBackendAddressPoolAssociationD resourceGroup := nicID.ResourceGroup backendAddressPoolId := splitId[1] - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { diff --git a/azurerm/resource_arm_network_interface_application_gateway_association_test.go b/azurerm/resource_arm_network_interface_application_gateway_association_test.go index 3393adee9da0..2ce353923ede 100644 --- a/azurerm/resource_arm_network_interface_application_gateway_association_test.go +++ b/azurerm/resource_arm_network_interface_application_gateway_association_test.go @@ -4,11 +4,12 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssociation_basic(t *testing.T) { @@ -31,7 +32,7 @@ func TestAccAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssociati } func TestAccAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssociation_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -90,7 +91,7 @@ func testCheckAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssocia return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -100,7 +101,7 @@ func testCheckAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssocia backendAddressPoolId := rs.Primary.Attributes["backend_address_pool_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") @@ -140,7 +141,7 @@ func testCheckAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssocia return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -150,7 +151,7 @@ func testCheckAzureRMNetworkInterfaceApplicationGatewayBackendAddressPoolAssocia backendAddressPoolId := rs.Primary.Attributes["backend_address_pool_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") diff --git a/azurerm/resource_arm_network_interface_application_security_group_association.go b/azurerm/resource_arm_network_interface_application_security_group_association.go index e56bf3ccb104..8f4e673fadd2 100644 --- a/azurerm/resource_arm_network_interface_application_security_group_association.go +++ b/azurerm/resource_arm_network_interface_application_security_group_association.go @@ -5,11 +5,13 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -48,7 +50,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociation() *schema.Re } func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Network Interface <-> Application Security Group Association creation.") @@ -57,7 +59,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationCreate(d *sch ipConfigurationName := d.Get("ip_configuration_name").(string) applicationSecurityGroupId := d.Get("application_security_group_id").(string) - id, err := parseAzureResourceID(networkInterfaceId) + id, err := azure.ParseAzureResourceID(networkInterfaceId) if err != nil { return err } @@ -65,8 +67,8 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationCreate(d *sch networkInterfaceName := id.Path["networkInterfaces"] resourceGroup := id.ResourceGroup - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { @@ -105,7 +107,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationCreate(d *sch for _, existingGroup := range *p.ApplicationSecurityGroups { if id := existingGroup.ID; id != nil { if *id == applicationSecurityGroupId { - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { return tf.ImportAsExistsError("azurerm_network_interface_application_security_group_association", *id) } @@ -141,7 +143,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationCreate(d *sch } func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -149,7 +151,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationRead(d *schem return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{applicationSecurityGroupId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -210,15 +212,13 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationRead(d *schem d.Set("application_security_group_id", applicationSecurityGroupId) d.Set("ip_configuration_name", ipConfigurationName) - if id := read.ID; id != nil { - d.Set("network_interface_id", *id) - } + d.Set("network_interface_id", read.ID) return nil } func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -226,7 +226,7 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationDelete(d *sch return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{applicationSecurityGroupId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -236,8 +236,8 @@ func resourceArmNetworkInterfaceApplicationSecurityGroupAssociationDelete(d *sch resourceGroup := nicID.ResourceGroup applicationSecurityGroupId := splitId[1] - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { diff --git a/azurerm/resource_arm_network_interface_application_security_group_association_test.go b/azurerm/resource_arm_network_interface_application_security_group_association_test.go index 545b492e365e..741e940b5d6f 100644 --- a/azurerm/resource_arm_network_interface_application_security_group_association_test.go +++ b/azurerm/resource_arm_network_interface_application_security_group_association_test.go @@ -4,11 +4,12 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNetworkInterfaceApplicationSecurityGroupAssociation_basic(t *testing.T) { @@ -31,7 +32,7 @@ func TestAccAzureRMNetworkInterfaceApplicationSecurityGroupAssociation_basic(t * } func TestAccAzureRMNetworkInterfaceApplicationSecurityGroupAssociation_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -90,7 +91,7 @@ func testCheckAzureRMNetworkInterfaceApplicationSecurityGroupAssociationExists(r return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -100,7 +101,7 @@ func testCheckAzureRMNetworkInterfaceApplicationSecurityGroupAssociationExists(r applicationSecurityGroupId := rs.Primary.Attributes["application_security_group_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") @@ -140,7 +141,7 @@ func testCheckAzureRMNetworkInterfaceApplicationSecurityGroupAssociationDisappea return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -150,7 +151,7 @@ func testCheckAzureRMNetworkInterfaceApplicationSecurityGroupAssociationDisappea applicationSecurityGroupId := rs.Primary.Attributes["application_security_group_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") @@ -223,7 +224,7 @@ resource "azurerm_network_interface" "test" { name = "testconfiguration1" subnet_id = "${azurerm_subnet.test.id}" private_ip_address_allocation = "Dynamic" - application_security_group_ids = [ "${azurerm_application_security_group.test.id}" ] + application_security_group_ids = ["${azurerm_application_security_group.test.id}"] } } diff --git a/azurerm/resource_arm_network_interface_backend_address_pool_association.go b/azurerm/resource_arm_network_interface_backend_address_pool_association.go index bd8a72594335..5cf6225c8622 100644 --- a/azurerm/resource_arm_network_interface_backend_address_pool_association.go +++ b/azurerm/resource_arm_network_interface_backend_address_pool_association.go @@ -5,11 +5,13 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -48,7 +50,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociation() *schema.Resource } func resourceArmNetworkInterfaceBackendAddressPoolAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Network Interface <-> Load Balancer Backend Address Pool Association creation.") @@ -57,7 +59,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationCreate(d *schema.Re ipConfigurationName := d.Get("ip_configuration_name").(string) backendAddressPoolId := d.Get("backend_address_pool_id").(string) - id, err := parseAzureResourceID(networkInterfaceId) + id, err := azure.ParseAzureResourceID(networkInterfaceId) if err != nil { return err } @@ -65,8 +67,8 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationCreate(d *schema.Re networkInterfaceName := id.Path["networkInterfaces"] resourceGroup := id.ResourceGroup - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { @@ -106,7 +108,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationCreate(d *schema.Re for _, existingPool := range *p.LoadBalancerBackendAddressPools { if id := existingPool.ID; id != nil { if *id == backendAddressPoolId { - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { return tf.ImportAsExistsError("azurerm_network_interface_backend_address_pool_association", resourceId) } @@ -141,7 +143,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationCreate(d *schema.Re } func resourceArmNetworkInterfaceBackendAddressPoolAssociationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -149,7 +151,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationRead(d *schema.Reso return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{backendAddressPoolId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -210,15 +212,13 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationRead(d *schema.Reso d.Set("backend_address_pool_id", backendAddressPoolId) d.Set("ip_configuration_name", ipConfigurationName) - if id := read.ID; id != nil { - d.Set("network_interface_id", *id) - } + d.Set("network_interface_id", read.ID) return nil } func resourceArmNetworkInterfaceBackendAddressPoolAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -226,7 +226,7 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationDelete(d *schema.Re return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{backendAddressPoolId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -236,8 +236,8 @@ func resourceArmNetworkInterfaceBackendAddressPoolAssociationDelete(d *schema.Re resourceGroup := nicID.ResourceGroup backendAddressPoolId := splitId[1] - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { diff --git a/azurerm/resource_arm_network_interface_backend_address_pool_association_test.go b/azurerm/resource_arm_network_interface_backend_address_pool_association_test.go index 2f8bf732863e..6ec02bce352f 100644 --- a/azurerm/resource_arm_network_interface_backend_address_pool_association_test.go +++ b/azurerm/resource_arm_network_interface_backend_address_pool_association_test.go @@ -4,11 +4,12 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNetworkInterfaceBackendAddressPoolAssociation_basic(t *testing.T) { @@ -31,7 +32,7 @@ func TestAccAzureRMNetworkInterfaceBackendAddressPoolAssociation_basic(t *testin } func TestAccAzureRMNetworkInterfaceBackendAddressPoolAssociation_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -90,7 +91,7 @@ func testCheckAzureRMNetworkInterfaceBackendAddressPoolAssociationExists(resourc return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -100,7 +101,7 @@ func testCheckAzureRMNetworkInterfaceBackendAddressPoolAssociationExists(resourc backendAddressPoolId := rs.Primary.Attributes["backend_address_pool_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") @@ -140,7 +141,7 @@ func testCheckAzureRMNetworkInterfaceBackendAddressPoolAssociationDisappears(res return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -150,7 +151,7 @@ func testCheckAzureRMNetworkInterfaceBackendAddressPoolAssociationDisappears(res backendAddressPoolId := rs.Primary.Attributes["backend_address_pool_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") diff --git a/azurerm/resource_arm_network_interface_nat_rule_association.go b/azurerm/resource_arm_network_interface_nat_rule_association.go index 9e7eed45feae..be37664bf106 100644 --- a/azurerm/resource_arm_network_interface_nat_rule_association.go +++ b/azurerm/resource_arm_network_interface_nat_rule_association.go @@ -5,11 +5,13 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -48,7 +50,7 @@ func resourceArmNetworkInterfaceNatRuleAssociation() *schema.Resource { } func resourceArmNetworkInterfaceNatRuleAssociationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Network Interface <-> Load Balancer NAT Rule Association creation.") @@ -57,7 +59,7 @@ func resourceArmNetworkInterfaceNatRuleAssociationCreate(d *schema.ResourceData, ipConfigurationName := d.Get("ip_configuration_name").(string) natRuleId := d.Get("nat_rule_id").(string) - id, err := parseAzureResourceID(networkInterfaceId) + id, err := azure.ParseAzureResourceID(networkInterfaceId) if err != nil { return err } @@ -65,8 +67,8 @@ func resourceArmNetworkInterfaceNatRuleAssociationCreate(d *schema.ResourceData, networkInterfaceName := id.Path["networkInterfaces"] resourceGroup := id.ResourceGroup - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { @@ -106,7 +108,7 @@ func resourceArmNetworkInterfaceNatRuleAssociationCreate(d *schema.ResourceData, for _, existingRule := range *p.LoadBalancerInboundNatRules { if id := existingRule.ID; id != nil { if *id == natRuleId { - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { return tf.ImportAsExistsError("azurerm_network_interface_nat_rule_association", resourceId) } @@ -141,7 +143,7 @@ func resourceArmNetworkInterfaceNatRuleAssociationCreate(d *schema.ResourceData, } func resourceArmNetworkInterfaceNatRuleAssociationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -149,7 +151,7 @@ func resourceArmNetworkInterfaceNatRuleAssociationRead(d *schema.ResourceData, m return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{natRuleId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -210,15 +212,13 @@ func resourceArmNetworkInterfaceNatRuleAssociationRead(d *schema.ResourceData, m d.Set("ip_configuration_name", ipConfigurationName) d.Set("nat_rule_id", natRuleId) - if id := read.ID; id != nil { - d.Set("network_interface_id", *id) - } + d.Set("network_interface_id", read.ID) return nil } func resourceArmNetworkInterfaceNatRuleAssociationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).ifaceClient + client := meta.(*ArmClient).network.InterfacesClient ctx := meta.(*ArmClient).StopContext splitId := strings.Split(d.Id(), "|") @@ -226,7 +226,7 @@ func resourceArmNetworkInterfaceNatRuleAssociationDelete(d *schema.ResourceData, return fmt.Errorf("Expected ID to be in the format {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{natRuleId} but got %q", d.Id()) } - nicID, err := parseAzureResourceID(splitId[0]) + nicID, err := azure.ParseAzureResourceID(splitId[0]) if err != nil { return err } @@ -236,8 +236,8 @@ func resourceArmNetworkInterfaceNatRuleAssociationDelete(d *schema.ResourceData, resourceGroup := nicID.ResourceGroup natRuleId := splitId[1] - azureRMLockByName(networkInterfaceName, networkInterfaceResourceName) - defer azureRMUnlockByName(networkInterfaceName, networkInterfaceResourceName) + locks.ByName(networkInterfaceName, networkInterfaceResourceName) + defer locks.UnlockByName(networkInterfaceName, networkInterfaceResourceName) read, err := client.Get(ctx, resourceGroup, networkInterfaceName, "") if err != nil { diff --git a/azurerm/resource_arm_network_interface_nat_rule_association_test.go b/azurerm/resource_arm_network_interface_nat_rule_association_test.go index d6ac9887e783..5b39c873e5cd 100644 --- a/azurerm/resource_arm_network_interface_nat_rule_association_test.go +++ b/azurerm/resource_arm_network_interface_nat_rule_association_test.go @@ -4,11 +4,12 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNetworkInterfaceNATRuleAssociation_basic(t *testing.T) { @@ -31,7 +32,7 @@ func TestAccAzureRMNetworkInterfaceNATRuleAssociation_basic(t *testing.T) { } func TestAccAzureRMNetworkInterfaceNATRuleAssociation_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -90,7 +91,7 @@ func testCheckAzureRMNetworkInterfaceNATRuleAssociationExists(resourceName strin return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -100,7 +101,7 @@ func testCheckAzureRMNetworkInterfaceNATRuleAssociationExists(resourceName strin natRuleId := rs.Primary.Attributes["nat_rule_id"] ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") @@ -140,7 +141,7 @@ func testCheckAzureRMNetworkInterfaceNATRuleAssociationDisappears(resourceName s return fmt.Errorf("Not found: %s", resourceName) } - nicID, err := parseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) + nicID, err := azure.ParseAzureResourceID(rs.Primary.Attributes["network_interface_id"]) if err != nil { return err } @@ -150,7 +151,7 @@ func testCheckAzureRMNetworkInterfaceNATRuleAssociationDisappears(resourceName s ipConfigurationName := rs.Primary.Attributes["ip_configuration_name"] natRuleId := rs.Primary.Attributes["nat_rule_id"] - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext read, err := client.Get(ctx, resourceGroup, nicName, "") diff --git a/azurerm/resource_arm_network_interface_test.go b/azurerm/resource_arm_network_interface_test.go index 8a5d05bd4078..e820ef6404d4 100644 --- a/azurerm/resource_arm_network_interface_test.go +++ b/azurerm/resource_arm_network_interface_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -31,7 +32,7 @@ func TestAccAzureRMNetworkInterface_disappears(t *testing.T) { } func TestAccAzureRMNetworkInterface_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -423,7 +424,7 @@ func testCheckAzureRMNetworkInterfaceExists(resourceName string) resource.TestCh return fmt.Errorf("Bad: no resource group found in state for availability set: %q", name) } - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name, "") @@ -453,7 +454,7 @@ func testCheckAzureRMNetworkInterfaceDisappears(resourceName string) resource.Te return fmt.Errorf("Bad: no resource group found in state for availability set: %q", name) } - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, name) @@ -470,7 +471,7 @@ func testCheckAzureRMNetworkInterfaceDisappears(resourceName string) resource.Te } func testCheckAzureRMNetworkInterfaceDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).ifaceClient + client := testAccProvider.Meta().(*ArmClient).network.InterfacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_network_packet_capture.go b/azurerm/resource_arm_network_packet_capture.go index 8f97543c4997..3f41338944e8 100644 --- a/azurerm/resource_arm_network_packet_capture.go +++ b/azurerm/resource_arm_network_packet_capture.go @@ -5,10 +5,11 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -135,7 +136,7 @@ func resourceArmNetworkPacketCapture() *schema.Resource { } func resourceArmNetworkPacketCaptureCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -147,7 +148,7 @@ func resourceArmNetworkPacketCaptureCreate(d *schema.ResourceData, meta interfac totalBytesPerSession := d.Get("maximum_bytes_per_session").(int) timeLimitInSeconds := d.Get("maximum_capture_duration").(int) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, watcherName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -201,10 +202,10 @@ func resourceArmNetworkPacketCaptureCreate(d *schema.ResourceData, meta interfac } func resourceArmNetworkPacketCaptureRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -249,10 +250,10 @@ func resourceArmNetworkPacketCaptureRead(d *schema.ResourceData, meta interface{ } func resourceArmNetworkPacketCaptureDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_network_packet_capture_test.go b/azurerm/resource_arm_network_packet_capture_test.go index b9d3f824ea45..d196b245a1e2 100644 --- a/azurerm/resource_arm_network_packet_capture_test.go +++ b/azurerm/resource_arm_network_packet_capture_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func testAccAzureRMNetworkPacketCapture_localDisk(t *testing.T) { @@ -38,7 +39,7 @@ func testAccAzureRMNetworkPacketCapture_localDisk(t *testing.T) { } func testAccAzureRMNetworkPacketCapture_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -148,7 +149,7 @@ func testAccAzureRMNetworkPacketCapture_withFilters(t *testing.T) { func testCheckAzureRMNetworkPacketCaptureExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + client := testAccProvider.Meta().(*ArmClient).network.PacketCapturesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext rs, ok := s.RootModule().Resources[resourceName] @@ -174,7 +175,7 @@ func testCheckAzureRMNetworkPacketCaptureExists(resourceName string) resource.Te } func testCheckAzureRMNetworkPacketCaptureDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + client := testAccProvider.Meta().(*ArmClient).network.PacketCapturesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_network_profile.go b/azurerm/resource_arm_network_profile.go index e81c300d5bdf..295be9b1818c 100644 --- a/azurerm/resource_arm_network_profile.go +++ b/azurerm/resource_arm_network_profile.go @@ -4,11 +4,14 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -77,13 +80,13 @@ func resourceArmNetworkProfile() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkProfileCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).netProfileClient + client := meta.(*ArmClient).network.ProfileClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Network Profile creation") @@ -91,7 +94,7 @@ func resourceArmNetworkProfileCreateUpdate(d *schema.ResourceData, meta interfac name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -105,7 +108,7 @@ func resourceArmNetworkProfileCreateUpdate(d *schema.ResourceData, meta interfac } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) cniConfigs, err := expandNetworkProfileContainerNetworkInterface(d) if err != nil { @@ -117,18 +120,18 @@ func resourceArmNetworkProfileCreateUpdate(d *schema.ResourceData, meta interfac return fmt.Errorf("Error extracting names of Subnet and Virtual Network: %+v", err) } - azureRMLockByName(name, azureNetworkProfileResourceName) - defer azureRMUnlockByName(name, azureNetworkProfileResourceName) + locks.ByName(name, azureNetworkProfileResourceName) + defer locks.UnlockByName(name, azureNetworkProfileResourceName) - azureRMLockMultipleByName(subnetsToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(subnetsToLock, subnetResourceName) + locks.MultipleByName(subnetsToLock, subnetResourceName) + defer locks.UnlockMultipleByName(subnetsToLock, subnetResourceName) - azureRMLockMultipleByName(vnetsToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) + locks.MultipleByName(vnetsToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) parameters := network.Profile{ Location: &location, - Tags: expandTags(tags), + Tags: tags.Expand(t), ProfilePropertiesFormat: &network.ProfilePropertiesFormat{ ContainerNetworkInterfaceConfigurations: cniConfigs, }, @@ -153,10 +156,10 @@ func resourceArmNetworkProfileCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmNetworkProfileRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).netProfileClient + client := meta.(*ArmClient).network.ProfileClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -192,16 +195,14 @@ func resourceArmNetworkProfileRead(d *schema.ResourceData, meta interface{}) err } } - flattenAndSetTags(d, profile.Tags) - - return nil + return tags.FlattenAndSet(d, profile.Tags) } func resourceArmNetworkProfileDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).netProfileClient + client := meta.(*ArmClient).network.ProfileClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -224,14 +225,14 @@ func resourceArmNetworkProfileDelete(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error extracting names of Subnet and Virtual Network: %+v", err) } - azureRMLockByName(name, azureNetworkProfileResourceName) - defer azureRMUnlockByName(name, azureNetworkProfileResourceName) + locks.ByName(name, azureNetworkProfileResourceName) + defer locks.UnlockByName(name, azureNetworkProfileResourceName) - azureRMLockMultipleByName(subnetsToLock, subnetResourceName) - defer azureRMUnlockMultipleByName(subnetsToLock, subnetResourceName) + locks.MultipleByName(subnetsToLock, subnetResourceName) + defer locks.UnlockMultipleByName(subnetsToLock, subnetResourceName) - azureRMLockMultipleByName(vnetsToLock, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) + locks.MultipleByName(vnetsToLock, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(vnetsToLock, virtualNetworkResourceName) if _, err = client.Delete(ctx, resourceGroup, name); err != nil { return fmt.Errorf("Error deleting Network Profile %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -293,7 +294,7 @@ func expandNetworkProfileVirtualNetworkSubnetNames(d *schema.ResourceData) (*[]s ipData := ipConfig.(map[string]interface{}) subnetID := ipData["subnet_id"].(string) - subnetResourceID, err := parseAzureResourceID(subnetID) + subnetResourceID, err := azure.ParseAzureResourceID(subnetID) if err != nil { return nil, nil, err } diff --git a/azurerm/resource_arm_network_profile_test.go b/azurerm/resource_arm_network_profile_test.go index 2226349df33d..a5d9e9985f26 100644 --- a/azurerm/resource_arm_network_profile_test.go +++ b/azurerm/resource_arm_network_profile_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMNetworkProfile_basic(t *testing.T) { } func TestAccAzureRMNetworkProfile_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -137,7 +138,7 @@ func testCheckAzureRMNetworkProfileExists(resourceName string) resource.TestChec return fmt.Errorf("Bad: no resource group found in state for Network Profile: %q", name) } - client := testAccProvider.Meta().(*ArmClient).netProfileClient + client := testAccProvider.Meta().(*ArmClient).network.ProfileClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name, "") if err != nil { @@ -166,7 +167,7 @@ func testCheckAzureRMNetworkProfileDisappears(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Network Profile: %q", name) } - client := testAccProvider.Meta().(*ArmClient).netProfileClient + client := testAccProvider.Meta().(*ArmClient).network.ProfileClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, resourceGroup, name); err != nil { return fmt.Errorf("Bad: Delete on netProfileClient: %+v", err) @@ -177,7 +178,7 @@ func testCheckAzureRMNetworkProfileDisappears(resourceName string) resource.Test } func testCheckAzureRMNetworkProfileDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).netProfileClient + client := testAccProvider.Meta().(*ArmClient).network.ProfileClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_network_security_group.go b/azurerm/resource_arm_network_security_group.go index 839bc0030a27..e98e8574c0c7 100644 --- a/azurerm/resource_arm_network_security_group.go +++ b/azurerm/resource_arm_network_security_group.go @@ -3,13 +3,17 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/go-multierror" "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/set" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -62,7 +66,7 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { string(network.SecurityRuleProtocolTCP), string(network.SecurityRuleProtocolUDP), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "source_port_range": { @@ -134,7 +138,7 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { string(network.SecurityRuleAccessAllow), string(network.SecurityRuleAccessDeny), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "priority": { @@ -150,25 +154,25 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { string(network.SecurityRuleDirectionInbound), string(network.SecurityRuleDirectionOutbound), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkSecurityGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).network.SecurityGroupClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -182,15 +186,15 @@ func resourceArmNetworkSecurityGroupCreateUpdate(d *schema.ResourceData, meta in } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) sgRules, sgErr := expandAzureRmSecurityRules(d) if sgErr != nil { return fmt.Errorf("Error Building list of Network Security Group Rules: %+v", sgErr) } - azureRMLockByName(name, networkSecurityGroupResourceName) - defer azureRMUnlockByName(name, networkSecurityGroupResourceName) + locks.ByName(name, networkSecurityGroupResourceName) + defer locks.UnlockByName(name, networkSecurityGroupResourceName) sg := network.SecurityGroup{ Name: &name, @@ -198,7 +202,7 @@ func resourceArmNetworkSecurityGroupCreateUpdate(d *schema.ResourceData, meta in SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ SecurityRules: &sgRules, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, name, sg) @@ -224,10 +228,10 @@ func resourceArmNetworkSecurityGroupCreateUpdate(d *schema.ResourceData, meta in } func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).network.SecurityGroupClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -256,16 +260,14 @@ func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{ } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).network.SecurityGroupClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_network_security_group_test.go b/azurerm/resource_arm_network_security_group_test.go index 0ea9a628bcc0..d149d8fc0da6 100644 --- a/azurerm/resource_arm_network_security_group_test.go +++ b/azurerm/resource_arm_network_security_group_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMNetworkSecurityGroup_basic(t *testing.T) { } func TestAccAzureRMNetworkSecurityGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -266,7 +267,6 @@ func TestAccAzureRMNetworkSecurityGroup_applicationSecurityGroup(t *testing.T) { func testCheckAzureRMNetworkSecurityGroupExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %q", resourceName) @@ -278,7 +278,7 @@ func testCheckAzureRMNetworkSecurityGroupExists(resourceName string) resource.Te return fmt.Errorf("Bad: no resource group found in state for network security group: %q", sgName) } - client := testAccProvider.Meta().(*ArmClient).secGroupClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityGroupClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, sgName, "") if err != nil { @@ -295,7 +295,6 @@ func testCheckAzureRMNetworkSecurityGroupExists(resourceName string) resource.Te func testCheckAzureRMNetworkSecurityGroupDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -307,7 +306,7 @@ func testCheckAzureRMNetworkSecurityGroupDisappears(resourceName string) resourc return fmt.Errorf("Bad: no resource group found in state for network security group: %q", sgName) } - client := testAccProvider.Meta().(*ArmClient).secGroupClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityGroupClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, sgName) if err != nil { @@ -321,7 +320,7 @@ func testCheckAzureRMNetworkSecurityGroupDisappears(resourceName string) resourc } func testCheckAzureRMNetworkSecurityGroupDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).secGroupClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityGroupClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_network_security_rule.go b/azurerm/resource_arm_network_security_rule.go index 85078b73773c..99987ecfb11a 100644 --- a/azurerm/resource_arm_network_security_rule.go +++ b/azurerm/resource_arm_network_security_rule.go @@ -3,11 +3,14 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -50,7 +53,7 @@ func resourceArmNetworkSecurityRule() *schema.Resource { string(network.SecurityRuleProtocolTCP), string(network.SecurityRuleProtocolUDP), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "source_port_range": { @@ -109,6 +112,7 @@ func resourceArmNetworkSecurityRule() *schema.Resource { ConflictsWith: []string{"destination_address_prefix"}, }, + //lintignore:S018 "source_application_security_group_ids": { Type: schema.TypeSet, MaxItems: 1, @@ -117,6 +121,7 @@ func resourceArmNetworkSecurityRule() *schema.Resource { Set: schema.HashString, }, + //lintignore:S018 "destination_application_security_group_ids": { Type: schema.TypeSet, MaxItems: 1, @@ -132,7 +137,7 @@ func resourceArmNetworkSecurityRule() *schema.Resource { string(network.SecurityRuleAccessAllow), string(network.SecurityRuleAccessDeny), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "priority": { @@ -148,21 +153,21 @@ func resourceArmNetworkSecurityRule() *schema.Resource { string(network.SecurityRuleDirectionInbound), string(network.SecurityRuleDirectionOutbound), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, }, } } func resourceArmNetworkSecurityRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secRuleClient + client := meta.(*ArmClient).network.SecurityRuleClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) nsgName := d.Get("network_security_group_name").(string) resGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, nsgName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -184,8 +189,8 @@ func resourceArmNetworkSecurityRuleCreateUpdate(d *schema.ResourceData, meta int direction := d.Get("direction").(string) protocol := d.Get("protocol").(string) - azureRMLockByName(nsgName, networkSecurityGroupResourceName) - defer azureRMUnlockByName(nsgName, networkSecurityGroupResourceName) + locks.ByName(nsgName, networkSecurityGroupResourceName) + defer locks.UnlockByName(nsgName, networkSecurityGroupResourceName) rule := network.SecurityRule{ Name: &name, @@ -291,10 +296,10 @@ func resourceArmNetworkSecurityRuleCreateUpdate(d *schema.ResourceData, meta int } func resourceArmNetworkSecurityRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secRuleClient + client := meta.(*ArmClient).network.SecurityRuleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -343,10 +348,10 @@ func resourceArmNetworkSecurityRuleRead(d *schema.ResourceData, meta interface{} } func resourceArmNetworkSecurityRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).secRuleClient + client := meta.(*ArmClient).network.SecurityRuleClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -354,8 +359,8 @@ func resourceArmNetworkSecurityRuleDelete(d *schema.ResourceData, meta interface nsgName := id.Path["networkSecurityGroups"] sgRuleName := id.Path["securityRules"] - azureRMLockByName(nsgName, networkSecurityGroupResourceName) - defer azureRMUnlockByName(nsgName, networkSecurityGroupResourceName) + locks.ByName(nsgName, networkSecurityGroupResourceName) + defer locks.UnlockByName(nsgName, networkSecurityGroupResourceName) future, err := client.Delete(ctx, resGroup, nsgName, sgRuleName) if err != nil { diff --git a/azurerm/resource_arm_network_security_rule_test.go b/azurerm/resource_arm_network_security_rule_test.go index 9a6bfcf03f22..992cee81669d 100644 --- a/azurerm/resource_arm_network_security_rule_test.go +++ b/azurerm/resource_arm_network_security_rule_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) { } func TestAccAzureRMNetworkSecurityRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -158,7 +159,6 @@ func TestAccAzureRMNetworkSecurityRule_applicationSecurityGroups(t *testing.T) { func testCheckAzureRMNetworkSecurityRuleExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -171,7 +171,7 @@ func testCheckAzureRMNetworkSecurityRuleExists(resourceName string) resource.Tes return fmt.Errorf("Bad: no resource group found in state for network security rule: %q", sgName) } - client := testAccProvider.Meta().(*ArmClient).secRuleClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityRuleClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, sgName, sgrName) @@ -188,7 +188,6 @@ func testCheckAzureRMNetworkSecurityRuleExists(resourceName string) resource.Tes func testCheckAzureRMNetworkSecurityRuleDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %q", resourceName) @@ -201,7 +200,7 @@ func testCheckAzureRMNetworkSecurityRuleDisappears(resourceName string) resource return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName) } - client := testAccProvider.Meta().(*ArmClient).secRuleClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityRuleClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, sgName, sgrName) if err != nil { @@ -215,11 +214,10 @@ func testCheckAzureRMNetworkSecurityRuleDisappears(resourceName string) resource } func testCheckAzureRMNetworkSecurityRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).secRuleClient + client := testAccProvider.Meta().(*ArmClient).network.SecurityRuleClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_network_security_rule" { continue } diff --git a/azurerm/resource_arm_network_watcher.go b/azurerm/resource_arm_network_watcher.go index 6e874ba04f69..c9ba4e9e024b 100644 --- a/azurerm/resource_arm_network_watcher.go +++ b/azurerm/resource_arm_network_watcher.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,19 +34,19 @@ func resourceArmNetworkWatcher() *schema.Resource { "location": azure.SchemaLocation(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmNetworkWatcherCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).watcherClient + client := meta.(*ArmClient).network.WatcherClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -58,11 +60,11 @@ func resourceArmNetworkWatcherCreateUpdate(d *schema.ResourceData, meta interfac } location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) watcher := network.Watcher{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, watcher); err != nil { @@ -83,10 +85,10 @@ func resourceArmNetworkWatcherCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).watcherClient + client := meta.(*ArmClient).network.WatcherClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -108,16 +110,14 @@ func resourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) err d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmNetworkWatcherDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).watcherClient + client := meta.(*ArmClient).network.WatcherClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go index 91d202bda933..8527e5441f9a 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -103,7 +104,7 @@ func testAccAzureRMNetworkWatcher_basic(t *testing.T) { } func testAccAzureRMNetworkWatcher_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -201,7 +202,6 @@ func testAccAzureRMNetworkWatcher_disappears(t *testing.T) { func testCheckAzureRMNetworkWatcherExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -213,7 +213,7 @@ func testCheckAzureRMNetworkWatcherExists(resourceName string) resource.TestChec return fmt.Errorf("Bad: no resource group found in state for Network Watcher: %q", name) } - client := testAccProvider.Meta().(*ArmClient).watcherClient + client := testAccProvider.Meta().(*ArmClient).network.WatcherClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -230,7 +230,6 @@ func testCheckAzureRMNetworkWatcherExists(resourceName string) resource.TestChec func testCheckAzureRMNetworkWatcherDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %q", resourceName) @@ -242,7 +241,7 @@ func testCheckAzureRMNetworkWatcherDisappears(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Network Watcher: %q", name) } - client := testAccProvider.Meta().(*ArmClient).watcherClient + client := testAccProvider.Meta().(*ArmClient).network.WatcherClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -261,7 +260,6 @@ func testCheckAzureRMNetworkWatcherDisappears(resourceName string) resource.Test func testCheckAzureRMNetworkWatcherDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_network_watcher" { continue } @@ -269,7 +267,7 @@ func testCheckAzureRMNetworkWatcherDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).watcherClient + client := testAccProvider.Meta().(*ArmClient).network.WatcherClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_notification_hub.go b/azurerm/resource_arm_notification_hub.go index b6c06e562a26..0828c008e5a3 100644 --- a/azurerm/resource_arm_notification_hub.go +++ b/azurerm/resource_arm_notification_hub.go @@ -13,9 +13,12 @@ import ( "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +var notificationHubResourceName = "azurerm_notification_hub" + const apnsProductionName = "Production" const apnsProductionEndpoint = "https://api.push.apple.com:443/3/device" const apnsSandboxName = "Sandbox" @@ -124,7 +127,7 @@ func resourceArmNotificationHub() *schema.Resource { // NOTE: skipping tags as there's a bug in the API where the Keys for Tags are returned in lower-case // Azure Rest API Specs issue: https://github.com/Azure/azure-sdk-for-go/issues/2239 - //"tags": tagsSchema(), + //"tags": tags.Schema(), }, } } @@ -138,7 +141,7 @@ func resourceArmNotificationHubCreateUpdate(d *schema.ResourceData, meta interfa resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -203,7 +206,7 @@ func resourceArmNotificationHubCreateUpdate(d *schema.ResourceData, meta interfa return resourceArmNotificationHubRead(d, meta) } -func notificationHubStateRefreshFunc(ctx context.Context, client notificationhubs.Client, resourceGroup, namespaceName, name string) resource.StateRefreshFunc { +func notificationHubStateRefreshFunc(ctx context.Context, client *notificationhubs.Client, resourceGroup, namespaceName, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroup, namespaceName, name) if err != nil { @@ -222,7 +225,7 @@ func resourceArmNotificationHubRead(d *schema.ResourceData, meta interface{}) er client := meta.(*ArmClient).notificationHubs.HubsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -272,7 +275,7 @@ func resourceArmNotificationHubDelete(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).notificationHubs.HubsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_notification_hub_authorization_rule.go b/azurerm/resource_arm_notification_hub_authorization_rule.go index e7401444e162..4c7a8c7ead36 100644 --- a/azurerm/resource_arm_notification_hub_authorization_rule.go +++ b/azurerm/resource_arm_notification_hub_authorization_rule.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -87,7 +89,7 @@ func resourceArmNotificationHubAuthorizationRuleCreateUpdate(d *schema.ResourceD send := d.Get("send").(bool) listen := d.Get("listen").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, notificationHubName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -100,6 +102,12 @@ func resourceArmNotificationHubAuthorizationRuleCreateUpdate(d *schema.ResourceD } } + locks.ByName(notificationHubName, notificationHubResourceName) + defer locks.UnlockByName(notificationHubName, notificationHubResourceName) + + locks.ByName(namespaceName, notificationHubNamespaceResourceName) + defer locks.UnlockByName(namespaceName, notificationHubNamespaceResourceName) + parameters := notificationhubs.SharedAccessAuthorizationRuleCreateOrUpdateParameters{ Properties: ¬ificationhubs.SharedAccessAuthorizationRuleProperties{ Rights: expandNotificationHubAuthorizationRuleRights(manage, send, listen), @@ -127,7 +135,7 @@ func resourceArmNotificationHubAuthorizationRuleRead(d *schema.ResourceData, met client := meta.(*ArmClient).notificationHubs.HubsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -174,7 +182,7 @@ func resourceArmNotificationHubAuthorizationRuleDelete(d *schema.ResourceData, m client := meta.(*ArmClient).notificationHubs.HubsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -183,6 +191,12 @@ func resourceArmNotificationHubAuthorizationRuleDelete(d *schema.ResourceData, m notificationHubName := id.Path["notificationHubs"] name := id.Path["AuthorizationRules"] + locks.ByName(notificationHubName, notificationHubResourceName) + defer locks.UnlockByName(notificationHubName, notificationHubResourceName) + + locks.ByName(namespaceName, notificationHubNamespaceResourceName) + defer locks.UnlockByName(namespaceName, notificationHubNamespaceResourceName) + resp, err := client.DeleteAuthorizationRule(ctx, resourceGroup, namespaceName, notificationHubName, name) if err != nil { if !utils.ResponseWasNotFound(resp) { diff --git a/azurerm/resource_arm_notification_hub_authorization_rule_test.go b/azurerm/resource_arm_notification_hub_authorization_rule_test.go index e2bb7670d9b9..b78cda4a81b3 100644 --- a/azurerm/resource_arm_notification_hub_authorization_rule_test.go +++ b/azurerm/resource_arm_notification_hub_authorization_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNotificationHubAuthorizationRule_listen(t *testing.T) { @@ -40,7 +41,7 @@ func TestAccAzureRMNotificationHubAuthorizationRule_listen(t *testing.T) { } func TestAccAzureRMNotificationHubAuthorizationRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -134,6 +135,61 @@ func TestAccAzureRMNotificationHubAuthorizationRule_send(t *testing.T) { }) } +func TestAccAzureRMNotificationHubAuthorizationRule_multi(t *testing.T) { + resourceOneName := "azurerm_notification_hub_authorization_rule.test1" + resourceTwoName := "azurerm_notification_hub_authorization_rule.test2" + resourceThreeName := "azurerm_notification_hub_authorization_rule.test3" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNotificationHubAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAzureRMNotificationHubAuthorizationRule_multi(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNotificationHubAuthorizationRuleExists(resourceOneName), + resource.TestCheckResourceAttr(resourceOneName, "manage", "false"), + resource.TestCheckResourceAttr(resourceOneName, "send", "true"), + resource.TestCheckResourceAttr(resourceOneName, "listen", "true"), + resource.TestCheckResourceAttrSet(resourceOneName, "primary_access_key"), + resource.TestCheckResourceAttrSet(resourceOneName, "secondary_access_key"), + testCheckAzureRMNotificationHubAuthorizationRuleExists(resourceTwoName), + resource.TestCheckResourceAttr(resourceTwoName, "manage", "false"), + resource.TestCheckResourceAttr(resourceTwoName, "send", "true"), + resource.TestCheckResourceAttr(resourceTwoName, "listen", "true"), + resource.TestCheckResourceAttrSet(resourceTwoName, "primary_access_key"), + resource.TestCheckResourceAttrSet(resourceTwoName, "secondary_access_key"), + testCheckAzureRMNotificationHubAuthorizationRuleExists(resourceThreeName), + resource.TestCheckResourceAttr(resourceThreeName, "manage", "false"), + resource.TestCheckResourceAttr(resourceThreeName, "send", "true"), + resource.TestCheckResourceAttr(resourceThreeName, "listen", "true"), + resource.TestCheckResourceAttrSet(resourceThreeName, "primary_access_key"), + resource.TestCheckResourceAttrSet(resourceThreeName, "secondary_access_key"), + ), + }, + { + ResourceName: resourceOneName, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceTwoName, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceThreeName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMNotificationHubAuthorizationRule_updated(t *testing.T) { resourceName := "azurerm_notification_hub_authorization_rule.test" @@ -271,6 +327,41 @@ resource "azurerm_notification_hub_authorization_rule" "test" { `, template, ri) } +func testAzureRMNotificationHubAuthorizationRule_multi(ri int, location string) string { + template := testAzureRMNotificationHubAuthorizationRule_template(ri, location) + return fmt.Sprintf(` +%s + +resource "azurerm_notification_hub_authorization_rule" "test1" { + name = "acctestruleone-%d" + notification_hub_name = "${azurerm_notification_hub.test.name}" + namespace_name = "${azurerm_notification_hub_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + send = true + listen = true +} + +resource "azurerm_notification_hub_authorization_rule" "test2" { + name = "acctestruletwo-%d" + notification_hub_name = "${azurerm_notification_hub.test.name}" + namespace_name = "${azurerm_notification_hub_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + send = true + listen = true +} + +resource "azurerm_notification_hub_authorization_rule" "test3" { + name = "acctestrulethree-%d" + notification_hub_name = "${azurerm_notification_hub.test.name}" + namespace_name = "${azurerm_notification_hub_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + send = true + listen = true +} + +`, template, ri, ri, ri) +} + func testAzureRMNotificationHubAuthorizationRule_manage(ri int, location string) string { template := testAzureRMNotificationHubAuthorizationRule_template(ri, location) return fmt.Sprintf(` diff --git a/azurerm/resource_arm_notification_hub_namespace.go b/azurerm/resource_arm_notification_hub_namespace.go index 7ca7caa9d3dd..3f81197b22d2 100644 --- a/azurerm/resource_arm_notification_hub_namespace.go +++ b/azurerm/resource_arm_notification_hub_namespace.go @@ -13,10 +13,14 @@ import ( "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +var notificationHubNamespaceResourceName = "azurerm_notification_hub_namespace" + func resourceArmNotificationHubNamespace() *schema.Resource { return &schema.Resource{ Create: resourceArmNotificationHubNamespaceCreateUpdate, @@ -40,9 +44,12 @@ func resourceArmNotificationHubNamespace() *schema.Resource { "location": azure.SchemaLocation(), "sku": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "This property has been deprecated in favour of the 'sku_name' property and will be removed in version 2.0 of the provider", + ConflictsWith: []string{"sku_name"}, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -59,6 +66,19 @@ func resourceArmNotificationHubNamespace() *schema.Resource { }, }, + "sku_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"sku"}, + ValidateFunc: validation.StringInSlice([]string{ + string(notificationhubs.Basic), + string(notificationhubs.Free), + string(notificationhubs.Standard), + }, false), + }, + "enabled": { Type: schema.TypeBool, Optional: true, @@ -72,12 +92,12 @@ func resourceArmNotificationHubNamespace() *schema.Resource { string(notificationhubs.Messaging), string(notificationhubs.NotificationHub), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, // NOTE: skipping tags as there's a bug in the API where the Keys for Tags are returned in lower-case // Azure Rest API Specs issue: https://github.com/Azure/azure-sdk-for-go/issues/2239 - //"tags": tagsSchema(), + //"tags": tags.Schema(), "servicebus_endpoint": { Type: schema.TypeString, @@ -91,13 +111,34 @@ func resourceArmNotificationHubNamespaceCreateUpdate(d *schema.ResourceData, met client := meta.(*ArmClient).notificationHubs.NamespacesClient ctx := meta.(*ArmClient).StopContext + // Remove in 2.0 + var sku notificationhubs.Sku + + if inputs := d.Get("sku").([]interface{}); len(inputs) != 0 { + input := inputs[0].(map[string]interface{}) + v := input["name"].(string) + + sku = notificationhubs.Sku{ + Name: notificationhubs.SkuName(v), + } + } else { + // Keep in 2.0 + sku = notificationhubs.Sku{ + Name: notificationhubs.SkuName(d.Get("sku_name").(string)), + } + } + + if sku.Name == "" { + return fmt.Errorf("either 'sku_name' or 'sku' must be defined in the configuration file") + } + name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) namespaceType := d.Get("namespace_type").(string) enabled := d.Get("enabled").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -112,7 +153,7 @@ func resourceArmNotificationHubNamespaceCreateUpdate(d *schema.ResourceData, met parameters := notificationhubs.NamespaceCreateOrUpdateParameters{ Location: utils.String(location), - Sku: expandNotificationHubNamespacesSku(d.Get("sku").([]interface{})), + Sku: &sku, NamespaceProperties: ¬ificationhubs.NamespaceProperties{ Region: utils.String(location), NamespaceType: notificationhubs.NamespaceType(namespaceType), @@ -153,7 +194,7 @@ func resourceArmNotificationHubNamespaceRead(d *schema.ResourceData, meta interf client := meta.(*ArmClient).notificationHubs.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -177,9 +218,17 @@ func resourceArmNotificationHubNamespaceRead(d *schema.ResourceData, meta interf d.Set("location", azure.NormalizeLocation(*location)) } - sku := flattenNotificationHubNamespacesSku(resp.Sku) - if err := d.Set("sku", sku); err != nil { - return fmt.Errorf("Error setting `sku`: %+v", err) + if sku := resp.Sku; sku != nil { + // Remove in 2.0 + if err := d.Set("sku", flattenNotificationHubNamespacesSku(sku)); err != nil { + return fmt.Errorf("Error setting 'sku': %+v", err) + } + + if err := d.Set("sku_name", string(sku.Name)); err != nil { + return fmt.Errorf("Error setting 'sku_name': %+v", err) + } + } else { + return fmt.Errorf("Error making Read request on Notification Hub Namespace %q (Resource Group %q): Unable to retrieve 'sku' value", name, resourceGroup) } if props := resp.NamespaceProperties; props != nil { @@ -195,7 +244,7 @@ func resourceArmNotificationHubNamespaceDelete(d *schema.ResourceData, meta inte client := meta.(*ArmClient).notificationHubs.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -225,16 +274,7 @@ func resourceArmNotificationHubNamespaceDelete(d *schema.ResourceData, meta inte return nil } -func expandNotificationHubNamespacesSku(input []interface{}) *notificationhubs.Sku { - v := input[0].(map[string]interface{}) - - skuName := v["name"].(string) - - return ¬ificationhubs.Sku{ - Name: notificationhubs.SkuName(skuName), - } -} - +// Remove in 2.0 func flattenNotificationHubNamespacesSku(input *notificationhubs.Sku) []interface{} { outputs := make([]interface{}, 0) if input == nil { @@ -248,7 +288,7 @@ func flattenNotificationHubNamespacesSku(input *notificationhubs.Sku) []interfac return outputs } -func notificationHubNamespaceStateRefreshFunc(ctx context.Context, client notificationhubs.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { +func notificationHubNamespaceStateRefreshFunc(ctx context.Context, client *notificationhubs.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroupName, name) if err != nil { @@ -263,7 +303,7 @@ func notificationHubNamespaceStateRefreshFunc(ctx context.Context, client notifi } } -func notificationHubNamespaceDeleteStateRefreshFunc(ctx context.Context, client notificationhubs.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { +func notificationHubNamespaceDeleteStateRefreshFunc(ctx context.Context, client *notificationhubs.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroupName, name) if err != nil { diff --git a/azurerm/resource_arm_notification_hub_namespace_test.go b/azurerm/resource_arm_notification_hub_namespace_test.go index 716ad4835ab0..72d3c2d98eaa 100644 --- a/azurerm/resource_arm_notification_hub_namespace_test.go +++ b/azurerm/resource_arm_notification_hub_namespace_test.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" "net/http" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNotificationHubNamespace_free(t *testing.T) { @@ -34,8 +36,50 @@ func TestAccAzureRMNotificationHubNamespace_free(t *testing.T) { }) } +// Remove in 2.0 +func TestAccAzureRMNotificationHubNamespace_freeClassic(t *testing.T) { + resourceName := "azurerm_notification_hub_namespace.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNotificationHubNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNotificationHubNamespace_freeClassic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNotificationHubNamespaceExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMNotificationHubNamespace_freeNotDefined(t *testing.T) { + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNotificationHubNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNotificationHubNamespace_freeNotDefined(ri, testLocation()), + ExpectError: regexp.MustCompile("either 'sku_name' or 'sku' must be defined in the configuration file"), + }, + }, + }) +} + func TestAccAzureRMNotificationHubNamespace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -120,6 +164,24 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_notification_hub_namespace" "test" { + name = "acctestnhn-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + namespace_type = "NotificationHub" + + sku_name = "Free" +} +`, ri, location, ri) +} + +func testAccAzureRMNotificationHubNamespace_freeClassic(ri int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_notification_hub_namespace" "test" { name = "acctestnhn-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -133,6 +195,22 @@ resource "azurerm_notification_hub_namespace" "test" { `, ri, location, ri) } +func testAccAzureRMNotificationHubNamespace_freeNotDefined(ri int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_notification_hub_namespace" "test" { + name = "acctestnhn-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + namespace_type = "NotificationHub" +} +`, ri, location, ri) +} + func testAccAzureRMNotificationHubNamespace_requiresImport(ri int, location string) string { return fmt.Sprintf(` %s @@ -143,9 +221,7 @@ resource "azurerm_notification_hub_namespace" "import" { location = "${azurerm_notification_hub_namespace.test.location}" namespace_type = "${azurerm_notification_hub_namespace.test.namespace_type}" - sku { - name = "Free" - } + sku_name = "Free" } `, testAccAzureRMNotificationHubNamespace_free(ri, location)) } diff --git a/azurerm/resource_arm_notification_hub_test.go b/azurerm/resource_arm_notification_hub_test.go index 1e03cf39be09..1055b8730e72 100644 --- a/azurerm/resource_arm_notification_hub_test.go +++ b/azurerm/resource_arm_notification_hub_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMNotificationHub_basic(t *testing.T) { @@ -37,7 +38,7 @@ func TestAccAzureRMNotificationHub_basic(t *testing.T) { } func TestAccAzureRMNotificationHub_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_packet_capture.go b/azurerm/resource_arm_packet_capture.go index b57d0a0c536d..ad81e59ce236 100644 --- a/azurerm/resource_arm_packet_capture.go +++ b/azurerm/resource_arm_packet_capture.go @@ -5,10 +5,11 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -134,7 +135,7 @@ func resourceArmPacketCapture() *schema.Resource { } func resourceArmPacketCaptureCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -146,7 +147,7 @@ func resourceArmPacketCaptureCreate(d *schema.ResourceData, meta interface{}) er totalBytesPerSession := d.Get("maximum_bytes_per_session").(int) timeLimitInSeconds := d.Get("maximum_capture_duration").(int) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, watcherName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -200,10 +201,10 @@ func resourceArmPacketCaptureCreate(d *schema.ResourceData, meta interface{}) er } func resourceArmPacketCaptureRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -248,10 +249,10 @@ func resourceArmPacketCaptureRead(d *schema.ResourceData, meta interface{}) erro } func resourceArmPacketCaptureDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).packetCapturesClient + client := meta.(*ArmClient).network.PacketCapturesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_packet_capture_test.go b/azurerm/resource_arm_packet_capture_test.go index 85d232306a31..c586c17e748d 100644 --- a/azurerm/resource_arm_packet_capture_test.go +++ b/azurerm/resource_arm_packet_capture_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func testAccAzureRMPacketCapture_localDisk(t *testing.T) { @@ -38,7 +39,7 @@ func testAccAzureRMPacketCapture_localDisk(t *testing.T) { } func testAccAzureRMPacketCapture_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -157,7 +158,7 @@ func testCheckAzureRMPacketCaptureExists(resourceName string) resource.TestCheck watcherName := rs.Primary.Attributes["network_watcher_name"] packetCaptureName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + client := testAccProvider.Meta().(*ArmClient).network.PacketCapturesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, watcherName, packetCaptureName) @@ -174,7 +175,7 @@ func testCheckAzureRMPacketCaptureExists(resourceName string) resource.TestCheck } func testCheckAzureRMPacketCaptureDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + client := testAccProvider.Meta().(*ArmClient).network.PacketCapturesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_policy_assignment.go b/azurerm/resource_arm_policy_assignment.go index 94a47af1d0b9..5ccf2db1cfaf 100644 --- a/azurerm/resource_arm_policy_assignment.go +++ b/azurerm/resource_arm_policy_assignment.go @@ -6,6 +6,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "time" @@ -22,8 +23,8 @@ import ( func resourceArmPolicyAssignment() *schema.Resource { return &schema.Resource{ - Create: resourceArmPolicyAssignmentCreateOrUpdate, - Update: resourceArmPolicyAssignmentCreateOrUpdate, + Create: resourceArmPolicyAssignmentCreateUpdate, + Update: resourceArmPolicyAssignmentCreateUpdate, Read: resourceArmPolicyAssignmentRead, Delete: resourceArmPolicyAssignmentDelete, Importer: &schema.ResourceImporter{ @@ -106,7 +107,7 @@ func resourceArmPolicyAssignment() *schema.Resource { } } -func resourceArmPolicyAssignmentCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceArmPolicyAssignmentCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).policy.AssignmentsClient ctx := meta.(*ArmClient).StopContext @@ -116,7 +117,7 @@ func resourceArmPolicyAssignmentCreateOrUpdate(d *schema.ResourceData, meta inte policyDefinitionId := d.Get("policy_definition_id").(string) displayName := d.Get("display_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, scope, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -260,7 +261,7 @@ func resourceArmPolicyAssignmentDelete(d *schema.ResourceData, meta interface{}) return nil } -func policyAssignmentRefreshFunc(ctx context.Context, client policy.AssignmentsClient, scope string, name string) resource.StateRefreshFunc { +func policyAssignmentRefreshFunc(ctx context.Context, client *policy.AssignmentsClient, scope string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, scope, name) if err != nil { diff --git a/azurerm/resource_arm_policy_assignment_test.go b/azurerm/resource_arm_policy_assignment_test.go index b5021640e599..241ad6f2ca58 100644 --- a/azurerm/resource_arm_policy_assignment_test.go +++ b/azurerm/resource_arm_policy_assignment_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMPolicyAssignment_basic(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMPolicyAssignment_basic(t *testing.T) { } func TestAccAzureRMPolicyAssignment_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -303,9 +304,11 @@ resource "azurerm_policy_assignment" "test" { name = "acctestpa-%d" scope = "${azurerm_resource_group.test.id}" policy_definition_id = "${azurerm_policy_definition.test.id}" + identity { type = "SystemAssigned" } + location = "%s" } `, ri, ri, ri, location, ri, location) diff --git a/azurerm/resource_arm_policy_definition.go b/azurerm/resource_arm_policy_definition.go index 4fd8c2a0a735..68c5a6bd3e04 100644 --- a/azurerm/resource_arm_policy_definition.go +++ b/azurerm/resource_arm_policy_definition.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "time" @@ -112,7 +113,7 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf description := d.Get("description").(string) managementGroupID := d.Get("management_group_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := getPolicyDefinition(ctx, client, name, managementGroupID) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -295,7 +296,7 @@ func parseManagementGroupIdFromPolicyId(id string) string { return "" } -func policyDefinitionRefreshFunc(ctx context.Context, client policy.DefinitionsClient, name string, managementGroupID string) resource.StateRefreshFunc { +func policyDefinitionRefreshFunc(ctx context.Context, client *policy.DefinitionsClient, name string, managementGroupID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := getPolicyDefinition(ctx, client, name, managementGroupID) @@ -322,7 +323,7 @@ func resourceArmPolicyDefinitionImport(d *schema.ResourceData, meta interface{}) return schema.ImportStatePassthrough(d, meta) } -func getPolicyDefinition(ctx context.Context, client policy.DefinitionsClient, name string, managementGroupID string) (res policy.Definition, err error) { +func getPolicyDefinition(ctx context.Context, client *policy.DefinitionsClient, name string, managementGroupID string) (res policy.Definition, err error) { if managementGroupID == "" { res, err = client.Get(ctx, name) } else { diff --git a/azurerm/resource_arm_policy_definition_test.go b/azurerm/resource_arm_policy_definition_test.go index bd03c8675772..12743c5cc9e8 100644 --- a/azurerm/resource_arm_policy_definition_test.go +++ b/azurerm/resource_arm_policy_definition_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMPolicyDefinition_basic(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMPolicyDefinition_basic(t *testing.T) { } func TestAccAzureRMPolicyDefinition_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -89,7 +90,6 @@ func TestAccAzureRMPolicyDefinition_computedMetadata(t *testing.T) { func TestAccAzureRMPolicyDefinitionAtMgmtGroup_basic(t *testing.T) { resourceName := "azurerm_policy_definition.test" - mgmtGroupName := "azurerm_management_group.test" ri := tf.AccRandTimeInt() @@ -101,7 +101,7 @@ func TestAccAzureRMPolicyDefinitionAtMgmtGroup_basic(t *testing.T) { { Config: testAzureRMPolicyDefinition_ManagementGroup(ri), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMPolicyDefinitionExistsInMgmtGroup(resourceName, mgmtGroupName), + testCheckAzureRMPolicyDefinitionExistsInMgmtGroup(resourceName), ), }, { @@ -113,7 +113,7 @@ func TestAccAzureRMPolicyDefinitionAtMgmtGroup_basic(t *testing.T) { }) } -func testCheckAzureRMPolicyDefinitionExistsInMgmtGroup(policyName string, managementGroupName string) resource.TestCheckFunc { +func testCheckAzureRMPolicyDefinitionExistsInMgmtGroup(policyName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[policyName] if !ok { diff --git a/azurerm/resource_arm_policy_set_definition.go b/azurerm/resource_arm_policy_set_definition.go index 1399ec60ea42..baf76d3fbc63 100644 --- a/azurerm/resource_arm_policy_set_definition.go +++ b/azurerm/resource_arm_policy_set_definition.go @@ -12,6 +12,7 @@ import ( "time" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/go-autorest/autorest" @@ -119,7 +120,7 @@ func resourceArmPolicySetDefinitionCreateUpdate(d *schema.ResourceData, meta int description := d.Get("description").(string) managementGroupID := d.Get("management_group_id").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := getPolicySetDefinition(ctx, client, name, managementGroupID) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -320,7 +321,7 @@ func parseManagementGroupIdFromPolicySetId(id string) string { return "" } -func policySetDefinitionRefreshFunc(ctx context.Context, client policy.SetDefinitionsClient, name string, managementGroupId string) resource.StateRefreshFunc { +func policySetDefinitionRefreshFunc(ctx context.Context, client *policy.SetDefinitionsClient, name string, managementGroupId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := getPolicySetDefinition(ctx, client, name, managementGroupId) if err != nil { @@ -331,7 +332,7 @@ func policySetDefinitionRefreshFunc(ctx context.Context, client policy.SetDefini } } -func getPolicySetDefinition(ctx context.Context, client policy.SetDefinitionsClient, name string, managementGroupID string) (res policy.SetDefinition, err error) { +func getPolicySetDefinition(ctx context.Context, client *policy.SetDefinitionsClient, name string, managementGroupID string) (res policy.SetDefinition, err error) { if managementGroupID == "" { res, err = client.Get(ctx, name) } else { diff --git a/azurerm/resource_arm_policy_set_definition_test.go b/azurerm/resource_arm_policy_set_definition_test.go index c0f07ef79be3..ffca463e61ed 100644 --- a/azurerm/resource_arm_policy_set_definition_test.go +++ b/azurerm/resource_arm_policy_set_definition_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" "github.com/hashicorp/terraform/terraform" @@ -39,7 +40,7 @@ func TestAccAzureRMPolicySetDefinition_builtIn(t *testing.T) { } func TestAccAzureRMPolicySetDefinition_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -239,7 +240,7 @@ POLICY_DEFINITIONS func testAzureRMPolicySetDefinition_ManagementGroup(ri int) string { return fmt.Sprintf(` resource "azurerm_management_group" "test" { - display_name = "acctestmg-%d" + display_name = "acctestmg-%d" } resource "azurerm_policy_set_definition" "test" { diff --git a/azurerm/resource_arm_postgresql_configuration.go b/azurerm/resource_arm_postgresql_configuration.go index 2da4b0528158..6fc7d33d570a 100644 --- a/azurerm/resource_arm_postgresql_configuration.go +++ b/azurerm/resource_arm_postgresql_configuration.go @@ -45,7 +45,7 @@ func resourceArmPostgreSQLConfiguration() *schema.Resource { } func resourceArmPostgreSQLConfigurationCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlConfigurationsClient + client := meta.(*ArmClient).postgres.ConfigurationsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Configuration creation.") @@ -84,10 +84,10 @@ func resourceArmPostgreSQLConfigurationCreateUpdate(d *schema.ResourceData, meta } func resourceArmPostgreSQLConfigurationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlConfigurationsClient + client := meta.(*ArmClient).postgres.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -115,10 +115,10 @@ func resourceArmPostgreSQLConfigurationRead(d *schema.ResourceData, meta interfa } func resourceArmPostgreSQLConfigurationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlConfigurationsClient + client := meta.(*ArmClient).postgres.ConfigurationsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_postgresql_configuration_test.go b/azurerm/resource_arm_postgresql_configuration_test.go index babcef200e85..8442672a0af2 100644 --- a/azurerm/resource_arm_postgresql_configuration_test.go +++ b/azurerm/resource_arm_postgresql_configuration_test.go @@ -124,7 +124,7 @@ func testCheckAzureRMPostgreSQLConfigurationValue(resourceName string, value str return fmt.Errorf("Bad: no resource group found in state for PostgreSQL Configuration: %s", name) } - client := testAccProvider.Meta().(*ArmClient).postgresqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).postgres.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -146,11 +146,10 @@ func testCheckAzureRMPostgreSQLConfigurationValue(resourceName string, value str func testCheckAzureRMPostgreSQLConfigurationValueReset(rInt int, configurationName string) resource.TestCheckFunc { return func(s *terraform.State) error { - resourceGroup := fmt.Sprintf("acctestRG-%d", rInt) serverName := fmt.Sprintf("acctestpsqlsvr-%d", rInt) - client := testAccProvider.Meta().(*ArmClient).postgresqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).postgres.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, configurationName) @@ -173,7 +172,7 @@ func testCheckAzureRMPostgreSQLConfigurationValueReset(rInt int, configurationNa } func testCheckAzureRMPostgreSQLConfigurationDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).postgresqlConfigurationsClient + client := testAccProvider.Meta().(*ArmClient).postgres.ConfigurationsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -237,7 +236,7 @@ resource "azurerm_postgresql_server" "test" { resource_group_name = "${azurerm_resource_group.test.name}" sku { - name = "GP_Gen5_2" + name = "GP_Gen5_2" capacity = 2 tier = "GeneralPurpose" family = "Gen5" diff --git a/azurerm/resource_arm_postgresql_database.go b/azurerm/resource_arm_postgresql_database.go index 01381048a2c6..c036fa35d16d 100644 --- a/azurerm/resource_arm_postgresql_database.go +++ b/azurerm/resource_arm_postgresql_database.go @@ -6,7 +6,10 @@ import ( "strings" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" "github.com/hashicorp/terraform/helper/schema" @@ -41,7 +44,7 @@ func resourceArmPostgreSQLDatabase() *schema.Resource { "charset": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ForceNew: true, }, @@ -49,14 +52,14 @@ func resourceArmPostgreSQLDatabase() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateCollation(), + ValidateFunc: validate.DatabaseCollation, }, }, } } func resourceArmPostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlDatabasesClient + client := meta.(*ArmClient).postgres.DatabasesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Database creation.") @@ -68,7 +71,7 @@ func resourceArmPostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{ charset := d.Get("charset").(string) collation := d.Get("collation").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -111,10 +114,10 @@ func resourceArmPostgreSQLDatabaseCreate(d *schema.ResourceData, meta interface{ } func resourceArmPostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlDatabasesClient + client := meta.(*ArmClient).postgres.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -150,10 +153,10 @@ func resourceArmPostgreSQLDatabaseRead(d *schema.ResourceData, meta interface{}) } func resourceArmPostgreSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlDatabasesClient + client := meta.(*ArmClient).postgres.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_postgresql_database_test.go b/azurerm/resource_arm_postgresql_database_test.go index 27fb0420703b..03c0f756fa67 100644 --- a/azurerm/resource_arm_postgresql_database_test.go +++ b/azurerm/resource_arm_postgresql_database_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -32,7 +33,7 @@ func TestAccAzureRMPostgreSQLDatabase_basic(t *testing.T) { } func TestAccAzureRMPostgreSQLDatabase_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -118,7 +119,7 @@ func testCheckAzureRMPostgreSQLDatabaseExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for PostgreSQL Database: %s", name) } - client := testAccProvider.Meta().(*ArmClient).postgresqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).postgres.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -134,7 +135,7 @@ func testCheckAzureRMPostgreSQLDatabaseExists(resourceName string) resource.Test } func testCheckAzureRMPostgreSQLDatabaseDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).postgresqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).postgres.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_postgresql_firewall_rule.go b/azurerm/resource_arm_postgresql_firewall_rule.go index e33e7884775f..6c18478345e8 100644 --- a/azurerm/resource_arm_postgresql_firewall_rule.go +++ b/azurerm/resource_arm_postgresql_firewall_rule.go @@ -6,6 +6,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" "github.com/hashicorp/terraform/helper/schema" @@ -53,7 +54,7 @@ func resourceArmPostgreSQLFirewallRule() *schema.Resource { } func resourceArmPostgreSQLFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlFirewallRulesClient + client := meta.(*ArmClient).postgres.FirewallRulesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Firewall Rule creation.") @@ -64,7 +65,7 @@ func resourceArmPostgreSQLFirewallRuleCreate(d *schema.ResourceData, meta interf startIPAddress := d.Get("start_ip_address").(string) endIPAddress := d.Get("end_ip_address").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -107,10 +108,10 @@ func resourceArmPostgreSQLFirewallRuleCreate(d *schema.ResourceData, meta interf } func resourceArmPostgreSQLFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlFirewallRulesClient + client := meta.(*ArmClient).postgres.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -139,10 +140,10 @@ func resourceArmPostgreSQLFirewallRuleRead(d *schema.ResourceData, meta interfac } func resourceArmPostgreSQLFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlFirewallRulesClient + client := meta.(*ArmClient).postgres.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_postgresql_firewall_rule_test.go b/azurerm/resource_arm_postgresql_firewall_rule_test.go index f5753b93de33..41f99bc424da 100644 --- a/azurerm/resource_arm_postgresql_firewall_rule_test.go +++ b/azurerm/resource_arm_postgresql_firewall_rule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMPostgreSQLFirewallRule_basic(t *testing.T) { } func TestAccAzureRMPostgreSQLFirewallRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -81,7 +82,7 @@ func testCheckAzureRMPostgreSQLFirewallRuleExists(resourceName string) resource. return fmt.Errorf("Bad: no resource group found in state for PostgreSQL Firewall Rule: %s", name) } - client := testAccProvider.Meta().(*ArmClient).postgresqlFirewallRulesClient + client := testAccProvider.Meta().(*ArmClient).postgres.FirewallRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, name) @@ -98,7 +99,7 @@ func testCheckAzureRMPostgreSQLFirewallRuleExists(resourceName string) resource. } func testCheckAzureRMPostgreSQLFirewallRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).postgresqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).postgres.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_postgresql_server.go b/azurerm/resource_arm_postgresql_server.go index a37418622ef8..b346b7b1520b 100644 --- a/azurerm/resource_arm_postgresql_server.go +++ b/azurerm/resource_arm_postgresql_server.go @@ -7,6 +7,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" @@ -130,9 +132,9 @@ func resourceArmPostgreSQLServer() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(postgresql.NineFullStopFive), string(postgresql.NineFullStopSix), + string(postgresql.OneOne), string(postgresql.OneZero), string(postgresql.OneZeroFullStopZero), - string(postgresql.OneZeroFullStopTwo), }, true), DiffSuppressFunc: suppress.CaseDifference, }, @@ -164,6 +166,15 @@ func resourceArmPostgreSQLServer() *schema.Resource { }, true), DiffSuppressFunc: suppress.CaseDifference, }, + "auto_grow": { + Type: schema.TypeString, + Optional: true, + Default: string(postgresql.StorageAutogrowEnabled), + ValidateFunc: validation.StringInSlice([]string{ + string(postgresql.StorageAutogrowEnabled), + string(postgresql.StorageAutogrowDisabled), + }, false), + }, }, }, }, @@ -183,11 +194,10 @@ func resourceArmPostgreSQLServer() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - tier, _ := diff.GetOk("sku.0.tier") storageMB, _ := diff.GetOk("storage_profile.0.storage_mb") @@ -201,7 +211,7 @@ func resourceArmPostgreSQLServer() *schema.Resource { } func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlServersClient + client := meta.(*ArmClient).postgres.ServersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Server creation.") @@ -215,9 +225,9 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) sslEnforcement := d.Get("ssl_enforcement").(string) version := d.Get("version").(string) createMode := "Default" - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -244,7 +254,7 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) CreateMode: postgresql.CreateMode(createMode), }, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Create(ctx, resourceGroup, name, properties) @@ -271,7 +281,7 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) } func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlServersClient + client := meta.(*ArmClient).postgres.ServersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Server update.") @@ -284,7 +294,7 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) version := d.Get("version").(string) sku := expandAzureRmPostgreSQLServerSku(d) storageProfile := expandAzureRmPostgreSQLStorageProfile(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) properties := postgresql.ServerUpdateParameters{ ServerUpdateParametersProperties: &postgresql.ServerUpdateParametersProperties{ @@ -294,7 +304,7 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) SslEnforcement: postgresql.SslEnforcementEnum(sslEnforcement), }, Sku: sku, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.Update(ctx, resourceGroup, name, properties) @@ -321,10 +331,10 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlServersClient + client := meta.(*ArmClient).postgres.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -361,19 +371,17 @@ func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error setting `storage_profile`: %+v", err) } - flattenAndSetTags(d, resp.Tags) - // Computed d.Set("fqdn", resp.FullyQualifiedDomainName) - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmPostgreSQLServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlServersClient + client := meta.(*ArmClient).postgres.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -424,11 +432,13 @@ func expandAzureRmPostgreSQLStorageProfile(d *schema.ResourceData) *postgresql.S backupRetentionDays := storageprofile["backup_retention_days"].(int) geoRedundantBackup := storageprofile["geo_redundant_backup"].(string) storageMB := storageprofile["storage_mb"].(int) + autoGrow := storageprofile["auto_grow"].(string) return &postgresql.StorageProfile{ BackupRetentionDays: utils.Int32(int32(backupRetentionDays)), GeoRedundantBackup: postgresql.GeoRedundantBackup(geoRedundantBackup), StorageMB: utils.Int32(int32(storageMB)), + StorageAutogrow: postgresql.StorageAutogrow(autoGrow), } } @@ -459,6 +469,8 @@ func flattenPostgreSQLStorageProfile(resp *postgresql.StorageProfile) []interfac values["storage_mb"] = *storageMB } + values["auto_grow"] = string(resp.StorageAutogrow) + if backupRetentionDays := resp.BackupRetentionDays; backupRetentionDays != nil { values["backup_retention_days"] = *backupRetentionDays } diff --git a/azurerm/resource_arm_postgresql_server_test.go b/azurerm/resource_arm_postgresql_server_test.go index 45d9e12d2695..89e9407e6932 100644 --- a/azurerm/resource_arm_postgresql_server_test.go +++ b/azurerm/resource_arm_postgresql_server_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -102,8 +103,38 @@ func TestAccAzureRMPostgreSQLServer_basicTenPointZero(t *testing.T) { }) } +func TestAccAzureRMPostgreSQLServer_basicEleven(t *testing.T) { + resourceName := "azurerm_postgresql_server.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPostgreSQLServer_basicEleven(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "administrator_login", "acctestun"), + resource.TestCheckResourceAttr(resourceName, "version", "11"), + resource.TestCheckResourceAttr(resourceName, "ssl_enforcement", "Enabled"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "administrator_login_password", // not returned as sensitive + }, + }, + }, + }) +} + func TestAccAzureRMPostgreSQLServer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -267,6 +298,7 @@ func TestAccAzureRMPostgreSQLServer_updated(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "sku.0.name", "GP_Gen5_2"), resource.TestCheckResourceAttr(resourceName, "version", "9.6"), resource.TestCheckResourceAttr(resourceName, "storage_profile.0.storage_mb", "51200"), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Disabled"), resource.TestCheckResourceAttr(resourceName, "administrator_login", "acctestun"), ), }, @@ -277,6 +309,7 @@ func TestAccAzureRMPostgreSQLServer_updated(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "sku.0.name", "GP_Gen5_4"), resource.TestCheckResourceAttr(resourceName, "version", "9.6"), resource.TestCheckResourceAttr(resourceName, "storage_profile.0.storage_mb", "640000"), + resource.TestCheckResourceAttr(resourceName, "storage_profile.0.auto_grow", "Enabled"), resource.TestCheckResourceAttr(resourceName, "administrator_login", "acctestun"), ), }, @@ -340,7 +373,7 @@ func testCheckAzureRMPostgreSQLServerExists(resourceName string) resource.TestCh return fmt.Errorf("Bad: no resource group found in state for PostgreSQL Server: %s", name) } - client := testAccProvider.Meta().(*ArmClient).postgresqlServersClient + client := testAccProvider.Meta().(*ArmClient).postgres.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -357,7 +390,7 @@ func testCheckAzureRMPostgreSQLServerExists(resourceName string) resource.TestCh } func testCheckAzureRMPostgreSQLServerDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).postgresqlServersClient + client := testAccProvider.Meta().(*ArmClient).postgres.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -384,7 +417,7 @@ func testCheckAzureRMPostgreSQLServerDestroy(s *terraform.State) error { return nil } -func testAccAzureRMPostgreSQLServer_basicNinePointFive(rInt int, location string) string { +func testAccAzureRMPostgreSQLServer_basic(rInt int, location string, version string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" @@ -407,80 +440,31 @@ resource "azurerm_postgresql_server" "test" { storage_mb = 51200 backup_retention_days = 7 geo_redundant_backup = "Disabled" + auto_grow = "Disabled" } administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "9.5" + version = "%s" ssl_enforcement = "Enabled" } -`, rInt, location, rInt) +`, rInt, location, rInt, version) } -func testAccAzureRMPostgreSQLServer_basicNinePointSix(rInt int, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" +func testAccAzureRMPostgreSQLServer_basicNinePointFive(rInt int, location string) string { + return testAccAzureRMPostgreSQLServer_basic(rInt, location, "9.5") } -resource "azurerm_postgresql_server" "test" { - name = "acctestpsqlsvr-%d" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - - sku { - name = "GP_Gen5_2" - capacity = 2 - tier = "GeneralPurpose" - family = "Gen5" - } - - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" -} -`, rInt, location, rInt) +func testAccAzureRMPostgreSQLServer_basicNinePointSix(rInt int, location string) string { + return testAccAzureRMPostgreSQLServer_basic(rInt, location, "9.6") } func testAccAzureRMPostgreSQLServer_basicTenPointZero(rInt int, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" + return testAccAzureRMPostgreSQLServer_basic(rInt, location, "10.0") } -resource "azurerm_postgresql_server" "test" { - name = "acctestpsqlsvr-%d" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - - sku { - name = "GP_Gen5_2" - capacity = 2 - tier = "GeneralPurpose" - family = "Gen5" - } - - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "10.0" - ssl_enforcement = "Enabled" -} -`, rInt, location, rInt) +func testAccAzureRMPostgreSQLServer_basicEleven(rInt int, location string) string { + return testAccAzureRMPostgreSQLServer_basic(rInt, location, "11") } func testAccAzureRMPostgreSQLServer_requiresImport(rInt int, location string) string { @@ -602,6 +586,7 @@ resource "azurerm_postgresql_server" "test" { storage_mb = 947200 backup_retention_days = 7 geo_redundant_backup = "Disabled" + auto_grow = "Enabled" } administrator_login = "acctestun" diff --git a/azurerm/resource_arm_postgresql_virtual_network_rule.go b/azurerm/resource_arm_postgresql_virtual_network_rule.go index 72f8774561c4..b83232d1ad6f 100644 --- a/azurerm/resource_arm_postgresql_virtual_network_rule.go +++ b/azurerm/resource_arm_postgresql_virtual_network_rule.go @@ -7,6 +7,7 @@ import ( "time" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" "github.com/hashicorp/terraform/helper/resource" @@ -59,7 +60,7 @@ func resourceArmPostgreSQLVirtualNetworkRule() *schema.Resource { } func resourceArmPostgreSQLVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlVirtualNetworkRulesClient + client := meta.(*ArmClient).postgres.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -68,7 +69,7 @@ func resourceArmPostgreSQLVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, subnetId := d.Get("subnet_id").(string) ignoreMissingVnetServiceEndpoint := d.Get("ignore_missing_vnet_service_endpoint").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -123,10 +124,10 @@ func resourceArmPostgreSQLVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, } func resourceArmPostgreSQLVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlVirtualNetworkRulesClient + client := meta.(*ArmClient).postgres.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -159,10 +160,10 @@ func resourceArmPostgreSQLVirtualNetworkRuleRead(d *schema.ResourceData, meta in } func resourceArmPostgreSQLVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).postgresqlVirtualNetworkRulesClient + client := meta.(*ArmClient).postgres.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -191,7 +192,7 @@ func resourceArmPostgreSQLVirtualNetworkRuleDelete(d *schema.ResourceData, meta return nil } -func postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client postgresql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { +func postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *postgresql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.Get(ctx, resourceGroup, serverName, name) diff --git a/azurerm/resource_arm_postgresql_virtual_network_rule_test.go b/azurerm/resource_arm_postgresql_virtual_network_rule_test.go index f1f3c3b7275d..d6b948cab2d7 100644 --- a/azurerm/resource_arm_postgresql_virtual_network_rule_test.go +++ b/azurerm/resource_arm_postgresql_virtual_network_rule_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMPostgreSQLVirtualNetworkRule_basic(t *testing.T) { } func TestAccAzureRMPostgreSQLVirtualNetworkRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -176,7 +177,7 @@ func testCheckAzureRMPostgreSQLVirtualNetworkRuleExists(resourceName string) res serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).postgresqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).postgres.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -202,7 +203,7 @@ func testCheckAzureRMPostgreSQLVirtualNetworkRuleDestroy(s *terraform.State) err serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).postgresqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).postgres.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -232,7 +233,7 @@ func testCheckAzureRMPostgreSQLVirtualNetworkRuleDisappears(resourceName string) serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).postgresqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).postgres.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, serverName, ruleName) diff --git a/azurerm/resource_arm_private_dns_a_record.go b/azurerm/resource_arm_private_dns_a_record.go new file mode 100644 index 000000000000..f965d6a03536 --- /dev/null +++ b/azurerm/resource_arm_private_dns_a_record.go @@ -0,0 +1,194 @@ +package azurerm + +import ( + "fmt" + "net/http" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPrivateDnsARecord() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPrivateDnsARecordCreateUpdate, + Read: resourceArmPrivateDnsARecordRead, + Update: resourceArmPrivateDnsARecordCreateUpdate, + Delete: resourceArmPrivateDnsARecordDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + // TODO: make this case sensitive once the API's fixed https://github.com/Azure/azure-rest-api-specs/issues/6641 + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "zone_name": { + Type: schema.TypeString, + Required: true, + }, + + "records": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + + "ttl": { + Type: schema.TypeInt, + Required: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmPrivateDnsARecordCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + zoneName := d.Get("zone_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, zoneName, privatedns.A, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Private DNS A Record %q (Private Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_private_dns_a_record", *existing.ID) + } + } + + ttl := int64(d.Get("ttl").(int)) + t := d.Get("tags").(map[string]interface{}) + + parameters := privatedns.RecordSet{ + Name: &name, + RecordSetProperties: &privatedns.RecordSetProperties{ + Metadata: tags.Expand(t), + TTL: &ttl, + ARecords: expandAzureRmPrivateDnsARecords(d), + }, + } + + eTag := "" + ifNoneMatch := "" // set to empty to allow updates to records after creation + if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, privatedns.A, name, parameters, eTag, ifNoneMatch); err != nil { + return fmt.Errorf("Error creating/updating Private DNS A Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + + resp, err := client.Get(ctx, resGroup, zoneName, privatedns.A, name) + if err != nil { + return fmt.Errorf("Error retrieving Private DNS A Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Private DNS A Record %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*resp.ID) + + return resourceArmPrivateDnsARecordRead(d, meta) +} + +func resourceArmPrivateDnsARecordRead(d *schema.ResourceData, meta interface{}) error { + dnsClient := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["A"] + zoneName := id.Path["privateDnsZones"] + + resp, err := dnsClient.Get(ctx, resGroup, zoneName, privatedns.A, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Private DNS A record %s: %+v", name, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resGroup) + d.Set("zone_name", zoneName) + d.Set("ttl", resp.TTL) + + if err := d.Set("records", flattenAzureRmPrivateDnsARecords(resp.ARecords)); err != nil { + return err + } + return tags.FlattenAndSet(d, resp.Metadata) +} + +func resourceArmPrivateDnsARecordDelete(d *schema.ResourceData, meta interface{}) error { + dnsClient := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["A"] + zoneName := id.Path["privateDnsZones"] + + resp, err := dnsClient.Delete(ctx, resGroup, zoneName, privatedns.A, name, "") + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error deleting Private DNS A Record %s: %+v", name, err) + } + + return nil +} + +func flattenAzureRmPrivateDnsARecords(records *[]privatedns.ARecord) []string { + results := make([]string, 0) + if records == nil { + return results + } + + for _, record := range *records { + if record.Ipv4Address == nil { + continue + } + + results = append(results, *record.Ipv4Address) + } + + return results +} + +func expandAzureRmPrivateDnsARecords(d *schema.ResourceData) *[]privatedns.ARecord { + recordStrings := d.Get("records").(*schema.Set).List() + records := make([]privatedns.ARecord, len(recordStrings)) + + for i, v := range recordStrings { + ipv4 := v.(string) + records[i] = privatedns.ARecord{ + Ipv4Address: &ipv4, + } + } + + return &records +} diff --git a/azurerm/resource_arm_private_dns_a_record_test.go b/azurerm/resource_arm_private_dns_a_record_test.go new file mode 100644 index 000000000000..2559cc825485 --- /dev/null +++ b/azurerm/resource_arm_private_dns_a_record_test.go @@ -0,0 +1,303 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMPrivateDnsARecord_basic(t *testing.T) { + resourceName := "azurerm_private_dns_a_record.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMPrivateDnsARecord_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsARecordDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsARecord_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_private_dns_a_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsARecordDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPrivateDnsARecord_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + ), + }, + { + Config: testAccAzureRMPrivateDnsARecord_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_private_dns_a_record"), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsARecord_updateRecords(t *testing.T) { + resourceName := "azurerm_private_dns_a_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsARecord_basic(ri, location) + postConfig := testAccAzureRMPrivateDnsARecord_updateRecords(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsARecordDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "records.#", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "records.#", "3"), + ), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsARecord_withTags(t *testing.T) { + resourceName := "azurerm_private_dns_a_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsARecord_withTags(ri, location) + postConfig := testAccAzureRMPrivateDnsARecord_withTagsUpdate(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsARecordDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsARecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMPrivateDnsARecordExists(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) + } + + aName := rs.Primary.Attributes["name"] + zoneName := rs.Primary.Attributes["zone_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Private DNS A record: %s", aName) + } + + conn := testAccProvider.Meta().(*ArmClient).privateDns.RecordSetsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := conn.Get(ctx, resourceGroup, zoneName, privatedns.A, aName) + if err != nil { + return fmt.Errorf("Bad: Get A RecordSet: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Private DNS A record %s (resource group: %s) does not exist", aName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMPrivateDnsARecordDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).privateDns.RecordSetsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_private_dns_a_record" { + continue + } + + aName := rs.Primary.Attributes["name"] + zoneName := rs.Primary.Attributes["zone_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, zoneName, privatedns.A, aName) + + if err != nil { + if resp.StatusCode == http.StatusNotFound { + return nil + } + + return err + } + + return fmt.Errorf("Private DNS A record still exists:\n%#v", resp.RecordSetProperties) + } + + return nil +} + +func testAccAzureRMPrivateDnsARecord_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_a_record" "test" { + name = "myarecord%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + records = ["1.2.3.4", "1.2.4.5"] +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsARecord_requiresImport(rInt int, location string) string { + template := testAccAzureRMPrivateDnsARecord_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_private_dns_a_record" "import" { + name = "${azurerm_private_dns_a_record.test.name}" + resource_group_name = "${azurerm_private_dns_a_record.test.resource_group_name}" + zone_name = "${azurerm_private_dns_a_record.test.zone_name}" + ttl = 300 + records = ["1.2.3.4", "1.2.4.5"] +} +`, template) +} + +func testAccAzureRMPrivateDnsARecord_updateRecords(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_a_record" "test" { + name = "myarecord%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + records = ["1.2.3.4", "1.2.4.5", "1.2.3.7"] +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsARecord_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_a_record" "test" { + name = "myarecord%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + records = ["1.2.3.4", "1.2.4.5"] + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsARecord_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_a_record" "test" { + name = "myarecord%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + records = ["1.2.3.4", "1.2.4.5"] + + tags = { + environment = "staging" + } +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_private_dns_cname_record.go b/azurerm/resource_arm_private_dns_cname_record.go new file mode 100644 index 000000000000..e68cfdd41faa --- /dev/null +++ b/azurerm/resource_arm_private_dns_cname_record.go @@ -0,0 +1,174 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPrivateDnsCNameRecord() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPrivateDnsCNameRecordCreateUpdate, + Read: resourceArmPrivateDnsCNameRecordRead, + Update: resourceArmPrivateDnsCNameRecordCreateUpdate, + Delete: resourceArmPrivateDnsCNameRecordDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + // TODO: make this case sensitive once the API's fixed https://github.com/Azure/azure-rest-api-specs/issues/6641 + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "zone_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "record": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "ttl": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 2147483647), + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmPrivateDnsCNameRecordCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + zoneName := d.Get("zone_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, zoneName, privatedns.CNAME, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Private DNS CNAME Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_private_dns_cname_record", *existing.ID) + } + } + + ttl := int64(d.Get("ttl").(int)) + record := d.Get("record").(string) + t := d.Get("tags").(map[string]interface{}) + + parameters := privatedns.RecordSet{ + Name: &name, + RecordSetProperties: &privatedns.RecordSetProperties{ + Metadata: tags.Expand(t), + TTL: &ttl, + CnameRecord: &privatedns.CnameRecord{ + Cname: &record, + }, + }, + } + + eTag := "" + ifNoneMatch := "" // set to empty to allow updates to records after creation + if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, privatedns.CNAME, name, parameters, eTag, ifNoneMatch); err != nil { + return fmt.Errorf("Error creating/updating Private DNS CNAME Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + + resp, err := client.Get(ctx, resGroup, zoneName, privatedns.CNAME, name) + if err != nil { + return fmt.Errorf("Error retrieving Private DNS CNAME Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Private DNS CNAME Record %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*resp.ID) + + return resourceArmPrivateDnsCNameRecordRead(d, meta) +} + +func resourceArmPrivateDnsCNameRecordRead(d *schema.ResourceData, meta interface{}) error { + dnsClient := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["CNAME"] + zoneName := id.Path["privateDnsZones"] + + resp, err := dnsClient.Get(ctx, resGroup, zoneName, privatedns.CNAME, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Private DNS CNAME record %s: %+v", name, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resGroup) + d.Set("zone_name", zoneName) + d.Set("ttl", resp.TTL) + + if props := resp.RecordSetProperties; props != nil { + if record := props.CnameRecord; record != nil { + d.Set("record", record.Cname) + } + } + + return tags.FlattenAndSet(d, resp.Metadata) +} + +func resourceArmPrivateDnsCNameRecordDelete(d *schema.ResourceData, meta interface{}) error { + dnsClient := meta.(*ArmClient).privateDns.RecordSetsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["CNAME"] + zoneName := id.Path["privateDnsZones"] + + _, err = dnsClient.Get(ctx, resGroup, zoneName, privatedns.CNAME, name) + if err != nil { + return fmt.Errorf("Error deleting Private DNS CNAME Record %s: %+v", name, err) + } + + return nil +} diff --git a/azurerm/resource_arm_private_dns_cname_record_test.go b/azurerm/resource_arm_private_dns_cname_record_test.go new file mode 100644 index 000000000000..a1ffbb7d40bd --- /dev/null +++ b/azurerm/resource_arm_private_dns_cname_record_test.go @@ -0,0 +1,349 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMPrivateDnsCNameRecord_basic(t *testing.T) { + resourceName := "azurerm_private_dns_cname_record.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMPrivateDnsCNameRecord_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsCNameRecordDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsCNameRecord_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_private_dns_cname_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsCNameRecordDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPrivateDnsCNameRecord_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + ), + }, + { + Config: testAccAzureRMPrivateDnsCNameRecord_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_private_dns_cname_record"), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsCNameRecord_subdomain(t *testing.T) { + resourceName := "azurerm_private_dns_cname_record.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMPrivateDnsCNameRecord_subdomain(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsCNameRecordDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "record", "test.contoso.com"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsCNameRecord_updateRecords(t *testing.T) { + resourceName := "azurerm_private_dns_cname_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsCNameRecord_basic(ri, location) + postConfig := testAccAzureRMPrivateDnsCNameRecord_updateRecords(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsCNameRecordDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsCNameRecord_withTags(t *testing.T) { + resourceName := "azurerm_private_dns_cname_record.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsCNameRecord_withTags(ri, location) + postConfig := testAccAzureRMPrivateDnsCNameRecord_withTagsUpdate(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsCNameRecordDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsCNameRecordExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMPrivateDnsCNameRecordExists(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) + } + + aName := rs.Primary.Attributes["name"] + zoneName := rs.Primary.Attributes["zone_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Private DNS CNAME record: %s", aName) + } + + conn := testAccProvider.Meta().(*ArmClient).privateDns.RecordSetsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := conn.Get(ctx, resourceGroup, zoneName, privatedns.CNAME, aName) + if err != nil { + return fmt.Errorf("Bad: Get CNAME RecordSet: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Private DNS CNAME record %s (resource group: %s) does not exist", aName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMPrivateDnsCNameRecordDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).privateDns.RecordSetsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_private_dns_cname_record" { + continue + } + + aName := rs.Primary.Attributes["name"] + zoneName := rs.Primary.Attributes["zone_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, zoneName, privatedns.A, aName) + + if err != nil { + if resp.StatusCode == http.StatusNotFound { + return nil + } + + return err + } + + return fmt.Errorf("Private DNS CNAME record still exists:\n%#v", resp.RecordSetProperties) + } + + return nil +} + +func testAccAzureRMPrivateDnsCNameRecord_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_cname_record" "test" { + name = "acctestcname%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + record = "contoso.com" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsCNameRecord_requiresImport(rInt int, location string) string { + template := testAccAzureRMPrivateDnsCNameRecord_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_private_dns_cname_record" "import" { + name = "${azurerm_private_dns_cname_record.test.name}" + resource_group_name = "${azurerm_private_dns_cname_record.test.resource_group_name}" + zone_name = "${azurerm_private_dns_cname_record.test.zone_name}" + ttl = 300 + record = "contoso.com" +} +`, template) +} + +func testAccAzureRMPrivateDnsCNameRecord_subdomain(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_cname_record" "test" { + name = "acctestcname%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + record = "test.contoso.com" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsCNameRecord_updateRecords(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_cname_record" "test" { + name = "acctestcname%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + record = "contoso.com" +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsCNameRecord_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_cname_record" "test" { + name = "acctestcname%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + record = "contoso.com" + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt, rInt) +} + +func testAccAzureRMPrivateDnsCNameRecord_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_cname_record" "test" { + name = "acctestcname%d" + resource_group_name = "${azurerm_resource_group.test.name}" + zone_name = "${azurerm_private_dns_zone.test.name}" + ttl = 300 + record = "contoso.com" + + tags = { + environment = "staging" + } +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_private_dns_zone.go b/azurerm/resource_arm_private_dns_zone.go new file mode 100644 index 000000000000..3462bf2bbb1c --- /dev/null +++ b/azurerm/resource_arm_private_dns_zone.go @@ -0,0 +1,176 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/schema" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPrivateDnsZone() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPrivateDnsZoneCreateUpdate, + Read: resourceArmPrivateDnsZoneRead, + Update: resourceArmPrivateDnsZoneCreateUpdate, + Delete: resourceArmPrivateDnsZoneDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "number_of_record_sets": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_number_of_record_sets": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_number_of_virtual_network_links": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_number_of_virtual_network_links_with_registration": { + Type: schema.TypeInt, + Computed: true, + }, + + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "tags": tags.Schema(), + }, + } +} + +func resourceArmPrivateDnsZoneCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.PrivateZonesClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("error checking for presence of existing Private DNS Zone %q (Resource Group %q): %s", name, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_private_dns_zone", *existing.ID) + } + } + + location := "global" + t := d.Get("tags").(map[string]interface{}) + + parameters := privatedns.PrivateZone{ + Location: &location, + Tags: tags.Expand(t), + } + + etag := "" + ifNoneMatch := "" // set to empty to allow updates to records after creation + future, err := client.CreateOrUpdate(ctx, resGroup, name, parameters, etag, ifNoneMatch) + if err != nil { + return fmt.Errorf("error creating/updating Private DNS Zone %q (Resource Group %q): %s", name, resGroup, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("error waiting for Private DNS Zone %q to become available: %+v", name, err) + } + + resp, err := client.Get(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("error retrieving Private DNS Zone %q (Resource Group %q): %s", name, resGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("cannot read Private DNS Zone %q (Resource Group %q) ID", name, resGroup) + } + + d.SetId(*resp.ID) + + return resourceArmPrivateDnsZoneRead(d, meta) +} + +func resourceArmPrivateDnsZoneRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.PrivateZonesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["privateDnsZones"] + + resp, err := client.Get(ctx, resGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("error reading Private DNS Zone %q (Resource Group %q): %+v", name, resGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resGroup) + + if props := resp.PrivateZoneProperties; props != nil { + d.Set("number_of_record_sets", props.NumberOfRecordSets) + d.Set("max_number_of_record_sets", props.MaxNumberOfRecordSets) + d.Set("max_number_of_virtual_network_links", props.MaxNumberOfVirtualNetworkLinks) + d.Set("max_number_of_virtual_network_links_with_registration", props.MaxNumberOfVirtualNetworkLinksWithRegistration) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmPrivateDnsZoneDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.PrivateZonesClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + name := id.Path["privateDnsZones"] + + etag := "" + future, err := client.Delete(ctx, resGroup, name, etag) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("error deleting Private DNS Zone %s (resource group %s): %+v", name, resGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("error deleting Private DNS Zone %s (resource group %s): %+v", name, resGroup, err) + } + + return nil +} diff --git a/azurerm/resource_arm_private_dns_zone_test.go b/azurerm/resource_arm_private_dns_zone_test.go new file mode 100644 index 000000000000..514a75f82806 --- /dev/null +++ b/azurerm/resource_arm_private_dns_zone_test.go @@ -0,0 +1,220 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMPrivateDnsZone_basic(t *testing.T) { + resourceName := "azurerm_private_dns_zone.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMPrivateDnsZone_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsZone_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_private_dns_zone.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPrivateDnsZone_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneExists(resourceName), + ), + }, + { + Config: testAccAzureRMPrivateDnsZone_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_private_dns_zone"), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsZone_withTags(t *testing.T) { + resourceName := "azurerm_private_dns_zone.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsZone_withTags(ri, location) + postConfig := testAccAzureRMPrivateDnsZone_withTagsUpdate(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMPrivateDnsZoneExists(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) + } + + zoneName := 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 Private DNS zone: %s", zoneName) + } + + client := testAccProvider.Meta().(*ArmClient).privateDns.PrivateZonesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, zoneName) + if err != nil { + return fmt.Errorf("Bad: Get Private DNS zone: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Private DNS zone %s (resource group: %s) does not exist", zoneName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMPrivateDnsZoneDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).privateDns.PrivateZonesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_private_dns_zone" { + continue + } + + zoneName := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, zoneName) + if err != nil { + if resp.StatusCode == http.StatusNotFound { + return nil + } + + return err + } + + return fmt.Errorf("Private DNS zone still exists:\n%#v", resp) + } + + return nil +} + +func testAccAzureRMPrivateDnsZone_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + +func testAccAzureRMPrivateDnsZone_requiresImport(rInt int, location string) string { + template := testAccAzureRMPrivateDnsZone_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_private_dns_zone" "import" { + name = "${azurerm_private_dns_zone.test.name}" + resource_group_name = "${azurerm_private_dns_zone.test.resource_group_name}" +} +`, template) +} + +func testAccAzureRMPrivateDnsZone_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMPrivateDnsZone_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "staging" + } +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_private_dns_zone_virtual_network_link.go b/azurerm/resource_arm_private_dns_zone_virtual_network_link.go new file mode 100644 index 000000000000..519e531f94d6 --- /dev/null +++ b/azurerm/resource_arm_private_dns_zone_virtual_network_link.go @@ -0,0 +1,218 @@ +package azurerm + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPrivateDnsZoneVirtualNetworkLink() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPrivateDnsZoneVirtualNetworkLinkCreateUpdate, + Read: resourceArmPrivateDnsZoneVirtualNetworkLinkRead, + Update: resourceArmPrivateDnsZoneVirtualNetworkLinkCreateUpdate, + Delete: resourceArmPrivateDnsZoneVirtualNetworkLinkDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "private_dns_zone_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "virtual_network_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "registration_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "tags": tagsSchema(), + }, + } +} + +func resourceArmPrivateDnsZoneVirtualNetworkLinkCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.VirtualNetworkLinksClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + dnsZoneName := d.Get("private_dns_zone_name").(string) + vNetID := d.Get("virtual_network_id").(string) + registrationEnabled := d.Get("registration_enabled").(bool) + resGroup := d.Get("resource_group_name").(string) + + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, dnsZoneName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("error checking for presence of existing Private DNS Zone Virtual network link %q (Resource Group %q): %s", name, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_private_dns_zone_virtual_network_link", *existing.ID) + } + } + + location := "global" + tags := d.Get("tags").(map[string]interface{}) + + parameters := privatedns.VirtualNetworkLink{ + Location: &location, + Tags: expandTags(tags), + VirtualNetworkLinkProperties: &privatedns.VirtualNetworkLinkProperties{ + VirtualNetwork: &privatedns.SubResource{ + ID: &vNetID, + }, + RegistrationEnabled: ®istrationEnabled, + }, + } + + etag := "" + ifNoneMatch := "" // set to empty to allow updates to records after creation + + future, err := client.CreateOrUpdate(ctx, resGroup, dnsZoneName, name, parameters, etag, ifNoneMatch) + if err != nil { + return fmt.Errorf("error creating/updating Private DNS Zone Virtual network link %q (Resource Group %q): %s", name, resGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("error waiting for Private DNS Zone Virtual network link %q to become available: %+v", name, err) + } + + resp, err := client.Get(ctx, resGroup, dnsZoneName, name) + if err != nil { + return fmt.Errorf("error retrieving Private DNS Zone Virtual network link %q (Resource Group %q): %s", name, resGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("cannot read Private DNS Zone Virtual network link %q (Resource Group %q) ID", name, resGroup) + } + + d.SetId(*resp.ID) + + return resourceArmPrivateDnsZoneVirtualNetworkLinkRead(d, meta) +} + +func resourceArmPrivateDnsZoneVirtualNetworkLinkRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.VirtualNetworkLinksClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + dnsZoneName := id.Path["privateDnsZones"] + name := id.Path["virtualNetworkLinks"] + + resp, err := client.Get(ctx, resGroup, dnsZoneName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("error reading Private DNS Zone Virtual network link %q (Resource Group %q): %+v", name, resGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resGroup) + d.Set("private_dns_zone_name", dnsZoneName) + + if props := resp.VirtualNetworkLinkProperties; props != nil { + d.Set("registration_enabled", props.RegistrationEnabled) + + if network := props.VirtualNetwork; network != nil { + d.Set("virtual_network_id", network.ID) + } + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmPrivateDnsZoneVirtualNetworkLinkDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).privateDns.VirtualNetworkLinksClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + dnsZoneName := id.Path["privateDnsZones"] + name := id.Path["virtualNetworkLinks"] + + etag := "" + if future, err := client.Delete(ctx, resGroup, dnsZoneName, name, etag); err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("error deleting Virtual Network Link %q (Private DNS Zone %q / Resource Group %q): %+v", name, dnsZoneName, resGroup, err) + } + + // whilst the Delete above returns a Future, the Azure API's broken such that even though it's marked as "gone" + // it's still kicking around - so we have to poll until this is actually gone + log.Printf("[DEBUG] Waiting for Virtual Network Link %q (Private DNS Zone %q / Resource Group %q) to be deleted", name, dnsZoneName, resGroup) + stateConf := &resource.StateChangeConf{ + Pending: []string{"Available"}, + Target: []string{"NotFound"}, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking to see if Virtual Network Link %q (Private DNS Zone %q / Resource Group %q) is available", name, dnsZoneName, resGroup) + resp, err := client.Get(ctx, resGroup, dnsZoneName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Virtual Network Link %q (Private DNS Zone %q / Resource Group %q) was not found", name, dnsZoneName, resGroup) + return "NotFound", "NotFound", nil + } + + return "", "error", err + } + + log.Printf("[DEBUG] Virtual Network Link %q (Private DNS Zone %q / Resource Group %q) still exists", name, dnsZoneName, resGroup) + return "Available", "Available", nil + }, + Timeout: 30 * time.Minute, + Delay: 30 * time.Second, + PollInterval: 10 * time.Second, + ContinuousTargetOccurence: 10, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("error waiting for deletion of Virtual Network Link %q (Private DNS Zone %q / Resource Group %q): %+v", name, dnsZoneName, resGroup, err) + } + + return nil +} diff --git a/azurerm/resource_arm_private_dns_zone_virtual_network_link_test.go b/azurerm/resource_arm_private_dns_zone_virtual_network_link_test.go new file mode 100644 index 000000000000..5fd95d10ba09 --- /dev/null +++ b/azurerm/resource_arm_private_dns_zone_virtual_network_link_test.go @@ -0,0 +1,284 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMPrivateDnsZoneVirtualNetworkLink_basic(t *testing.T) { + resourceName := "azurerm_private_dns_zone_virtual_network_link.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMPrivateDnsZoneVirtualNetworkLink_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsZoneVirtualNetworkLink_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_private_dns_zone_virtual_network_link.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPrivateDnsZoneVirtualNetworkLink_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkExists(resourceName), + ), + }, + { + Config: testAccAzureRMPrivateDnsZoneVirtualNetworkLink_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_private_dns_zone_virtual_network_link"), + }, + }, + }) +} + +func TestAccAzureRMPrivateDnsZoneVirtualNetworkLink_withTags(t *testing.T) { + resourceName := "azurerm_private_dns_zone_virtual_network_link.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccAzureRMPrivateDnsZoneVirtualNetworkLink_withTags(ri, location) + postConfig := testAccAzureRMPrivateDnsZoneVirtualNetworkLink_withTagsUpdate(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkExists(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"] + dnsZoneName := rs.Primary.Attributes["private_dns_zone_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Private DNS zone virtual network link: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).privateDns.VirtualNetworkLinksClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, dnsZoneName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: virtual network link %q (Private DNS zone %q / resource group: %s) does not exist", name, dnsZoneName, resourceGroup) + } + + return fmt.Errorf("Bad: Get Private DNS zone virtual network link: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMPrivateDnsZoneVirtualNetworkLinkDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).privateDns.VirtualNetworkLinksClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_private_dns_zone_virtual_network_link" { + continue + } + + name := rs.Primary.Attributes["name"] + dnsZoneName := rs.Primary.Attributes["private_dns_zone_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, dnsZoneName, name) + if err != nil { + if resp.StatusCode == http.StatusNotFound { + return nil + } + + return err + } + + return fmt.Errorf("Private DNS zone virtual network link still exists:\n%#v", resp) + } + + return nil +} + +func testAccAzureRMPrivateDnsZoneVirtualNetworkLink_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 = "vnet%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.0.0.0/16"] + + subnet { + name = "subnet1" + address_prefix = "10.0.1.0/24" + } +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_zone_virtual_network_link" "test" { + name = "acctest%d" + private_dns_zone_name = "${azurerm_private_dns_zone.test.name}" + virtual_network_id = "${azurerm_virtual_network.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMPrivateDnsZoneVirtualNetworkLink_requiresImport(rInt int, location string) string { + template := testAccAzureRMPrivateDnsZoneVirtualNetworkLink_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_private_dns_zone_virtual_network_link" "import" { + name = "${azurerm_private_dns_zone_virtual_network_link.test.name} + private_dns_zone_name = "${azurerm_private_dns_zone_virtual_network_link.test.private_dns_zone_name}" + virtual_network_id = "${azurerm_private_dns_zone_virtual_network_link.test.virtual_network_id}" + resource_group_name = "${azurerm_private_dns_zone_virtual_network_link.test.resource_group_name}" +} +`, template) +} + +func testAccAzureRMPrivateDnsZoneVirtualNetworkLink_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 = "vnet%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.0.0.0/16"] + + subnet { + name = "subnet1" + address_prefix = "10.0.1.0/24" + } +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_zone_virtual_network_link" "test" { + name = "acctest%d" + private_dns_zone_name = "${azurerm_private_dns_zone.test.name}" + virtual_network_id = "${azurerm_virtual_network.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMPrivateDnsZoneVirtualNetworkLink_withTagsUpdate(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "vnet%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.0.0.0/16"] + + subnet { + name = "subnet1" + address_prefix = "10.0.1.0/24" + } +} + +resource "azurerm_private_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_private_dns_zone_virtual_network_link" "test" { + name = "acctestzone%d.com" + private_dns_zone_name = "${azurerm_private_dns_zone.test.name}" + virtual_network_id = "${azurerm_virtual_network.test.id}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "staging" + } +} +`, rInt, location, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_proximity_placement_group.go b/azurerm/resource_arm_proximity_placement_group.go new file mode 100644 index 000000000000..0ad2253c74b8 --- /dev/null +++ b/azurerm/resource_arm_proximity_placement_group.go @@ -0,0 +1,125 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmProximityPlacementGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceArmProximityPlacementGroupCreateUpdate, + Read: resourceArmProximityPlacementGroupRead, + Update: resourceArmProximityPlacementGroupCreateUpdate, + Delete: resourceArmProximityPlacementGroupDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "tags": tags.Schema(), + }, + } +} + +func resourceArmProximityPlacementGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM Proximity Placement Group creation.") + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Proximity Placement Group %q (Resource Group %q): %s", name, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_proximity_placement_group", *existing.ID) + } + } + + ppg := compute.ProximityPlacementGroup{ + Name: &name, + Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + + resp, err := client.CreateOrUpdate(ctx, resourceGroup, name, ppg) + if err != nil { + return err + } + + d.SetId(*resp.ID) + + return resourceArmProximityPlacementGroupRead(d, meta) +} + +func resourceArmProximityPlacementGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["proximityPlacementGroups"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Proximity Placement Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmProximityPlacementGroupDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["proximityPlacementGroups"] + + _, err = client.Delete(ctx, resGroup, name) + return err +} diff --git a/azurerm/resource_arm_proximity_placement_group_test.go b/azurerm/resource_arm_proximity_placement_group_test.go new file mode 100644 index 000000000000..319d0d9af55b --- /dev/null +++ b/azurerm/resource_arm_proximity_placement_group_test.go @@ -0,0 +1,276 @@ +package azurerm + +import ( + "fmt" + "testing" + + "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccProximityPlacementGroup_basic(t *testing.T) { + resourceName := "azurerm_proximity_placement_group.test" + ri := tf.AccRandTimeInt() + config := testAccProximityPlacementGroup_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMProximityPlacementGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMProximityPlacementGroupExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProximityPlacementGroup_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_proximity_placement_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMProximityPlacementGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProximityPlacementGroup_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMProximityPlacementGroupExists(resourceName), + ), + }, + { + Config: testAccProximityPlacementGroup_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_proximity_placement_group"), + }, + }, + }) +} + +func TestAccProximityPlacementGroup_disappears(t *testing.T) { + resourceName := "azurerm_proximity_placement_group.test" + ri := tf.AccRandTimeInt() + config := testAccProximityPlacementGroup_basic(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMProximityPlacementGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMProximityPlacementGroupExists(resourceName), + testCheckAzureRMProximityPlacementGroupDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccProximityPlacementGroup_withTags(t *testing.T) { + resourceName := "azurerm_proximity_placement_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + preConfig := testAccProximityPlacementGroup_withTags(ri, location) + postConfig := testAccProximityPlacementGroup_withUpdatedTags(ri, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMProximityPlacementGroupDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMProximityPlacementGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "Production"), + resource.TestCheckResourceAttr(resourceName, "tags.cost_center", "MSFT"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMProximityPlacementGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "staging"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMProximityPlacementGroupExists(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) + } + + ppgName := 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 proximity placement group: %s", ppgName) + } + + client := testAccProvider.Meta().(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, ppgName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Availability Set %q (resource group: %q) does not exist", ppgName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on Proximity Placement Groups Client: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMProximityPlacementGroupDisappears(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) + } + + ppgName := 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 proximity placement group: %s", ppgName) + } + + client := testAccProvider.Meta().(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Delete(ctx, resourceGroup, ppgName) + if err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Bad: Delete on Proximity Placement Groups Client: %+v", err) + } + } + + return nil + } +} + +func testCheckAzureRMProximityPlacementGroupDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_proximity_placement_group" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).compute.ProximityPlacementGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return fmt.Errorf("Proximity placement group still exists:\n%#v", resp.ProximityPlacementGroupProperties) + } + + return nil +} + +func testAccProximityPlacementGroup_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_proximity_placement_group" "test" { + name = "acctestPPG-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + +func testAccProximityPlacementGroup_requiresImport(rInt int, location string) string { + template := testAccProximityPlacementGroup_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_proximity_placement_group" "import" { + name = "${azurerm_proximity_placement_group.test.name}" + location = "${azurerm_proximity_placement_group.test.location}" + resource_group_name = "${azurerm_proximity_placement_group.test.resource_group_name}" +} +`, template) +} + +func testAccProximityPlacementGroup_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_proximity_placement_group" "test" { + name = "acctestPPG-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt) +} + +func testAccProximityPlacementGroup_withUpdatedTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_proximity_placement_group" "test" { + name = "acctestPPG-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags = { + environment = "staging" + } +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_public_ip.go b/azurerm/resource_arm_public_ip.go index ab67d939ef4b..60e63a7db834 100644 --- a/azurerm/resource_arm_public_ip.go +++ b/azurerm/resource_arm_public_ip.go @@ -5,13 +5,16 @@ import ( "log" "strings" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/state" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -24,7 +27,7 @@ func resourceArmPublicIp() *schema.Resource { Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return nil, err } @@ -64,7 +67,7 @@ func resourceArmPublicIp() *schema.Resource { Type: schema.TypeString, Optional: true, DiffSuppressFunc: suppress.CaseDifference, - StateFunc: ignoreCaseStateFunc, + StateFunc: state.IgnoreCase, ConflictsWith: []string{"allocation_method"}, Computed: true, Deprecated: "this property has been deprecated in favor of `allocation_method` to better match the api", @@ -135,13 +138,13 @@ func resourceArmPublicIp() *schema.Resource { "zones": azure.SchemaSingleZone(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmPublicIpCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPClient + client := meta.(*ArmClient).network.PublicIPsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Public IP creation.") @@ -150,7 +153,7 @@ func resourceArmPublicIpCreateUpdate(d *schema.ResourceData, meta interface{}) e location := azure.NormalizeLocation(d.Get("location").(string)) resGroup := d.Get("resource_group_name").(string) sku := d.Get("sku").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) zones := azure.ExpandZones(d.Get("zones").([]interface{})) idleTimeout := d.Get("idle_timeout_in_minutes").(int) ipVersion := network.IPVersion(d.Get("ip_version").(string)) @@ -176,7 +179,7 @@ func resourceArmPublicIpCreateUpdate(d *schema.ResourceData, meta interface{}) e } } - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -200,7 +203,7 @@ func resourceArmPublicIpCreateUpdate(d *schema.ResourceData, meta interface{}) e PublicIPAddressVersion: ipVersion, IdleTimeoutInMinutes: utils.Int32(int32(idleTimeout)), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), Zones: zones, } @@ -252,10 +255,10 @@ func resourceArmPublicIpCreateUpdate(d *schema.ResourceData, meta interface{}) e } func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPClient + client := meta.(*ArmClient).network.PublicIPsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -288,6 +291,10 @@ func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error { d.Set("allocation_method", string(props.PublicIPAllocationMethod)) d.Set("ip_version", string(props.PublicIPAddressVersion)) + if publicIpPrefix := props.PublicIPPrefix; publicIpPrefix != nil { + d.Set("public_ip_prefix_id", publicIpPrefix.ID) + } + if settings := props.DNSSettings; settings != nil { d.Set("fqdn", settings.Fqdn) d.Set("reverse_fqdn", settings.ReverseFqdn) @@ -298,16 +305,14 @@ func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmPublicIpDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPClient + client := meta.(*ArmClient).network.PublicIPsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_public_ip_prefix.go b/azurerm/resource_arm_public_ip_prefix.go index 84a76224aa2f..e8114b766428 100644 --- a/azurerm/resource_arm_public_ip_prefix.go +++ b/azurerm/resource_arm_public_ip_prefix.go @@ -4,11 +4,12 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-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/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -59,13 +60,13 @@ func resourceArmPublicIpPrefix() *schema.Resource { "zones": azure.SchemaSingleZone(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmPublicIpPrefixCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPPrefixClient + client := meta.(*ArmClient).network.PublicIPPrefixesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Public IP Prefix creation.") @@ -75,7 +76,7 @@ func resourceArmPublicIpPrefixCreateUpdate(d *schema.ResourceData, meta interfac resGroup := d.Get("resource_group_name").(string) sku := d.Get("sku").(string) prefix_length := d.Get("prefix_length").(int) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) zones := azure.ExpandZones(d.Get("zones").([]interface{})) publicIpPrefix := network.PublicIPPrefix{ @@ -87,7 +88,7 @@ func resourceArmPublicIpPrefixCreateUpdate(d *schema.ResourceData, meta interfac PublicIPPrefixPropertiesFormat: &network.PublicIPPrefixPropertiesFormat{ PrefixLength: utils.Int32(int32(prefix_length)), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), Zones: zones, } @@ -114,10 +115,10 @@ func resourceArmPublicIpPrefixCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmPublicIpPrefixRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPPrefixClient + client := meta.(*ArmClient).network.PublicIPPrefixesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -150,16 +151,14 @@ func resourceArmPublicIpPrefixRead(d *schema.ResourceData, meta interface{}) err d.Set("ip_prefix", props.IPPrefix) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmPublicIpPrefixDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).publicIPPrefixClient + client := meta.(*ArmClient).network.PublicIPPrefixesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_public_ip_prefix_test.go b/azurerm/resource_arm_public_ip_prefix_test.go index 9ee9233e5745..fad8e039e701 100644 --- a/azurerm/resource_arm_public_ip_prefix_test.go +++ b/azurerm/resource_arm_public_ip_prefix_test.go @@ -24,7 +24,7 @@ func testCheckAzureRMPublicIPPrefixExists(resourceName string) resource.TestChec return fmt.Errorf("Bad: no resource group found in state for public ip prefix: %s", publicIpPrefixName) } - client := testAccProvider.Meta().(*ArmClient).publicIPPrefixClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPPrefixesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, publicIpPrefixName, "") @@ -54,7 +54,7 @@ func testCheckAzureRMPublicIPPrefixDisappears(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for public ip prefix: %s", publicIpPrefixName) } - client := testAccProvider.Meta().(*ArmClient).publicIPPrefixClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPPrefixesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, publicIpPrefixName) if err != nil { @@ -70,7 +70,7 @@ func testCheckAzureRMPublicIPPrefixDisappears(resourceName string) resource.Test } func testCheckAzureRMPublicIPPrefixDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).publicIPPrefixClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPPrefixesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_public_ip_test.go b/azurerm/resource_arm_public_ip_test.go index e6086d489da9..fad9584b76ef 100644 --- a/azurerm/resource_arm_public_ip_test.go +++ b/azurerm/resource_arm_public_ip_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMPublicIpStatic_basic(t *testing.T) { @@ -40,7 +41,7 @@ func TestAccAzureRMPublicIpStatic_basic(t *testing.T) { } func TestAccAzureRMPublicIpStatic_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -205,7 +206,6 @@ func TestAccAzureRMPublicIpDynamic_basic_withIPv6(t *testing.T) { }, }, }) - } func TestAccAzureRMPublicIpStatic_basic_defaultsToIPv4(t *testing.T) { @@ -542,7 +542,7 @@ func testCheckAzureRMPublicIpExists(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for public ip: %s", publicIPName) } - client := testAccProvider.Meta().(*ArmClient).publicIPClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, publicIPName, "") @@ -572,7 +572,7 @@ func testCheckAzureRMPublicIpDisappears(resourceName string) resource.TestCheckF return fmt.Errorf("Bad: no resource group found in state for public ip: %s", publicIpName) } - client := testAccProvider.Meta().(*ArmClient).publicIPClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, publicIpName) if err != nil { @@ -588,7 +588,7 @@ func testCheckAzureRMPublicIpDisappears(resourceName string) resource.TestCheckF } func testCheckAzureRMPublicIpDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).publicIPClient + client := testAccProvider.Meta().(*ArmClient).network.PublicIPsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_recovery_services_fabric.go b/azurerm/resource_arm_recovery_services_fabric.go new file mode 100644 index 000000000000..39d23026e606 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_fabric.go @@ -0,0 +1,150 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesFabric() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryServicesFabricCreate, + Read: resourceArmRecoveryServicesFabricRead, + Update: nil, + Delete: resourceArmRecoveryServicesFabricDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "location": azure.SchemaLocation(), + }, + } +} + +func resourceArmRecoveryServicesFabricCreate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + location := azure.NormalizeLocation(d.Get("location").(string)) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.FabricClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing rec overy services fabric %s (vault %s): %+v", name, vaultName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_resource_group", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + parameters := siterecovery.FabricCreationInput{ + Properties: &siterecovery.FabricCreationInputProperties{ + CustomDetails: siterecovery.AzureFabricCreationInput{ + InstanceType: "Azure", + Location: &location, + }, + }, + } + + future, err := client.Create(ctx, name, parameters) + if err != nil { + return fmt.Errorf("Error creating recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, name) + if err != nil { + return fmt.Errorf("Error retrieving recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryServicesFabricRead(d, meta) +} + +func resourceArmRecoveryServicesFabricRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + name := id.Path["replicationFabrics"] + + client := meta.(*ArmClient).recoveryServices.FabricClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + if props := resp.Properties; props != nil { + if azureDetails, isAzureDetails := props.CustomDetails.AsAzureFabricSpecificDetails(); isAzureDetails { + d.Set("location", azureDetails.Location) + } + } + d.Set("recovery_vault_name", vaultName) + return nil +} + +func resourceArmRecoveryServicesFabricDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + name := id.Path["replicationFabrics"] + + client := meta.(*ArmClient).recoveryServices.FabricClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + future, err := client.Delete(ctx, name) + if err != nil { + return fmt.Errorf("Error deleting recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + + return nil +} diff --git a/azurerm/resource_arm_recovery_services_fabric_test.go b/azurerm/resource_arm_recovery_services_fabric_test.go new file mode 100644 index 000000000000..24e4ef885b8c --- /dev/null +++ b/azurerm/resource_arm_recovery_services_fabric_test.go @@ -0,0 +1,97 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryFabric_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test" + vaultName := "azurerm_recovery_services_vault.test" + resourceName := "azurerm_recovery_services_fabric.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryFabric_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryFabricExists(resourceGroupName, vaultName, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryFabric_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_fabric" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric-%d" + location = "${azurerm_resource_group.test.location}" +} +`, rInt, location, rInt, rInt) +} + +func testCheckAzureRMRecoveryFabricExists(resourceGroupStateName, vaultStateName string, resourceStateName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + fabricState, ok := s.RootModule().Resources[resourceStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + fabricName := fabricState.Primary.Attributes["name"] + + // Ensure fabric exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.FabricClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName) + if err != nil { + return fmt.Errorf("Bad: Get on fabricClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: fabric: %q does not exist", fabricName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_network_mapping.go b/azurerm/resource_arm_recovery_services_network_mapping.go new file mode 100644 index 000000000000..5d9724bdf983 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_network_mapping.go @@ -0,0 +1,202 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/hashicorp/terraform/helper/schema" + "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesNetworkMapping() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryNetworkMappingCreate, + Read: resourceArmRecoveryNetworkMappingRead, + Delete: resourceArmRecoveryNetworkMappingDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "source_recovery_fabric_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "target_recovery_fabric_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "source_network_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_network_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + }, + } +} + +func resourceArmRecoveryNetworkMappingCreate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + fabricName := d.Get("source_recovery_fabric_name").(string) + targetFabricName := d.Get("target_recovery_fabric_name").(string) + sourceNetworkId := d.Get("source_network_id").(string) + targetNetworkId := d.Get("target_network_id").(string) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.NetworkMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + //get network name from id + parsedSourceNetworkId, err := azure.ParseAzureResourceID(sourceNetworkId) + if err != nil { + return fmt.Errorf("[ERROR] Unable to parse source_network_id '%s' (network mapping %s): %+v", sourceNetworkId, name, err) + } + sourceNetworkName, hasName := parsedSourceNetworkId.Path["virtualNetworks"] + if !hasName { + sourceNetworkName, hasName = parsedSourceNetworkId.Path["virtualnetworks"] // Handle that different APIs return different ID casings + if !hasName { + return fmt.Errorf("[ERROR] parsed source_network_id '%s' doesn't contain 'virtualnetworks'", parsedSourceNetworkId) + } + } + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, fabricName, sourceNetworkName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing recovery services fabric %s (vault %s): %+v", name, vaultName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_recovery_network_mapping", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + var parameters = siterecovery.CreateNetworkMappingInput{ + Properties: &siterecovery.CreateNetworkMappingInputProperties{ + RecoveryNetworkID: &targetNetworkId, + RecoveryFabricName: &targetFabricName, + FabricSpecificDetails: siterecovery.AzureToAzureCreateNetworkMappingInput{ + PrimaryNetworkID: &sourceNetworkId, + }, + }, + } + future, err := client.Create(ctx, fabricName, sourceNetworkName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating recovery network mapping %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating recovery network mapping %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, fabricName, sourceNetworkName, name) + if err != nil { + return fmt.Errorf("Error retrieving recovery network mapping %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryNetworkMappingRead(d, meta) +} + +func resourceArmRecoveryNetworkMappingRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + networkName := id.Path["replicationNetworks"] + name := id.Path["replicationNetworkMappings"] + + client := meta.(*ArmClient).recoveryServices.NetworkMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, networkName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + d.Set("resource_group_name", resGroup) + d.Set("recovery_vault_name", vaultName) + d.Set("source_recovery_fabric_name", fabricName) + d.Set("name", resp.Name) + if props := resp.Properties; props != nil { + d.Set("source_network_id", props.PrimaryNetworkID) + d.Set("target_network_id", props.RecoveryNetworkID) + + targetFabricId, err := azure.ParseAzureResourceID(azure.HandleAzureSdkForGoBug2824(*resp.Properties.RecoveryFabricArmID)) + if err != nil { + return err + } + d.Set("target_recovery_fabric_name", targetFabricId.Path["replicationFabrics"]) + } + + return nil +} + +func resourceArmRecoveryNetworkMappingDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + networkName := id.Path["replicationNetworks"] + name := id.Path["replicationNetworkMappings"] + + client := meta.(*ArmClient).recoveryServices.NetworkMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + future, err := client.Delete(ctx, fabricName, networkName, name) + if err != nil { + return fmt.Errorf("Error deleting recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + return nil +} diff --git a/azurerm/resource_arm_recovery_services_network_mapping_test.go b/azurerm/resource_arm_recovery_services_network_mapping_test.go new file mode 100644 index 000000000000..127663d57283 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_network_mapping_test.go @@ -0,0 +1,141 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryNetworkMapping_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test" + vaultName := "azurerm_recovery_services_vault.test" + fabricName := "azurerm_recovery_services_fabric.test1" + networkName := "azurerm_virtual_network.test1" + resourceName := "azurerm_recovery_network_mapping.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryNetworkMapping_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryNetworkMappingExists(resourceGroupName, vaultName, fabricName, networkName, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryNetworkMapping_basic(rInt int, location string, altLocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG1-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_fabric" "test1" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric1-%d" + location = "${azurerm_resource_group.test.location}" +} + +resource "azurerm_recovery_services_fabric" "test2" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric2-%d" + location = "%s" + depends_on = ["azurerm_recovery_services_fabric.test1"] +} + +resource "azurerm_virtual_network" "test1" { + name = "network1-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = [ "192.168.1.0/24" ] + location = "${azurerm_recovery_services_fabric.test1.location}" +} + +resource "azurerm_virtual_network" "test2" { + name = "network2-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = [ "192.168.2.0/24" ] + location = "${azurerm_recovery_services_fabric.test2.location}" +} + +resource "azurerm_recovery_network_mapping" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "mapping-%d" + source_recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + target_recovery_fabric_name = "${azurerm_recovery_services_fabric.test2.name}" + source_network_id = "${azurerm_virtual_network.test1.id}" + target_network_id = "${azurerm_virtual_network.test2.id}" +} +`, rInt, location, rInt, rInt, rInt, altLocation, rInt, rInt, rInt) +} + +func testCheckAzureRMRecoveryNetworkMappingExists(resourceGroupStateName, vaultStateName string, fabricStateName string, networkStateName string, networkStateMappingName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + fabricState, ok := s.RootModule().Resources[fabricStateName] + if !ok { + return fmt.Errorf("Not found: %s", fabricStateName) + } + networkState, ok := s.RootModule().Resources[networkStateName] + if !ok { + return fmt.Errorf("Not found: %s", fabricStateName) + } + networkMappingState, ok := s.RootModule().Resources[networkStateMappingName] + if !ok { + return fmt.Errorf("Not found: %s", networkStateMappingName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + fabricName := fabricState.Primary.Attributes["name"] + networkName := networkState.Primary.Attributes["name"] + mappingName := networkMappingState.Primary.Attributes["name"] + + // Ensure mapping exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.NetworkMappingClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, networkName, mappingName) + if err != nil { + return fmt.Errorf("Bad: Get on networkMappingClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: networkMapping: %q does not exist", mappingName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_protected_vm.go b/azurerm/resource_arm_recovery_services_protected_vm.go index d5e44b5436ae..6378baf04e7e 100644 --- a/azurerm/resource_arm_recovery_services_protected_vm.go +++ b/azurerm/resource_arm_recovery_services_protected_vm.go @@ -4,17 +4,17 @@ import ( "context" "fmt" "log" - "regexp" "strings" "time" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2017-07-01/backup" "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/utils" ) @@ -35,13 +35,10 @@ func resourceArmRecoveryServicesProtectedVm() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "recovery_vault_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile("^[a-zA-Z][-a-zA-Z0-9]{1,49}$"), - "Recovery Service Vault name must be 2 - 50 characters long, start with a letter, contain only letters, numbers and hyphens.", - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, }, "source_vm_id": { @@ -54,21 +51,20 @@ func resourceArmRecoveryServicesProtectedVm() *schema.Resource { "backup_policy_id": { Type: schema.TypeString, Required: true, - ForceNew: true, ValidateFunc: azure.ValidateResourceID, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmRecoveryServicesProtectedVmCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectedItemsClient + client := meta.(*ArmClient).recoveryServices.ProtectedItemsClient ctx := meta.(*ArmClient).StopContext resourceGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) vaultName := d.Get("recovery_vault_name").(string) vmId := d.Get("source_vm_id").(string) @@ -89,7 +85,7 @@ func resourceArmRecoveryServicesProtectedVmCreateUpdate(d *schema.ResourceData, log.Printf("[DEBUG] Creating/updating Recovery Service Protected VM %s (resource group %q)", protectedItemName, resourceGroup) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err2 := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") if err2 != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -103,7 +99,7 @@ func resourceArmRecoveryServicesProtectedVmCreateUpdate(d *schema.ResourceData, } item := backup.ProtectedItemResource{ - Tags: expandTags(tags), + Tags: tags.Expand(t), Properties: &backup.AzureIaaSComputeVMProtectedItem{ PolicyID: &policyId, ProtectedItemType: backup.ProtectedItemTypeMicrosoftClassicComputevirtualMachines, @@ -118,21 +114,22 @@ func resourceArmRecoveryServicesProtectedVmCreateUpdate(d *schema.ResourceData, return fmt.Errorf("Error creating/updating Recovery Service Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err) } - resp, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, true, vaultName, resourceGroup, containerName, protectedItemName) + resp, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, true, vaultName, resourceGroup, containerName, protectedItemName, policyId, d.IsNewResource()) if err != nil { return err } - id := strings.Replace(*resp.ID, "Subscriptions", "subscriptions", 1) + + id := strings.Replace(*resp.ID, "Subscriptions", "subscriptions", 1) // This code is a workaround for this bug https://github.com/Azure/azure-sdk-for-go/issues/2824 d.SetId(id) return resourceArmRecoveryServicesProtectedVmRead(d, meta) } func resourceArmRecoveryServicesProtectedVmRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectedItemsClient + client := meta.(*ArmClient).recoveryServices.ProtectedItemsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -167,16 +164,14 @@ func resourceArmRecoveryServicesProtectedVmRead(d *schema.ResourceData, meta int } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRecoveryServicesProtectedVmDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectedItemsClient + client := meta.(*ArmClient).recoveryServices.ProtectedItemsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -195,20 +190,19 @@ func resourceArmRecoveryServicesProtectedVmDelete(d *schema.ResourceData, meta i } } - if _, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, false, vaultName, resourceGroup, containerName, protectedItemName); err != nil { + if _, err := resourceArmRecoveryServicesProtectedVmWaitForState(client, ctx, false, vaultName, resourceGroup, containerName, protectedItemName, "", false); err != nil { return err } return nil } -func resourceArmRecoveryServicesProtectedVmWaitForState(client backup.ProtectedItemsGroupClient, ctx context.Context, found bool, vaultName, resourceGroup, containerName, protectedItemName string) (backup.ProtectedItemResource, error) { +func resourceArmRecoveryServicesProtectedVmWaitForState(client *backup.ProtectedItemsGroupClient, ctx context.Context, found bool, vaultName, resourceGroup, containerName, protectedItemName string, policyId string, newResource bool) (backup.ProtectedItemResource, error) { state := &resource.StateChangeConf{ Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Delay: 10 * time.Second, Refresh: func() (interface{}, string, error) { - resp, err := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { @@ -216,8 +210,23 @@ func resourceArmRecoveryServicesProtectedVmWaitForState(client backup.ProtectedI } return resp, "Error", fmt.Errorf("Error making Read request on Recovery Service Protected VM %q (Resource Group %q): %+v", protectedItemName, resourceGroup, err) + } else if !newResource && policyId != "" { + if properties := resp.Properties; properties != nil { + if vm, ok := properties.AsAzureIaaSComputeVMProtectedItem(); ok { + if v := vm.PolicyID; v != nil { + if strings.Replace(*v, "Subscriptions", "subscriptions", 1) != policyId { + return resp, "NotFound", nil + } + } else { + return resp, "Error", fmt.Errorf("Error reading policy ID attribute nil on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } + } else { + return resp, "Error", fmt.Errorf("Error reading properties on Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } + } else { + return resp, "Error", fmt.Errorf("Error reading properties on empty Recovery Service Protected VM %q (Resource Group %q)", protectedItemName, resourceGroup) + } } - return resp, "Found", nil }, } diff --git a/azurerm/resource_arm_recovery_services_protected_vm_test.go b/azurerm/resource_arm_recovery_services_protected_vm_test.go index 5f750fea9832..6abe04ee3e16 100644 --- a/azurerm/resource_arm_recovery_services_protected_vm_test.go +++ b/azurerm/resource_arm_recovery_services_protected_vm_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -42,7 +43,7 @@ func TestAccAzureRMRecoveryServicesProtectedVm_basic(t *testing.T) { } func TestAccAzureRMRecoveryServicesProtectedVm_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -103,6 +104,55 @@ func TestAccAzureRMRecoveryServicesProtectedVm_separateResourceGroups(t *testing }) } +func TestAccAzureRMRecoveryServicesProtectedVm_updateBackupPolicyId(t *testing.T) { + virtualMachine := "azurerm_virtual_machine.test" + protectedVmResourceName := "azurerm_recovery_services_protected_vm.test" + fBackupPolicyResourceName := "azurerm_recovery_services_protection_policy_vm.test" + sBackupPolicyResourceName := "azurerm_recovery_services_protection_policy_vm.test_change_backup" + + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRecoveryServicesProtectedVmDestroy, + Steps: []resource.TestStep{ + { // Create resources and link first backup policy id + ResourceName: fBackupPolicyResourceName, + Config: testAccAzureRMRecoveryServicesProtectedVm_linkFirstBackupPolicy(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(protectedVmResourceName, "backup_policy_id", fBackupPolicyResourceName, "id"), + ), + }, + { // Modify backup policy id to the second one + // Set Destroy false to prevent error from cleaning up dangling resource + ResourceName: sBackupPolicyResourceName, + Config: testAccAzureRMRecoveryServicesProtectedVm_linkSecondBackupPolicy(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(protectedVmResourceName, "backup_policy_id", sBackupPolicyResourceName, "id"), + ), + }, + { // Remove backup policy link + // Backup policy link will need to be removed first so the VM's backup policy subsequently reverts to Default + // Azure API is quite sensitive, adding the step to control resource cleanup order + ResourceName: fBackupPolicyResourceName, + Config: testAccAzureRMRecoveryServicesProtectedVm_withVM(ri, testLocation()), + Check: resource.ComposeTestCheckFunc(), + }, + { // Then VM can be removed + ResourceName: virtualMachine, + Config: testAccAzureRMRecoveryServicesProtectedVm_withSecondPolicy(ri, testLocation()), + Check: resource.ComposeTestCheckFunc(), + }, + { // Remove backup policies and vault + ResourceName: protectedVmResourceName, + Config: testAccAzureRMRecoveryServicesProtectedVm_basePolicyTest(ri, testLocation()), + Check: resource.ComposeTestCheckFunc(), + }, + }, + }) +} + func testCheckAzureRMRecoveryServicesProtectedVmDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_recovery_services_protected_vm" { @@ -125,7 +175,7 @@ func testCheckAzureRMRecoveryServicesProtectedVmDestroy(s *terraform.State) erro protectedItemName := fmt.Sprintf("VM;iaasvmcontainerv2;%s;%s", parsedVmId.ResourceGroup, vmName) containerName := fmt.Sprintf("iaasvmcontainer;iaasvmcontainerv2;%s;%s", parsedVmId.ResourceGroup, vmName) - client := testAccProvider.Meta().(*ArmClient).recoveryServicesProtectedItemsClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ProtectedItemsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") @@ -172,7 +222,7 @@ func testCheckAzureRMRecoveryServicesProtectedVmExists(resourceName string) reso protectedItemName := fmt.Sprintf("VM;iaasvmcontainerv2;%s;%s", parsedVmId.ResourceGroup, vmName) containerName := fmt.Sprintf("iaasvmcontainer;iaasvmcontainerv2;%s;%s", parsedVmId.ResourceGroup, vmName) - client := testAccProvider.Meta().(*ArmClient).recoveryServicesProtectedItemsClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ProtectedItemsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, vaultName, resourceGroup, "Azure", containerName, protectedItemName, "") @@ -331,6 +381,209 @@ resource "azurerm_recovery_services_protected_vm" "test" { `, testAccAzureRMRecoveryServicesProtectedVm_base(rInt, location)) } +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_basePolicyTest(rInt int, location string) string { + rstr := strconv.Itoa(rInt) + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_virtual_network" "test" { + name = "vnet" + location = "${azurerm_resource_group.test.location}" + address_space = ["10.0.0.0/16"] + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctest_subnet" + virtual_network_name = "${azurerm_virtual_network.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_prefix = "10.0.10.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctest_nic" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "acctestipconfig" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "Dynamic" + public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_public_ip" "test" { + name = "acctest-ip" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + allocation_method = "Dynamic" + domain_name_label = "acctestip%[1]d" +} + +resource "azurerm_storage_account" "test" { + name = "acctest%[3]s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_managed_disk" "test" { + name = "acctest-datadisk" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1023" +} +`, rInt, location, rstr[len(rstr)-5:]) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_withVault(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} +`, testAccAzureRMRecoveryServicesProtectedVm_basePolicyTest(rInt, location), rInt) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_withFirstPolicy(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_recovery_services_protection_policy_vm" "test" { + name = "acctest-%[2]d" + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + + backup { + frequency = "Daily" + time = "23:00" + } + + retention_daily { + count = 10 + } +} +`, testAccAzureRMRecoveryServicesProtectedVm_withVault(rInt, location), rInt) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_withSecondPolicy(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_recovery_services_protection_policy_vm" "test_change_backup" { + name = "acctest2-%[2]d" + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + + backup { + frequency = "Daily" + time = "23:00" + } + + retention_daily { + count = 15 + } +} +`, testAccAzureRMRecoveryServicesProtectedVm_withFirstPolicy(rInt, location), rInt) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_withVM(rInt int, location string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_virtual_machine" "test" { + name = "acctestvm-%[2]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + vm_size = "Standard_A0" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + delete_os_disk_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "acctest-osdisk" + managed_disk_type = "Standard_LRS" + caching = "ReadWrite" + create_option = "FromImage" + } + + storage_data_disk { + name = "acctest-datadisk" + managed_disk_id = "${azurerm_managed_disk.test.id}" + managed_disk_type = "Standard_LRS" + disk_size_gb = "${azurerm_managed_disk.test.disk_size_gb}" + create_option = "Attach" + lun = 0 + } + + os_profile { + computer_name = "acctest" + admin_username = "vmadmin" + admin_password = "Password123!@#" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + boot_diagnostics { + enabled = true + storage_uri = "${azurerm_storage_account.test.primary_blob_endpoint}" + } +} +`, testAccAzureRMRecoveryServicesProtectedVm_withSecondPolicy(rInt, location), rInt) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_linkFirstBackupPolicy(rInt int, location string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_recovery_services_protected_vm" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + source_vm_id = "${azurerm_virtual_machine.test.id}" + backup_policy_id = "${azurerm_recovery_services_protection_policy_vm.test.id}" +} +`, testAccAzureRMRecoveryServicesProtectedVm_withVM(rInt, location)) +} + +// For update backup policy id test +func testAccAzureRMRecoveryServicesProtectedVm_linkSecondBackupPolicy(rInt int, location string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_recovery_services_protected_vm" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + source_vm_id = "${azurerm_virtual_machine.test.id}" + backup_policy_id = "${azurerm_recovery_services_protection_policy_vm.test_change_backup.id}" +} +`, testAccAzureRMRecoveryServicesProtectedVm_withVM(rInt, location)) +} + func testAccAzureRMRecoveryServicesProtectedVm_requiresImport(rInt int, location string) string { return fmt.Sprintf(` %s diff --git a/azurerm/resource_arm_recovery_services_protection_container.go b/azurerm/resource_arm_recovery_services_protection_container.go new file mode 100644 index 000000000000..da6582dc86b2 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_protection_container.go @@ -0,0 +1,148 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesProtectionContainer() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryServicesProtectionContainerCreate, + Read: resourceArmRecoveryServicesProtectionContainerRead, + Update: nil, + Delete: resourceArmSiteRecoveryProtectionContainerDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "recovery_fabric_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + } +} + +func resourceArmRecoveryServicesProtectionContainerCreate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + fabricName := d.Get("recovery_fabric_name").(string) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.ProtectionContainerClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, fabricName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_recovery_services_protection_container", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + parameters := siterecovery.CreateProtectionContainerInput{ + Properties: &siterecovery.CreateProtectionContainerInputProperties{}, + } + + future, err := client.Create(ctx, fabricName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + + resp, err := client.Get(ctx, fabricName, name) + if err != nil { + return fmt.Errorf("Error retrieving site recovery protection container %s (fabric %s): %+v", name, fabricName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryServicesProtectionContainerRead(d, meta) +} + +func resourceArmRecoveryServicesProtectionContainerRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + name := id.Path["replicationProtectionContainers"] + + client := meta.(*ArmClient).recoveryServices.ProtectionContainerClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + d.Set("recovery_vault_name", vaultName) + d.Set("recovery_fabric_name", fabricName) + return nil +} + +func resourceArmSiteRecoveryProtectionContainerDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + name := id.Path["replicationProtectionContainers"] + + client := meta.(*ArmClient).recoveryServices.ProtectionContainerClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + future, err := client.Delete(ctx, fabricName, name) + if err != nil { + return fmt.Errorf("Error deleting recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services protection container %s (fabric %s): %+v", name, fabricName, err) + } + + return nil +} diff --git a/azurerm/resource_arm_recovery_services_protection_container_mapping.go b/azurerm/resource_arm_recovery_services_protection_container_mapping.go new file mode 100644 index 000000000000..bbf0c9a98cc9 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_protection_container_mapping.go @@ -0,0 +1,191 @@ +package azurerm + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesProtectionContainerMapping() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryServicesContainerMappingCreate, + Read: resourceArmRecoveryServicesContainerMappingRead, + Update: nil, + Delete: resourceArmSiteRecoveryServicesContainerMappingDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "recovery_fabric_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "recovery_replication_policy_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "recovery_source_protection_container_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "recovery_target_protection_container_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + }, + } +} + +func resourceArmRecoveryServicesContainerMappingCreate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + fabricName := d.Get("recovery_fabric_name").(string) + policyId := d.Get("recovery_replication_policy_id").(string) + protectionContainerName := d.Get("recovery_source_protection_container_name").(string) + targetContainerId := d.Get("recovery_target_protection_container_id").(string) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.ContainerMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, fabricName, protectionContainerName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing recovery services protection container mapping %s (fabric %s, container %s): %+v", name, fabricName, protectionContainerName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_recovery_services_protection_container_mapping", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + var parameters = siterecovery.CreateProtectionContainerMappingInput{ + Properties: &siterecovery.CreateProtectionContainerMappingInputProperties{ + TargetProtectionContainerID: &targetContainerId, + PolicyID: &policyId, + ProviderSpecificInput: siterecovery.ReplicationProviderSpecificContainerMappingInput{}, + }, + } + future, err := client.Create(ctx, fabricName, protectionContainerName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, fabricName, protectionContainerName, name) + if err != nil { + return fmt.Errorf("Error retrieving site recovery protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryServicesContainerMappingRead(d, meta) +} + +func resourceArmRecoveryServicesContainerMappingRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + protectionContainerName := id.Path["replicationProtectionContainers"] + name := id.Path["replicationProtectionContainerMappings"] + + client := meta.(*ArmClient).recoveryServices.ContainerMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, protectionContainerName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + d.Set("resource_group_name", resGroup) + d.Set("recovery_vault_name", vaultName) + d.Set("recovery_fabric_name", fabricName) + d.Set("recovery_source_protection_container_name", resp.Properties.SourceProtectionContainerFriendlyName) + d.Set("name", resp.Name) + d.Set("recovery_replication_policy_id", resp.Properties.PolicyID) + d.Set("recovery_target_protection_container_id", resp.Properties.TargetProtectionContainerID) + return nil +} + +func resourceArmSiteRecoveryServicesContainerMappingDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + protectionContainerName := id.Path["replicationProtectionContainers"] + name := id.Path["replicationProtectionContainerMappings"] + instanceType := string(siterecovery.InstanceTypeBasicReplicationProviderSpecificContainerMappingInputInstanceTypeA2A) + + client := meta.(*ArmClient).recoveryServices.ContainerMappingClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + input := siterecovery.RemoveProtectionContainerMappingInput{ + Properties: &siterecovery.RemoveProtectionContainerMappingInputProperties{ + ProviderSpecificInput: &siterecovery.ReplicationProviderContainerUnmappingInput{ + InstanceType: &instanceType, + }, + }, + } + + future, err := client.Delete(ctx, fabricName, protectionContainerName, name, input) + if err != nil { + return fmt.Errorf("Error deleting recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services protection container mapping %s (vault %s): %+v", name, vaultName, err) + } + + return nil +} diff --git a/azurerm/resource_arm_recovery_services_protection_container_mapping_test.go b/azurerm/resource_arm_recovery_services_protection_container_mapping_test.go new file mode 100644 index 000000000000..0a9c0807c2b9 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_protection_container_mapping_test.go @@ -0,0 +1,149 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryProtectionContainerMapping_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test1" + vaultName := "azurerm_recovery_services_vault.test" + fabricName := "azurerm_recovery_services_fabric.test1" + protectionContainerName := "azurerm_recovery_services_protection_container.test1" + resourceName := "azurerm_recovery_services_protection_container_mapping.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryProtectionContainerMapping_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryProtectionContainerMappingExists(resourceGroupName, vaultName, fabricName, protectionContainerName, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryProtectionContainerMapping_basic(rInt int, location string, altLocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test1" { + name = "acctestRG1-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test1.location}" + resource_group_name = "${azurerm_resource_group.test1.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_fabric" "test1" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric1-%d" + location = "${azurerm_resource_group.test1.location}" +} + +resource "azurerm_recovery_services_fabric" "test2" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric2-%d" + location = "%s" + depends_on = ["azurerm_recovery_services_fabric.test1"] +} + +resource "azurerm_recovery_services_protection_container" "test1" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + name = "acctest-protection-cont1-%d" +} + +resource "azurerm_recovery_services_protection_container" "test2" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test2.name}" + name = "acctest-protection-cont2-%d" +} + +resource "azurerm_recovery_services_replication_policy" "test" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-policy-%d" + recovery_point_retention_in_minutes = "${24 * 60}" + application_consistent_snapshot_frequency_in_minutes = "${4 * 60}" +} + +resource "azurerm_recovery_services_protection_container_mapping" "test" { + resource_group_name = "${azurerm_resource_group.test1.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + recovery_source_protection_container_name = "${azurerm_recovery_services_protection_container.test1.name}" + recovery_target_protection_container_id = "${azurerm_recovery_services_protection_container.test2.id}" + recovery_replication_policy_id = "${azurerm_recovery_services_replication_policy.test.id}" + name = "mapping-%d" +} +`, rInt, location, rInt, rInt, rInt, altLocation, rInt, rInt, rInt, rInt) +} + +func testCheckAzureRMRecoveryProtectionContainerMappingExists(resourceGroupStateName, vaultStateName string, resourceStateName string, protectionContainerStateName string, protectionContainerStateMappingName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + fabricState, ok := s.RootModule().Resources[resourceStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + protectionContainerState, ok := s.RootModule().Resources[protectionContainerStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + protectionContainerMappingState, ok := s.RootModule().Resources[protectionContainerStateMappingName] + if !ok { + return fmt.Errorf("Not found: %s", protectionContainerStateMappingName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + fabricName := fabricState.Primary.Attributes["name"] + protectionContainerName := protectionContainerState.Primary.Attributes["name"] + mappingName := protectionContainerMappingState.Primary.Attributes["name"] + + // Ensure mapping exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ContainerMappingClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, protectionContainerName, mappingName) + if err != nil { + return fmt.Errorf("Bad: Get on fabricClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: fabric: %q does not exist", fabricName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_protection_container_test.go b/azurerm/resource_arm_recovery_services_protection_container_test.go new file mode 100644 index 000000000000..eb472cda58c2 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_protection_container_test.go @@ -0,0 +1,111 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryProtectionContainer_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test" + vaultName := "azurerm_recovery_services_vault.test" + fabricName := "azurerm_recovery_services_fabric.test" + resourceName := "azurerm_recovery_services_protection_container.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryProtectionContainer_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryProtectionContainerExists(resourceGroupName, vaultName, fabricName, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryProtectionContainer_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_fabric" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric-%d" + location = "${azurerm_resource_group.test.location}" +} + +resource "azurerm_recovery_services_protection_container" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test.name}" + name = "acctest-protection-cont-%d" +} + +`, rInt, location, rInt, rInt, rInt) +} + +func testCheckAzureRMRecoveryProtectionContainerExists(resourceGroupStateName, vaultStateName string, resourceStateName string, protectionContainerStateName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + fabricState, ok := s.RootModule().Resources[resourceStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + protectionContainerState, ok := s.RootModule().Resources[protectionContainerStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + fabricName := fabricState.Primary.Attributes["name"] + protectionContainerName := protectionContainerState.Primary.Attributes["name"] + + // Ensure fabric exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ProtectionContainerClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, protectionContainerName) + if err != nil { + return fmt.Errorf("Bad: Get on fabricClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: fabric: %q does not exist", fabricName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_protection_policy_vm.go b/azurerm/resource_arm_recovery_services_protection_policy_vm.go index 227613ffaa45..a8a0792cd9fa 100644 --- a/azurerm/resource_arm_recovery_services_protection_policy_vm.go +++ b/azurerm/resource_arm_recovery_services_protection_policy_vm.go @@ -10,6 +10,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2017-07-01/backup" @@ -49,13 +51,10 @@ func resourceArmRecoveryServicesProtectionPolicyVm() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "recovery_vault_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile("^[a-zA-Z][-a-zA-Z0-9]{1,49}$"), - "Recovery Service Vault name must be 2 - 50 characters long, start with a letter, contain only letters, numbers and hyphens.", - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, }, "timezone": { @@ -242,13 +241,12 @@ func resourceArmRecoveryServicesProtectionPolicyVm() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, //if daily, we need daily retention //if weekly daily cannot be set, and we need weekly CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - _, hasDaily := diff.GetOk("retention_daily") _, hasWeekly := diff.GetOk("retention_weekly") @@ -279,13 +277,13 @@ func resourceArmRecoveryServicesProtectionPolicyVm() *schema.Resource { } func resourceArmRecoveryServicesProtectionPolicyVmCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectionPoliciesClient + client := meta.(*ArmClient).recoveryServices.ProtectionPoliciesClient ctx := meta.(*ArmClient).StopContext policyName := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) vaultName := d.Get("recovery_vault_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) log.Printf("[DEBUG] Creating/updating Recovery Service Protection Policy %s (resource group %q)", policyName, resourceGroup) @@ -297,7 +295,7 @@ func resourceArmRecoveryServicesProtectionPolicyVmCreateUpdate(d *schema.Resourc } times := append(make([]date.Time, 0), date.Time{Time: dateOfDay}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err2 := client.Get(ctx, vaultName, resourceGroup, policyName) if err2 != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -311,7 +309,7 @@ func resourceArmRecoveryServicesProtectionPolicyVmCreateUpdate(d *schema.Resourc } policy := backup.ProtectionPolicyResource{ - Tags: expandTags(tags), + Tags: tags.Expand(t), Properties: &backup.AzureIaaSVMProtectionPolicy{ TimeZone: utils.String(d.Get("timezone").(string)), BackupManagementType: backup.BackupManagementTypeAzureIaasVM, @@ -341,10 +339,10 @@ func resourceArmRecoveryServicesProtectionPolicyVmCreateUpdate(d *schema.Resourc } func resourceArmRecoveryServicesProtectionPolicyVmRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectionPoliciesClient + client := meta.(*ArmClient).recoveryServices.ProtectionPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -370,7 +368,6 @@ func resourceArmRecoveryServicesProtectionPolicyVmRead(d *schema.ResourceData, m d.Set("recovery_vault_name", vaultName) if properties, ok := resp.Properties.AsAzureIaaSVMProtectionPolicy(); ok && properties != nil { - d.Set("timezone", properties.TimeZone) if schedule, ok := properties.SchedulePolicy.AsSimpleSchedulePolicy(); ok && schedule != nil { @@ -411,20 +408,17 @@ func resourceArmRecoveryServicesProtectionPolicyVmRead(d *schema.ResourceData, m } else { d.Set("retention_yearly", nil) } - } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRecoveryServicesProtectionPolicyVmDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesProtectionPoliciesClient + client := meta.(*ArmClient).recoveryServices.ProtectionPoliciesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -702,13 +696,12 @@ func flattenArmRecoveryServicesProtectionPolicyRetentionWeeklyFormat(retention * return weekdays, weeks } -func resourceArmRecoveryServicesProtectionPolicyWaitForState(client backup.ProtectionPoliciesClient, ctx context.Context, found bool, vaultName, resourceGroup, policyName string) (backup.ProtectionPolicyResource, error) { +func resourceArmRecoveryServicesProtectionPolicyWaitForState(client *backup.ProtectionPoliciesClient, ctx context.Context, found bool, vaultName, resourceGroup, policyName string) (backup.ProtectionPolicyResource, error) { state := &resource.StateChangeConf{ Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Delay: 10 * time.Second, Refresh: func() (interface{}, string, error) { - resp, err := client.Get(ctx, vaultName, resourceGroup, policyName) if err != nil { if utils.ResponseWasNotFound(resp.Response) { diff --git a/azurerm/resource_arm_recovery_services_protection_policy_vm_test.go b/azurerm/resource_arm_recovery_services_protection_policy_vm_test.go index 0c13193b1e4c..b05384019794 100644 --- a/azurerm/resource_arm_recovery_services_protection_policy_vm_test.go +++ b/azurerm/resource_arm_recovery_services_protection_policy_vm_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -34,7 +35,7 @@ func TestAccAzureRMRecoveryServicesProtectionPolicyVm_basicDaily(t *testing.T) { } func TestAccAzureRMRecoveryServicesProtectionPolicyVm_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -254,7 +255,7 @@ func TestAccAzureRMRecoveryServicesProtectionPolicyVm_updateWeeklyToPartial(t *t } func testCheckAzureRMRecoveryServicesProtectionPolicyVmDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).recoveryServicesProtectionPoliciesClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ProtectionPoliciesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -283,7 +284,7 @@ func testCheckAzureRMRecoveryServicesProtectionPolicyVmDestroy(s *terraform.Stat func testCheckAzureRMRecoveryServicesProtectionPolicyVmExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).recoveryServicesProtectionPoliciesClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ProtectionPoliciesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext // Ensure we have enough information in state to look up in API diff --git a/azurerm/resource_arm_recovery_services_replicated_vm.go b/azurerm/resource_arm_recovery_services_replicated_vm.go new file mode 100644 index 000000000000..129705870978 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_replicated_vm.go @@ -0,0 +1,341 @@ +package azurerm + +import ( + "bytes" + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "github.com/hashicorp/terraform/helper/hashcode" + "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/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesReplicatedVm() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryReplicatedItemCreate, + Read: resourceArmRecoveryReplicatedItemRead, + Delete: resourceArmRecoveryReplicatedItemDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "source_recovery_fabric_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "source_vm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_recovery_fabric_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "recovery_replication_policy_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "source_recovery_protection_container_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "target_recovery_protection_container_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_resource_group_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_availability_set_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "managed_disk": { + Type: schema.TypeSet, + ConfigMode: schema.SchemaConfigModeAttr, + Optional: true, + ForceNew: true, + Set: resourceArmRecoveryReplicatedVmDiskHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disk_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + DiffSuppressFunc: suppress.CaseDifference, + }, + "staging_storage_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_resource_group_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_disk_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.StandardLRS), + string(compute.PremiumLRS), + string(compute.StandardSSDLRS), + string(compute.UltraSSDLRS), + }, true), + DiffSuppressFunc: suppress.CaseDifference, + }, + "target_replica_disk_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.StandardLRS), + string(compute.PremiumLRS), + string(compute.StandardSSDLRS), + string(compute.UltraSSDLRS), + }, true), + DiffSuppressFunc: suppress.CaseDifference, + }, + }, + }, + }, + }, + } +} + +func resourceArmRecoveryReplicatedItemCreate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + fabricName := d.Get("source_recovery_fabric_name").(string) + sourceVmId := d.Get("source_vm_id").(string) + policyId := d.Get("recovery_replication_policy_id").(string) + sourceProtectionContainerName := d.Get("source_recovery_protection_container_name").(string) + targetProtectionContainerId := d.Get("target_recovery_protection_container_id").(string) + targetResourceGroupId := d.Get("target_resource_group_id").(string) + + var targetAvailabilitySetID *string + if id, isSet := d.GetOk("target_availability_set_id"); isSet { + tmp := id.(string) + targetAvailabilitySetID = &tmp + } else { + targetAvailabilitySetID = nil + } + + client := meta.(*ArmClient).recoveryServices.ReplicationMigrationItemsClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, fabricName, sourceProtectionContainerName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing recovery services replicated vm %s (vault %s): %+v", name, vaultName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_recovery_replicated_vm", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + managedDisks := []siterecovery.A2AVMManagedDiskInputDetails{} + + for _, raw := range d.Get("managed_disk").(*schema.Set).List() { + diskInput := raw.(map[string]interface{}) + diskId := diskInput["disk_id"].(string) + primaryStagingAzureStorageAccountID := diskInput["staging_storage_account_id"].(string) + recoveryResourceGroupId := diskInput["target_resource_group_id"].(string) + targetReplicaDiskType := diskInput["target_replica_disk_type"].(string) + targetDiskType := diskInput["target_disk_type"].(string) + + managedDisks = append(managedDisks, siterecovery.A2AVMManagedDiskInputDetails{ + DiskID: &diskId, + PrimaryStagingAzureStorageAccountID: &primaryStagingAzureStorageAccountID, + RecoveryResourceGroupID: &recoveryResourceGroupId, + RecoveryReplicaDiskAccountType: &targetReplicaDiskType, + RecoveryTargetDiskAccountType: &targetDiskType, + }) + } + + var parameters = siterecovery.EnableProtectionInput{ + Properties: &siterecovery.EnableProtectionInputProperties{ + PolicyID: &policyId, + ProviderSpecificDetails: siterecovery.A2AEnableProtectionInput{ + FabricObjectID: &sourceVmId, + RecoveryContainerID: &targetProtectionContainerId, + RecoveryResourceGroupID: &targetResourceGroupId, + RecoveryAvailabilitySetID: targetAvailabilitySetID, + VMManagedDisks: &managedDisks, + }, + }, + } + future, err := client.Create(ctx, fabricName, sourceProtectionContainerName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating replicated vm %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating replicated vm %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, fabricName, sourceProtectionContainerName, name) + if err != nil { + return fmt.Errorf("Error retrieving replicated vm %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryReplicatedItemRead(d, meta) +} + +func resourceArmRecoveryReplicatedItemRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + protectionContainerName := id.Path["replicationProtectionContainers"] + name := id.Path["replicationProtectedItems"] + + client := meta.(*ArmClient).recoveryServices.ReplicationMigrationItemsClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, protectionContainerName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services replicated vm %s (vault %s): %+v", name, vaultName, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resGroup) + d.Set("recovery_vault_name", vaultName) + d.Set("source_recovery_fabric_name", fabricName) + d.Set("target_recovery_fabric_id", resp.Properties.RecoveryFabricID) + d.Set("recovery_replication_policy_id", resp.Properties.PolicyID) + d.Set("source_recovery_protection_container_name", protectionContainerName) + d.Set("target_recovery_protection_container_id", resp.Properties.RecoveryContainerID) + + if a2aDetails, isA2a := resp.Properties.ProviderSpecificDetails.AsA2AReplicationDetails(); isA2a { + d.Set("source_vm_id", a2aDetails.FabricObjectID) + d.Set("target_resource_group_id", a2aDetails.RecoveryAzureResourceGroupID) + d.Set("target_availability_set_id", a2aDetails.RecoveryAvailabilitySet) + if a2aDetails.ProtectedManagedDisks != nil { + disksOutput := make([]interface{}, 0) + for _, disk := range *a2aDetails.ProtectedManagedDisks { + diskOutput := make(map[string]interface{}) + diskOutput["disk_id"] = *disk.DiskID + diskOutput["staging_storage_account_id"] = *disk.PrimaryStagingAzureStorageAccountID + diskOutput["target_resource_group_id"] = *disk.RecoveryResourceGroupID + diskOutput["target_replica_disk_type"] = *disk.RecoveryReplicaDiskAccountType + diskOutput["target_disk_type"] = *disk.RecoveryTargetDiskAccountType + + disksOutput = append(disksOutput, diskOutput) + } + d.Set("managed_disk", schema.NewSet(resourceArmRecoveryReplicatedVmDiskHash, disksOutput)) + } + } + + return nil +} + +func resourceArmRecoveryReplicatedItemDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + fabricName := id.Path["replicationFabrics"] + protectionContainerName := id.Path["replicationProtectionContainers"] + name := id.Path["replicationProtectedItems"] + + disableProtectionInput := siterecovery.DisableProtectionInput{ + Properties: &siterecovery.DisableProtectionInputProperties{ + DisableProtectionReason: siterecovery.NotSpecified, + ReplicationProviderInput: siterecovery.DisableProtectionProviderSpecificInput{}, + }, + } + + client := meta.(*ArmClient).recoveryServices.ReplicationMigrationItemsClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + future, err := client.Delete(ctx, fabricName, protectionContainerName, name, disableProtectionInput) + if err != nil { + return fmt.Errorf("Error deleting recovery services replicated vm %s (vault %s): %+v", name, vaultName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services replicated vm %s (vault %s): %+v", name, vaultName, err) + } + return nil +} + +func resourceArmRecoveryReplicatedVmDiskHash(v interface{}) int { + var buf bytes.Buffer + + if m, ok := v.(map[string]interface{}); ok { + if v, ok := m["disk_id"]; ok { + buf.WriteString(strings.ToLower(v.(string))) + } + } + + return hashcode.String(buf.String()) +} diff --git a/azurerm/resource_arm_recovery_services_replicated_vm_test.go b/azurerm/resource_arm_recovery_services_replicated_vm_test.go new file mode 100644 index 000000000000..2224e5fb2c32 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_replicated_vm_test.go @@ -0,0 +1,263 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryReplicatedVm_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test2" + vaultName := "azurerm_recovery_services_vault.test" + fabricName := "azurerm_recovery_services_fabric.test1" + protectionContainerName := "azurerm_recovery_services_protection_container.test1" + replicationName := "azurerm_recovery_replicated_vm.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryReplicatedVm_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryReplicatedVmExists(resourceGroupName, vaultName, fabricName, protectionContainerName, replicationName), + ), + }, + { + ResourceName: replicationName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryReplicatedVm_basic(rInt int, location string, altLocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG1-%d" + location = "%s" +} + +resource "azurerm_resource_group" "test2" { + name = "acctestRG2-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test2.location}" + resource_group_name = "${azurerm_resource_group.test2.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_fabric" "test1" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric1-%d" + location = "${azurerm_resource_group.test.location}" +} + +resource "azurerm_recovery_services_fabric" "test2" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-fabric2-%d" + location = "${azurerm_resource_group.test2.location}" + depends_on = ["azurerm_recovery_services_fabric.test1"] +} + +resource "azurerm_recovery_services_protection_container" "test1" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + name = "acctest-protection-cont1-%d" +} + +resource "azurerm_recovery_services_protection_container" "test2" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test2.name}" + name = "acctest-protection-cont2-%d" +} + +resource "azurerm_recovery_services_replication_policy" "test" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-policy-%d" + recovery_point_retention_in_minutes = "${24 * 60}" + application_consistent_snapshot_frequency_in_minutes = "${4 * 60}" +} + +resource "azurerm_recovery_services_protection_container_mapping" "test" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + recovery_source_protection_container_name = "${azurerm_recovery_services_protection_container.test1.name}" + recovery_target_protection_container_id = "${azurerm_recovery_services_protection_container.test2.id}" + recovery_replication_policy_id = "${azurerm_recovery_services_replication_policy.test.id}" + name = "mapping-%d" +} + +resource "azurerm_virtual_network" "test1" { + name = "net-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = [ "192.168.1.0/24" ] + location = "${azurerm_recovery_services_fabric.test1.location}" +} +resource "azurerm_subnet" "test1" { + name = "snet-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test1.name}" + address_prefix = "192.168.1.0/24" +} + + +resource "azurerm_virtual_network" "test2" { + name = "net-%d" + resource_group_name = "${azurerm_resource_group.test2.name}" + address_space = [ "192.168.2.0/24" ] + location = "${azurerm_recovery_services_fabric.test2.location}" +} + +resource "azurerm_recovery_network_mapping" "test" { + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "mapping-%d" + source_recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + target_recovery_fabric_name = "${azurerm_recovery_services_fabric.test2.name}" + source_network_id = "${azurerm_virtual_network.test1.id}" + target_network_id = "${azurerm_virtual_network.test2.id}" +} + +resource "azurerm_network_interface" "test" { + name = "vm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "vm-%d" + subnet_id = "${azurerm_subnet.test1.id}" + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_virtual_machine" "test" { + name = "vm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + vm_size = "Standard_B1s" + + storage_image_reference { + publisher = "OpenLogic" + offer = "CentOS" + sku = "7.5" + version = "latest" + } + + storage_os_disk { + name = "disk-%d" + os_type = "Linux" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Premium_LRS" + } + + os_profile { + admin_username = "testadmin" + admin_password = "Password1234!" + computer_name = "vm-%d" + } + + os_profile_linux_config { + disable_password_authentication = false + + } + network_interface_ids = ["${azurerm_network_interface.test.id}"] +} + +resource "azurerm_storage_account" "test" { + name = "acct%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_recovery_replicated_vm" "test" { + name = "repl-%d" + resource_group_name = "${azurerm_resource_group.test2.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + source_vm_id = "${azurerm_virtual_machine.test.id}" + source_recovery_fabric_name = "${azurerm_recovery_services_fabric.test1.name}" + recovery_replication_policy_id = "${azurerm_recovery_services_replication_policy.test.id}" + source_recovery_protection_container_name = "${azurerm_recovery_services_protection_container.test1.name}" + + target_resource_group_id = "${azurerm_resource_group.test2.id}" + target_recovery_fabric_id = "${azurerm_recovery_services_fabric.test2.id}" + target_recovery_protection_container_id = "${azurerm_recovery_services_protection_container.test2.id}" + + managed_disk { + disk_id = "${azurerm_virtual_machine.test.storage_os_disk.0.managed_disk_id}" + staging_storage_account_id = "${azurerm_storage_account.test.id}" + target_resource_group_id = "${azurerm_resource_group.test2.id}" + target_disk_type = "Premium_LRS" + target_replica_disk_type = "Premium_LRS" + } + depends_on = ["azurerm_recovery_services_protection_container_mapping.test", "azurerm_recovery_network_mapping.test"] +} +`, rInt, location, rInt, altLocation, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt) +} + +func testCheckAzureRMRecoveryReplicatedVmExists(resourceGroupStateName, vaultStateName string, resourceStateName string, protectionContainerStateName string, replicationStateName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + fabricState, ok := s.RootModule().Resources[resourceStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + protectionContainerState, ok := s.RootModule().Resources[protectionContainerStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceStateName) + } + replicationState, ok := s.RootModule().Resources[replicationStateName] + if !ok { + return fmt.Errorf("Not found: %s", replicationStateName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + fabricName := fabricState.Primary.Attributes["name"] + protectionContainerName := protectionContainerState.Primary.Attributes["name"] + replicationName := replicationState.Primary.Attributes["name"] + + // Ensure mapping exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ReplicationMigrationItemsClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, fabricName, protectionContainerName, replicationName) + if err != nil { + return fmt.Errorf("Bad: Get on replicationVmClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: fabric: %q does not exist", fabricName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_replication_policy.go b/azurerm/resource_arm_recovery_services_replication_policy.go new file mode 100644 index 000000000000..441a9347eb86 --- /dev/null +++ b/azurerm/resource_arm_recovery_services_replication_policy.go @@ -0,0 +1,202 @@ +package azurerm + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2018-01-10/siterecovery" + "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/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmRecoveryServicesReplicationPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceArmRecoveryServicesReplicationPolicyCreate, + Read: resourceArmRecoveryServicesReplicationPolicyRead, + Update: resourceArmRecoveryServicesReplicationPolicyUpdate, + Delete: resourceArmSiteRecoveryReplicationPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + + "recovery_vault_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, + }, + "recovery_point_retention_in_minutes": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + ValidateFunc: validation.IntBetween(1, 365*24*60), + }, + "application_consistent_snapshot_frequency_in_minutes": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + ValidateFunc: validation.IntBetween(1, 365*24*60), + }, + }, + } +} + +func resourceArmRecoveryServicesReplicationPolicyCreate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.ReplicationPoliciesClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing recovery services replication policy %s: %+v", name, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_recovery_services_replication_policy", azure.HandleAzureSdkForGoBug2824(*existing.ID)) + } + } + + recoveryPoint := int32(d.Get("recovery_point_retention_in_minutes").(int)) + appConsitency := int32(d.Get("application_consistent_snapshot_frequency_in_minutes").(int)) + var parameters = siterecovery.CreatePolicyInput{ + Properties: &siterecovery.CreatePolicyInputProperties{ + ProviderSpecificInput: &siterecovery.A2APolicyCreationInput{ + RecoveryPointHistory: &recoveryPoint, + AppConsistentFrequencyInMinutes: &appConsitency, + MultiVMSyncStatus: siterecovery.Enable, + InstanceType: siterecovery.InstanceTypeBasicPolicyProviderSpecificInputInstanceTypeA2A, + }, + }, + } + future, err := client.Create(ctx, name, parameters) + if err != nil { + return fmt.Errorf("Error creating recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error creating recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, name) + if err != nil { + return fmt.Errorf("Error retrieving site recovery replication policy %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryServicesReplicationPolicyRead(d, meta) +} + +func resourceArmRecoveryServicesReplicationPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + resGroup := d.Get("resource_group_name").(string) + vaultName := d.Get("recovery_vault_name").(string) + name := d.Get("name").(string) + + client := meta.(*ArmClient).recoveryServices.ReplicationPoliciesClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + recoveryPoint := int32(d.Get("recovery_point_retention_in_minutes").(int)) + appConsitency := int32(d.Get("application_consistent_snapshot_frequency_in_minutes").(int)) + var parameters = siterecovery.UpdatePolicyInput{ + Properties: &siterecovery.UpdatePolicyInputProperties{ + ReplicationProviderSettings: &siterecovery.A2APolicyCreationInput{ + RecoveryPointHistory: &recoveryPoint, + AppConsistentFrequencyInMinutes: &appConsitency, + MultiVMSyncStatus: siterecovery.Enable, + InstanceType: siterecovery.InstanceTypeBasicPolicyProviderSpecificInputInstanceTypeA2A, + }, + }, + } + future, err := client.Update(ctx, name, parameters) + if err != nil { + return fmt.Errorf("Error updating recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error updating recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + + resp, err := client.Get(ctx, name) + if err != nil { + return fmt.Errorf("Error retrieving site recovery replication policy %s (vault %s): %+v", name, vaultName, err) + } + + d.SetId(azure.HandleAzureSdkForGoBug2824(*resp.ID)) + + return resourceArmRecoveryServicesReplicationPolicyRead(d, meta) +} + +func resourceArmRecoveryServicesReplicationPolicyRead(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + name := id.Path["replicationPolicies"] + + client := meta.(*ArmClient).recoveryServices.ReplicationPoliciesClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Get(ctx, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + d.Set("recovery_vault_name", vaultName) + if a2APolicyDetails, isA2A := resp.Properties.ProviderSpecificDetails.AsA2APolicyDetails(); isA2A { + d.Set("recovery_point_retention_in_minutes", a2APolicyDetails.RecoveryPointHistory) + d.Set("application_consistent_snapshot_frequency_in_minutes", a2APolicyDetails.AppConsistentFrequencyInMinutes) + } + return nil +} + +func resourceArmSiteRecoveryReplicationPolicyDelete(d *schema.ResourceData, meta interface{}) error { + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + vaultName := id.Path["vaults"] + name := id.Path["replicationPolicies"] + + client := meta.(*ArmClient).recoveryServices.ReplicationPoliciesClient(resGroup, vaultName) + ctx := meta.(*ArmClient).StopContext + + future, err := client.Delete(ctx, name) + if err != nil { + return fmt.Errorf("Error deleting recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of recovery services replication policy %s (vault %s): %+v", name, vaultName, err) + } + + return nil +} diff --git a/azurerm/resource_arm_recovery_services_replication_policy_test.go b/azurerm/resource_arm_recovery_services_replication_policy_test.go new file mode 100644 index 000000000000..b82a3ab1bdcd --- /dev/null +++ b/azurerm/resource_arm_recovery_services_replication_policy_test.go @@ -0,0 +1,98 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMRecoveryReplicationPolicy_basic(t *testing.T) { + resourceGroupName := "azurerm_resource_group.test" + vaultName := "azurerm_recovery_services_vault.test" + resourceName := "azurerm_recovery_services_replication_policy.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMResourceGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRecoveryReplicationPolicy_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRecoveryReplicationPolicyExists(resourceGroupName, vaultName, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMRecoveryReplicationPolicy_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_recovery_services_replication_policy" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + recovery_vault_name = "${azurerm_recovery_services_vault.test.name}" + name = "acctest-policy-%d" + recovery_point_retention_in_minutes = "${24 * 60}" + application_consistent_snapshot_frequency_in_minutes = "${4 * 60}" +} +`, rInt, location, rInt, rInt) +} + +func testCheckAzureRMRecoveryReplicationPolicyExists(resourceGroupStateName, vaultStateName string, policyStateName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + resourceGroupState, ok := s.RootModule().Resources[resourceGroupStateName] + if !ok { + return fmt.Errorf("Not found: %s", resourceGroupStateName) + } + vaultState, ok := s.RootModule().Resources[vaultStateName] + if !ok { + return fmt.Errorf("Not found: %s", vaultStateName) + } + policyState, ok := s.RootModule().Resources[policyStateName] + if !ok { + return fmt.Errorf("Not found: %s", policyStateName) + } + + resourceGroupName := resourceGroupState.Primary.Attributes["name"] + vaultName := vaultState.Primary.Attributes["name"] + policyName := policyState.Primary.Attributes["name"] + + // Ensure fabric exists in API + client := testAccProvider.Meta().(*ArmClient).recoveryServices.ReplicationPoliciesClient(resourceGroupName, vaultName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, policyName) + if err != nil { + return fmt.Errorf("Bad: Get on fabricClient: %+v", err) + } + + if resp.Response.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: fabric: %q does not exist", policyName) + } + + return nil + } +} diff --git a/azurerm/resource_arm_recovery_services_vault.go b/azurerm/resource_arm_recovery_services_vault.go index c0906c83dbf5..3abaf8b23ed6 100644 --- a/azurerm/resource_arm_recovery_services_vault.go +++ b/azurerm/resource_arm_recovery_services_vault.go @@ -3,12 +3,13 @@ package azurerm import ( "fmt" "log" - "regexp" + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2016-06-01/recoveryservices" "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/helpers/tf" - - "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2016-06-01/recoveryservices" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -28,25 +29,22 @@ func resourceArmRecoveryServicesVault() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile("^[a-zA-Z][-a-zA-Z0-9]{1,49}$"), - "Recovery Service Vault name must be 2 - 50 characters long, start with a letter, contain only letters, numbers and hyphens.", - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateRecoveryServicesVaultName, }, "location": azure.SchemaLocation(), "resource_group_name": azure.SchemaResourceGroupName(), - "tags": tagsSchema(), + "tags": tags.Schema(), "sku": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(recoveryservices.RS0), string(recoveryservices.Standard), @@ -57,17 +55,17 @@ func resourceArmRecoveryServicesVault() *schema.Resource { } func resourceArmRecoveryServicesVaultCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesVaultsClient + client := meta.(*ArmClient).recoveryServices.VaultsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) location := d.Get("location").(string) resourceGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) log.Printf("[DEBUG] Creating/updating Recovery Service Vault %q (resource group %q)", name, resourceGroup) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -83,7 +81,7 @@ func resourceArmRecoveryServicesVaultCreateUpdate(d *schema.ResourceData, meta i //build vault struct vault := recoveryservices.Vault{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), Sku: &recoveryservices.Sku{ Name: recoveryservices.SkuName(d.Get("sku").(string)), }, @@ -102,10 +100,10 @@ func resourceArmRecoveryServicesVaultCreateUpdate(d *schema.ResourceData, meta i } func resourceArmRecoveryServicesVaultRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesVaultsClient + client := meta.(*ArmClient).recoveryServices.VaultsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -135,16 +133,14 @@ func resourceArmRecoveryServicesVaultRead(d *schema.ResourceData, meta interface d.Set("sku", string(sku.Name)) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRecoveryServicesVaultDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).recoveryServicesVaultsClient + client := meta.(*ArmClient).recoveryServices.VaultsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_recovery_services_vault_test.go b/azurerm/resource_arm_recovery_services_vault_test.go index 6f2fad9e15f3..be756e58086c 100644 --- a/azurerm/resource_arm_recovery_services_vault_test.go +++ b/azurerm/resource_arm_recovery_services_vault_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,7 +41,7 @@ func TestAccAzureRMRecoveryServicesVault_basic(t *testing.T) { } func TestAccAzureRMRecoveryServicesVault_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -81,7 +82,7 @@ func testCheckAzureRMRecoveryServicesVaultDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).recoveryServicesVaultsClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -113,7 +114,7 @@ func testCheckAzureRMRecoveryServicesVaultExists(resourceName string) resource.T return fmt.Errorf("Bad: no resource group found in state for Recovery Services Vault: %q", name) } - client := testAccProvider.Meta().(*ArmClient).recoveryServicesVaultsClient + client := testAccProvider.Meta().(*ArmClient).recoveryServices.VaultsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_redis_cache.go b/azurerm/resource_arm_redis_cache.go index b10ca01a74f9..8a99c8de7eb8 100644 --- a/azurerm/resource_arm_redis_cache.go +++ b/azurerm/resource_arm_redis_cache.go @@ -11,6 +11,9 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" "github.com/hashicorp/terraform/helper/resource" @@ -244,7 +247,7 @@ func resourceArmRedisCache() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -264,10 +267,10 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error family := redis.SkuFamily(d.Get("family").(string)) sku := redis.SkuName(d.Get("sku_name").(string)) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -315,16 +318,16 @@ func resourceArmRedisCacheCreate(d *schema.ResourceData, meta interface{}) error } if v, ok := d.GetOk("subnet_id"); ok { - parsed, parseErr := parseAzureResourceID(v.(string)) + parsed, parseErr := azure.ParseAzureResourceID(v.(string)) if parseErr != nil { return fmt.Errorf("Error parsing Azure Resource ID %q", v.(string)) } subnetName := parsed.Path["subnets"] virtualNetworkName := parsed.Path["virtualNetworks"] - azureRMLockByName(subnetName, subnetResourceName) - defer azureRMUnlockByName(subnetName, subnetResourceName) - azureRMLockByName(virtualNetworkName, virtualNetworkResourceName) - defer azureRMUnlockByName(virtualNetworkName, virtualNetworkResourceName) + locks.ByName(subnetName, subnetResourceName) + defer locks.UnlockByName(subnetName, subnetResourceName) + locks.ByName(virtualNetworkName, virtualNetworkResourceName) + defer locks.UnlockByName(virtualNetworkName, virtualNetworkResourceName) parameters.SubnetID = utils.String(v.(string)) } @@ -388,8 +391,8 @@ func resourceArmRedisCacheUpdate(d *schema.ResourceData, meta interface{}) error family := redis.SkuFamily(d.Get("family").(string)) sku := redis.SkuName(d.Get("sku_name").(string)) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) parameters := redis.UpdateParameters{ UpdateProperties: &redis.UpdateProperties{ @@ -470,7 +473,7 @@ func resourceArmRedisCacheRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).redis.Client ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -543,16 +546,14 @@ func resourceArmRedisCacheRead(d *schema.ResourceData, meta interface{}) error { d.Set("primary_access_key", keysResp.PrimaryKey) d.Set("secondary_access_key", keysResp.SecondaryKey) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRedisCacheDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).redis.Client ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -568,16 +569,16 @@ func resourceArmRedisCacheDelete(d *schema.ResourceData, meta interface{}) error } props := *read.Properties if subnetID := props.SubnetID; subnetID != nil { - parsed, parseErr := parseAzureResourceID(*subnetID) + parsed, parseErr := azure.ParseAzureResourceID(*subnetID) if parseErr != nil { return fmt.Errorf("Error parsing Azure Resource ID %q", *subnetID) } subnetName := parsed.Path["subnets"] virtualNetworkName := parsed.Path["virtualNetworks"] - azureRMLockByName(subnetName, subnetResourceName) - defer azureRMUnlockByName(subnetName, subnetResourceName) - azureRMLockByName(virtualNetworkName, virtualNetworkResourceName) - defer azureRMUnlockByName(virtualNetworkName, virtualNetworkResourceName) + locks.ByName(subnetName, subnetResourceName) + defer locks.UnlockByName(subnetName, subnetResourceName) + locks.ByName(virtualNetworkName, virtualNetworkResourceName) + defer locks.UnlockByName(virtualNetworkName, virtualNetworkResourceName) } future, err := client.Delete(ctx, resGroup, name) if err != nil { @@ -599,7 +600,7 @@ func resourceArmRedisCacheDelete(d *schema.ResourceData, meta interface{}) error return nil } -func redisStateRefreshFunc(ctx context.Context, client redis.Client, resourceGroupName string, sgName string) resource.StateRefreshFunc { +func redisStateRefreshFunc(ctx context.Context, client *redis.Client, resourceGroupName string, sgName string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroupName, sgName) if err != nil { diff --git a/azurerm/resource_arm_redis_cache_test.go b/azurerm/resource_arm_redis_cache_test.go index df88da709b1e..bdf45ed23d56 100644 --- a/azurerm/resource_arm_redis_cache_test.go +++ b/azurerm/resource_arm_redis_cache_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMRedisCacheFamily_validation(t *testing.T) { @@ -130,7 +131,7 @@ func TestAccAzureRMRedisCache_basic(t *testing.T) { } func TestAccAzureRMRedisCache_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } diff --git a/azurerm/resource_arm_redis_firewall_rule.go b/azurerm/resource_arm_redis_firewall_rule.go index aac42ccfd2cd..82f816085afb 100644 --- a/azurerm/resource_arm_redis_firewall_rule.go +++ b/azurerm/resource_arm_redis_firewall_rule.go @@ -6,10 +6,12 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "regexp" "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -64,7 +66,7 @@ func resourceArmRedisFirewallRuleCreateUpdate(d *schema.ResourceData, meta inter startIP := d.Get("start_ip").(string) endIP := d.Get("end_ip").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, cacheName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -84,28 +86,30 @@ func resourceArmRedisFirewallRuleCreateUpdate(d *schema.ResourceData, meta inter }, } - if _, err := client.CreateOrUpdate(ctx, resourceGroup, cacheName, name, parameters); err != nil { - return err - } + return resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + if _, err := client.CreateOrUpdate(ctx, resourceGroup, cacheName, name, parameters); err != nil { + return resource.NonRetryableError(fmt.Errorf("Error creating the rule: %s", err)) + } - read, err := client.Get(ctx, resourceGroup, cacheName, name) - if err != nil { - return err - } - if read.ID == nil { - return fmt.Errorf("Cannot read Redis Firewall Rule %q (cache %q / resource group %q) ID", name, cacheName, resourceGroup) - } + read, err := client.Get(ctx, resourceGroup, cacheName, name) + if err != nil { + return resource.RetryableError(fmt.Errorf("Expected instance to be created but was in non existent state, retrying")) + } + if read.ID == nil { + return resource.NonRetryableError(fmt.Errorf("Cannot read Redis Firewall Rule %q (cache %q / resource group %q) ID", name, cacheName, resourceGroup)) + } - d.SetId(*read.ID) + d.SetId(*read.ID) - return resourceArmRedisFirewallRuleRead(d, meta) + return resource.NonRetryableError(resourceArmRedisFirewallRuleRead(d, meta)) + }) } func resourceArmRedisFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).redis.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -140,7 +144,7 @@ func resourceArmRedisFirewallRuleDelete(d *schema.ResourceData, meta interface{} client := meta.(*ArmClient).redis.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_redis_firewall_rule_test.go b/azurerm/resource_arm_redis_firewall_rule_test.go index 8b73010e5468..a63c2161afff 100644 --- a/azurerm/resource_arm_redis_firewall_rule_test.go +++ b/azurerm/resource_arm_redis_firewall_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -75,8 +76,39 @@ func TestAccAzureRMRedisFirewallRule_basic(t *testing.T) { }) } +func TestAccAzureRMRedisFirewallRule_multi(t *testing.T) { + ruleOne := "azurerm_redis_firewall_rule.test" + ruleTwo := "azurerm_redis_firewall_rule.double" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRedisFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRedisFirewallRule_multi(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRedisFirewallRuleExists(ruleOne), + testCheckAzureRMRedisFirewallRuleExists(ruleTwo), + ), + }, + { + ResourceName: ruleOne, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: ruleTwo, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMRedisFirewallRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -157,7 +189,7 @@ func testCheckAzureRMRedisFirewallRuleExists(resourceName string) resource.TestC } func testCheckAzureRMRedisFirewallRuleDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).resourceGroupsClient + client := testAccProvider.Meta().(*ArmClient).resource.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -213,6 +245,20 @@ resource "azurerm_redis_firewall_rule" "test" { `, rInt, location, rInt, rInt) } +func testAccAzureRMRedisFirewallRule_multi(rInt int, location string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_redis_firewall_rule" "double" { + name = "fwruletwo%d" + redis_cache_name = "${azurerm_redis_cache.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + start_ip = "4.5.6.7" + end_ip = "8.9.0.1" +} +`, testAccAzureRMRedisFirewallRule_basic(rInt, location), rInt) +} + func testAccAzureRMRedisFirewallRule_requiresImport(rInt int, location string) string { return fmt.Sprintf(` %s diff --git a/azurerm/resource_arm_relay_namespace.go b/azurerm/resource_arm_relay_namespace.go index 74b87a08ed95..cc59b2c46e35 100644 --- a/azurerm/resource_arm_relay_namespace.go +++ b/azurerm/resource_arm_relay_namespace.go @@ -6,7 +6,10 @@ import ( "log" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "time" @@ -41,15 +44,18 @@ func resourceArmRelayNamespace() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), "sku": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "This property has been deprecated in favour of the 'sku_name' property and will be removed in version 2.0 of the provider", + ConflictsWith: []string{"sku_name"}, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(relay.Standard), }, true), @@ -58,6 +64,16 @@ func resourceArmRelayNamespace() *schema.Resource { }, }, + "sku_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"sku"}, + ValidateFunc: validation.StringInSlice([]string{ + string(relay.Standard), + }, false), + }, + "metric_id": { Type: schema.TypeString, Computed: true, @@ -87,7 +103,7 @@ func resourceArmRelayNamespace() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } @@ -95,17 +111,40 @@ func resourceArmRelayNamespace() *schema.Resource { func resourceArmRelayNamespaceCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).relay.NamespacesClient ctx := meta.(*ArmClient).StopContext + + // Remove in 2.0 + var sku relay.Sku + + if inputs := d.Get("sku").([]interface{}); len(inputs) != 0 { + input := inputs[0].(map[string]interface{}) + v := input["name"].(string) + + sku = relay.Sku{ + Name: utils.String(v), + Tier: relay.SkuTier(v), + } + } else { + // Keep in 2.0 + sku = relay.Sku{ + Name: utils.String(d.Get("sku_name").(string)), + Tier: relay.SkuTier(d.Get("sku_name").(string)), + } + } + + if *sku.Name == "" { + return fmt.Errorf("either 'sku_name' or 'sku' must be defined in the configuration file") + } + log.Printf("[INFO] preparing arguments for Relay Namespace creation.") name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) resourceGroup := d.Get("resource_group_name").(string) - sku := expandRelayNamespaceSku(d) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -120,7 +159,7 @@ func resourceArmRelayNamespaceCreateUpdate(d *schema.ResourceData, meta interfac parameters := relay.Namespace{ Location: utils.String(location), - Sku: sku, + Sku: &sku, NamespaceProperties: &relay.NamespaceProperties{}, Tags: expandedTags, } @@ -151,7 +190,7 @@ func resourceArmRelayNamespaceRead(d *schema.ResourceData, meta interface{}) err client := meta.(*ArmClient).relay.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -175,10 +214,16 @@ func resourceArmRelayNamespaceRead(d *schema.ResourceData, meta interface{}) err } if sku := resp.Sku; sku != nil { - flattenedSku := flattenRelayNamespaceSku(sku) - if err = d.Set("sku", flattenedSku); err != nil { - return fmt.Errorf("Error setting `sku`: %+v", err) + // Remove in 2.0 + if err := d.Set("sku", flattenRelayNamespaceSku(sku)); err != nil { + return fmt.Errorf("Error setting 'sku': %+v", err) + } + + if err := d.Set("sku_name", sku.Name); err != nil { + return fmt.Errorf("Error setting 'sku_name': %+v", err) } + } else { + return fmt.Errorf("Error making Read request on Relay Namespace %q (Resource Group %q): Unable to retrieve 'sku' value", name, resourceGroup) } if props := resp.NamespaceProperties; props != nil { @@ -195,16 +240,14 @@ func resourceArmRelayNamespaceRead(d *schema.ResourceData, meta interface{}) err d.Set("secondary_connection_string", keysResp.SecondaryConnectionString) d.Set("secondary_key", keysResp.SecondaryKey) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRelayNamespaceDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).relay.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -236,7 +279,7 @@ func resourceArmRelayNamespaceDelete(d *schema.ResourceData, meta interface{}) e return nil } -func relayNamespaceDeleteRefreshFunc(ctx context.Context, client relay.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { +func relayNamespaceDeleteRefreshFunc(ctx context.Context, client *relay.NamespacesClient, resourceGroupName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { res, err := client.Get(ctx, resourceGroupName, name) if err != nil { @@ -251,18 +294,7 @@ func relayNamespaceDeleteRefreshFunc(ctx context.Context, client relay.Namespace } } -func expandRelayNamespaceSku(d *schema.ResourceData) *relay.Sku { - vs := d.Get("sku").([]interface{}) - v := vs[0].(map[string]interface{}) - - name := v["name"].(string) - - return &relay.Sku{ - Name: utils.String(name), - Tier: relay.SkuTier(name), - } -} - +// Remove in 2.0 func flattenRelayNamespaceSku(input *relay.Sku) []interface{} { if input == nil { return []interface{}{} diff --git a/azurerm/resource_arm_relay_namespace_test.go b/azurerm/resource_arm_relay_namespace_test.go index 7ce2880e7173..d3d280e118e9 100644 --- a/azurerm/resource_arm_relay_namespace_test.go +++ b/azurerm/resource_arm_relay_namespace_test.go @@ -3,11 +3,13 @@ package azurerm import ( "fmt" "net/http" + "regexp" "testing" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMRelayNamespace_basic(t *testing.T) { @@ -28,6 +30,7 @@ func TestAccAzureRMRelayNamespace_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "secondary_connection_string"), resource.TestCheckResourceAttrSet(resourceName, "primary_key"), resource.TestCheckResourceAttrSet(resourceName, "secondary_key"), + resource.TestCheckResourceAttr(resourceName, "sku_name", "Standard"), ), }, { @@ -38,8 +41,57 @@ func TestAccAzureRMRelayNamespace_basic(t *testing.T) { }, }) } + +// Remove in 2.0 +func TestAccAzureRMRelayNamespace_basicClassic(t *testing.T) { + resourceName := "azurerm_relay_namespace.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRelayNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRelayNamespace_basicClassic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRelayNamespaceExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "metric_id"), + resource.TestCheckResourceAttrSet(resourceName, "primary_connection_string"), + resource.TestCheckResourceAttrSet(resourceName, "secondary_connection_string"), + resource.TestCheckResourceAttrSet(resourceName, "primary_key"), + resource.TestCheckResourceAttrSet(resourceName, "secondary_key"), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "Standard"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Remove in 2.0 +func TestAccAzureRMRelayNamespace_basicNotDefined(t *testing.T) { + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRelayNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRelayNamespace_basicNotDefined(ri, testLocation()), + ExpectError: regexp.MustCompile("either 'sku_name' or 'sku' must be defined in the configuration file"), + }, + }, + }) +} + func TestAccAzureRMRelayNamespace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -160,6 +212,24 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_relay_namespace" "test" { + name = "acctestrn-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku_name = "Standard" +} +`, rInt, location, rInt) +} + +// Remove in 2.0 +func testAccAzureRMRelayNamespace_basicClassic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_relay_namespace" "test" { name = "acctestrn-%d" location = "${azurerm_resource_group.test.location}" @@ -172,6 +242,21 @@ resource "azurerm_relay_namespace" "test" { `, rInt, location, rInt) } +func testAccAzureRMRelayNamespace_basicNotDefined(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_relay_namespace" "test" { + name = "acctestrn-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + func testAccAzureRMRelayNamespace_requiresImport(rInt int, location string) string { return fmt.Sprintf(` %s @@ -181,9 +266,7 @@ resource "azurerm_relay_namespace" "import" { location = "${azurerm_relay_namespace.test.location}" resource_group_name = "${azurerm_relay_namespace.test.resource_group_name}" - sku { - name = "Standard" - } + sku_name = "Standard" } `, testAccAzureRMRelayNamespace_basic(rInt, location)) } @@ -200,9 +283,7 @@ resource "azurerm_relay_namespace" "test" { location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - sku { - name = "Standard" - } + sku_name = "Standard" tags = { Hello = "World" diff --git a/azurerm/resource_arm_resource_group.go b/azurerm/resource_arm_resource_group.go index 234b67e0c7a5..b72ed31b2aa8 100644 --- a/azurerm/resource_arm_resource_group.go +++ b/azurerm/resource_arm_resource_group.go @@ -6,6 +6,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" "github.com/hashicorp/terraform/helper/schema" @@ -18,7 +20,6 @@ func resourceArmResourceGroup() *schema.Resource { Create: resourceArmResourceGroupCreateUpdate, Read: resourceArmResourceGroupRead, Update: resourceArmResourceGroupCreateUpdate, - Exists: resourceArmResourceGroupExists, Delete: resourceArmResourceGroupDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -29,20 +30,20 @@ func resourceArmResourceGroup() *schema.Resource { "location": azure.SchemaLocation(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).resourceGroupsClient + client := meta.(*ArmClient).resource.GroupsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -57,7 +58,7 @@ func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface parameters := resources.Group{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.CreateOrUpdate(ctx, name, parameters); err != nil { @@ -75,10 +76,10 @@ func resourceArmResourceGroupCreateUpdate(d *schema.ResourceData, meta interface } func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).resourceGroupsClient + client := meta.(*ArmClient).resource.GroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return fmt.Errorf("Error parsing Azure Resource ID %q: %+v", d.Id(), err) } @@ -100,39 +101,14 @@ func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) erro if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, resp.Tags) - - return nil -} - -func resourceArmResourceGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) { - client := meta.(*ArmClient).resourceGroupsClient - ctx := meta.(*ArmClient).StopContext - - id, err := parseAzureResourceID(d.Id()) - if err != nil { - return false, fmt.Errorf("Error parsing Azure Resource ID %q: %+v", d.Id(), err) - } - - name := id.ResourceGroup - - resp, err := client.Get(ctx, name) - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return false, nil - } - - return false, fmt.Errorf("Error reading resource group: %+v", err) - } - - return true, nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).resourceGroupsClient + client := meta.(*ArmClient).resource.GroupsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return fmt.Errorf("Error parsing Azure Resource ID %q: %+v", d.Id(), err) } diff --git a/azurerm/resource_arm_resource_group_test.go b/azurerm/resource_arm_resource_group_test.go index bf636a7a2f55..c19ecc67bf2b 100644 --- a/azurerm/resource_arm_resource_group_test.go +++ b/azurerm/resource_arm_resource_group_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMResourceGroup_basic(t *testing.T) { @@ -35,7 +36,7 @@ func TestAccAzureRMResourceGroup_basic(t *testing.T) { } func TestAccAzureRMResourceGroup_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -137,7 +138,7 @@ func testCheckAzureRMResourceGroupExists(resourceName string) resource.TestCheck resourceGroup := rs.Primary.Attributes["name"] // Ensure resource group exists in API - client := testAccProvider.Meta().(*ArmClient).resourceGroupsClient + client := testAccProvider.Meta().(*ArmClient).resource.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup) @@ -164,7 +165,7 @@ func testCheckAzureRMResourceGroupDisappears(resourceName string) resource.TestC resourceGroup := rs.Primary.Attributes["name"] // Ensure resource group exists in API - client := testAccProvider.Meta().(*ArmClient).resourceGroupsClient + client := testAccProvider.Meta().(*ArmClient).resource.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext deleteFuture, err := client.Delete(ctx, resourceGroup) @@ -182,7 +183,7 @@ func testCheckAzureRMResourceGroupDisappears(resourceName string) resource.TestC } func testCheckAzureRMResourceGroupDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).resourceGroupsClient + client := testAccProvider.Meta().(*ArmClient).resource.GroupsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_role_assignment.go b/azurerm/resource_arm_role_assignment.go index 0c332eac1942..771877313c7d 100644 --- a/azurerm/resource_arm_role_assignment.go +++ b/azurerm/resource_arm_role_assignment.go @@ -7,10 +7,11 @@ import ( "time" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -64,13 +65,24 @@ func resourceArmRoleAssignment() *schema.Resource { Required: true, ForceNew: true, }, + + "principal_type": { + Type: schema.TypeString, + Computed: true, + }, + + "skip_service_principal_aad_check": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, }, } } func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) error { - roleAssignmentsClient := meta.(*ArmClient).roleAssignmentsClient - roleDefinitionsClient := meta.(*ArmClient).roleDefinitionsClient + roleAssignmentsClient := meta.(*ArmClient).authorization.RoleAssignmentsClient + roleDefinitionsClient := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -81,7 +93,7 @@ func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) e roleDefinitionId = v.(string) } else if v, ok := d.GetOk("role_definition_name"); ok { roleName := v.(string) - roleDefinitions, err := roleDefinitionsClient.List(ctx, "", fmt.Sprintf("roleName eq '%s'", roleName)) + roleDefinitions, err := roleDefinitionsClient.List(ctx, scope, fmt.Sprintf("roleName eq '%s'", roleName)) if err != nil { return fmt.Errorf("Error loading Role Definition List: %+v", err) } @@ -105,11 +117,11 @@ func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) e name = uuid } - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := roleAssignmentsClient.Get(ctx, scope, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Error checking for presence of existing Role Assignment ID for %q (Scope %q)", name, scope) + return fmt.Errorf("Error checking for presence of existing Role Assignment ID for %q (Scope %q): %+v", name, scope, err) } } @@ -125,6 +137,12 @@ func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) e }, } + skipPrincipalCheck := d.Get("skip_service_principal_aad_check").(bool) + + if skipPrincipalCheck { + properties.RoleAssignmentProperties.PrincipalType = authorization.ServicePrincipal + } + if err := resource.Retry(300*time.Second, retryRoleAssignmentsClient(scope, name, properties, meta)); err != nil { return err } @@ -142,8 +160,8 @@ func resourceArmRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) e } func resourceArmRoleAssignmentRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleAssignmentsClient - roleDefinitionsClient := meta.(*ArmClient).roleDefinitionsClient + client := meta.(*ArmClient).authorization.RoleAssignmentsClient + roleDefinitionsClient := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext resp, err := client.GetByID(ctx, d.Id()) @@ -163,6 +181,7 @@ func resourceArmRoleAssignmentRead(d *schema.ResourceData, meta interface{}) err d.Set("scope", props.Scope) d.Set("role_definition_id", props.RoleDefinitionID) d.Set("principal_id", props.PrincipalID) + d.Set("principal_type", props.PrincipalType) //allows for import when role name is used (also if the role name changes a plan will show a diff) if roleId := props.RoleDefinitionID; roleId != nil { @@ -181,7 +200,7 @@ func resourceArmRoleAssignmentRead(d *schema.ResourceData, meta interface{}) err } func resourceArmRoleAssignmentDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleAssignmentsClient + client := meta.(*ArmClient).authorization.RoleAssignmentsClient ctx := meta.(*ArmClient).StopContext id, err := parseRoleAssignmentId(d.Id()) @@ -213,7 +232,7 @@ func validateRoleDefinitionName(i interface{}, k string) ([]string, []error) { func retryRoleAssignmentsClient(scope string, name string, properties authorization.RoleAssignmentCreateParameters, meta interface{}) func() *resource.RetryError { return func() *resource.RetryError { - roleAssignmentsClient := meta.(*ArmClient).roleAssignmentsClient + roleAssignmentsClient := meta.(*ArmClient).authorization.RoleAssignmentsClient ctx := meta.(*ArmClient).StopContext resp, err := roleAssignmentsClient.Create(ctx, scope, name, properties) diff --git a/azurerm/resource_arm_role_assignment_test.go b/azurerm/resource_arm_role_assignment_test.go index 063b9a37fc53..a5ac2d068ed8 100644 --- a/azurerm/resource_arm_role_assignment_test.go +++ b/azurerm/resource_arm_role_assignment_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -24,8 +25,12 @@ func TestAccAzureRMRoleAssignment(t *testing.T) { "requiresImport": testAccAzureRMRoleAssignment_requiresImport, }, "assignment": { - "sp": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal, - "group": testAccAzureRMActiveDirectoryServicePrincipal_group, + "sp": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal, + "spType": testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipalWithType, + "group": testAccAzureRMActiveDirectoryServicePrincipal_group, + }, + "management": { + "assign": testAccAzureRMRoleAssignment_managementGroup, }, } @@ -61,6 +66,9 @@ func testAccAzureRMRoleAssignment_emptyName(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -87,13 +95,16 @@ func testAccAzureRMRoleAssignment_roleName(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) } func testAccAzureRMRoleAssignment_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -142,6 +153,9 @@ func testAccAzureRMRoleAssignment_dataActions(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -166,6 +180,9 @@ func testAccAzureRMRoleAssignment_builtin(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) @@ -192,12 +209,16 @@ func testAccAzureRMRoleAssignment_custom(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "skip_service_principal_aad_check", + }, }, }, }) } func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal(t *testing.T) { + resourceName := "azurerm_role_assignment.test" ri := tf.AccRandTimeInt() id := uuid.New().String() @@ -207,7 +228,27 @@ func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipal(t *testing.T CheckDestroy: testCheckAzureRMRoleAssignmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMRoleAssingment_servicePrincipal(ri, id), + Config: testAccAzureRMRoleAssignment_servicePrincipal(ri, id), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), + resource.TestCheckResourceAttr(resourceName, "principal_type", "ServicePrincipal"), + ), + }, + }, + }) +} + +func testAccAzureRMActiveDirectoryServicePrincipal_servicePrincipalWithType(t *testing.T) { + ri := tf.AccRandTimeInt() + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRoleAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRoleAssignment_servicePrincipalWithType(ri, id), Check: resource.ComposeTestCheckFunc( testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), ), @@ -226,7 +267,7 @@ func testAccAzureRMActiveDirectoryServicePrincipal_group(t *testing.T) { CheckDestroy: testCheckAzureRMRoleAssignmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMRoleAssingment_group(ri, id), + Config: testAccAzureRMRoleAssignment_group(ri, id), Check: resource.ComposeTestCheckFunc( testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), ), @@ -245,7 +286,7 @@ func testCheckAzureRMRoleAssignmentExists(resourceName string) resource.TestChec scope := rs.Primary.Attributes["scope"] roleAssignmentName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).roleAssignmentsClient + client := testAccProvider.Meta().(*ArmClient).authorization.RoleAssignmentsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, scope, roleAssignmentName) @@ -269,7 +310,7 @@ func testCheckAzureRMRoleAssignmentDestroy(s *terraform.State) error { scope := rs.Primary.Attributes["scope"] roleAssignmentName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).roleAssignmentsClient + client := testAccProvider.Meta().(*ArmClient).authorization.RoleAssignmentsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, scope, roleAssignmentName) @@ -287,6 +328,24 @@ func testCheckAzureRMRoleAssignmentDestroy(s *terraform.State) error { return nil } +func testAccAzureRMRoleAssignment_managementGroup(t *testing.T) { + groupId := uuid.New().String() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRoleAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRoleAssignment_managementGroupConfig(groupId), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRoleAssignmentExists("azurerm_role_assignment.test"), + ), + }, + }, + }) +} + func testAccAzureRMRoleAssignment_emptyNameConfig() string { return ` data "azurerm_subscription" "primary" {} @@ -398,7 +457,7 @@ resource "azurerm_role_assignment" "test" { `, roleDefinitionId, rInt, roleAssignmentId) } -func testAccAzureRMRoleAssingment_servicePrincipal(rInt int, roleAssignmentID string) string { +func testAccAzureRMRoleAssignment_servicePrincipal(rInt int, roleAssignmentID string) string { return fmt.Sprintf(` data "azurerm_subscription" "current" {} @@ -419,7 +478,29 @@ resource "azurerm_role_assignment" "test" { `, rInt, roleAssignmentID) } -func testAccAzureRMRoleAssingment_group(rInt int, roleAssignmentID string) string { +func testAccAzureRMRoleAssignment_servicePrincipalWithType(rInt int, roleAssignmentID string) string { + return fmt.Sprintf(` +data "azurerm_subscription" "current" {} + +resource "azuread_application" "test" { + name = "acctestspa-%d" +} + +resource "azuread_service_principal" "test" { + application_id = "${azuread_application.test.application_id}" +} + +resource "azurerm_role_assignment" "test" { + name = "%s" + scope = "${data.azurerm_subscription.current.id}" + role_definition_name = "Reader" + principal_id = "${azuread_service_principal.test.id}" + skip_service_principal_aad_check = true +} +`, rInt, roleAssignmentID) +} + +func testAccAzureRMRoleAssignment_group(rInt int, roleAssignmentID string) string { return fmt.Sprintf(` data "azurerm_subscription" "current" {} @@ -435,3 +516,25 @@ resource "azurerm_role_assignment" "test" { } `, rInt, roleAssignmentID) } + +func testAccAzureRMRoleAssignment_managementGroupConfig(groupId string) string { + return fmt.Sprintf(` +data "azurerm_subscription" "primary" {} + +data "azurerm_client_config" "test" {} + +data "azurerm_role_definition" "test" { + name = "Monitoring Reader" +} + +resource "azurerm_management_group" "test" { + group_id = "%s" +} + +resource "azurerm_role_assignment" "test" { + scope = "${azurerm_management_group.test.id}" + role_definition_id = "${data.azurerm_role_definition.test.id}" + principal_id = "${data.azurerm_client_config.test.service_principal_object_id}" +} +`, groupId) +} diff --git a/azurerm/resource_arm_role_definition.go b/azurerm/resource_arm_role_definition.go index ae28cd179cd3..6a6de5c886e6 100644 --- a/azurerm/resource_arm_role_definition.go +++ b/azurerm/resource_arm_role_definition.go @@ -6,8 +6,9 @@ import ( "strings" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -98,7 +99,7 @@ func resourceArmRoleDefinition() *schema.Resource { } func resourceArmRoleDefinitionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleDefinitionsClient + client := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext roleDefinitionId := d.Get("role_definition_id").(string) @@ -118,7 +119,7 @@ func resourceArmRoleDefinitionCreateUpdate(d *schema.ResourceData, meta interfac permissions := expandRoleDefinitionPermissions(d) assignableScopes := expandRoleDefinitionAssignableScopes(d) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, scope, roleDefinitionId) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -158,7 +159,7 @@ func resourceArmRoleDefinitionCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmRoleDefinitionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleDefinitionsClient + client := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext resp, err := client.GetByID(ctx, d.Id()) @@ -172,6 +173,16 @@ func resourceArmRoleDefinitionRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error loading Role Definition %q: %+v", d.Id(), err) } + if id := resp.ID; id != nil { + roleDefinitionId, err := parseRoleDefinitionId(*id) + if err != nil { + return fmt.Errorf("Error parsing Role Definition ID: %+v", err) + } + if roleDefinitionId != nil { + d.Set("role_definition_id", roleDefinitionId.roleDefinitionId) + } + } + if props := resp.RoleDefinitionProperties; props != nil { d.Set("name", props.RoleName) d.Set("description", props.Description) @@ -191,7 +202,7 @@ func resourceArmRoleDefinitionRead(d *schema.ResourceData, meta interface{}) err } func resourceArmRoleDefinitionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).roleDefinitionsClient + client := meta.(*ArmClient).authorization.RoleDefinitionsClient ctx := meta.(*ArmClient).StopContext id, err := parseRoleDefinitionId(d.Id()) diff --git a/azurerm/resource_arm_role_definition_test.go b/azurerm/resource_arm_role_definition_test.go index 3a01f0f86204..d17b0de71ee0 100644 --- a/azurerm/resource_arm_role_definition_test.go +++ b/azurerm/resource_arm_role_definition_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMRoleDefinition_basic(t *testing.T) { } func TestAccAzureRMRoleDefinition_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -125,6 +126,40 @@ func TestAccAzureRMRoleDefinition_update(t *testing.T) { }) } +func TestAccAzureRMRoleDefinition_updateEmptyId(t *testing.T) { + resourceName := "azurerm_role_definition.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMRoleDefinitionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMRoleDefinition_emptyId(ri), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRoleDefinitionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.actions.0", "*"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.not_actions.#", "0"), + ), + }, + { + Config: testAccAzureRMRoleDefinition_updateEmptyId(ri), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMRoleDefinitionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.actions.0", "*"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.not_actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0.not_actions.0", "Microsoft.Authorization/*/read"), + ), + }, + }, + }) +} + func TestAccAzureRMRoleDefinition_emptyName(t *testing.T) { resourceName := "azurerm_role_definition.test" ri := tf.AccRandTimeInt() @@ -156,7 +191,7 @@ func testCheckAzureRMRoleDefinitionExists(resourceName string) resource.TestChec scope := rs.Primary.Attributes["scope"] roleDefinitionId := rs.Primary.Attributes["role_definition_id"] - client := testAccProvider.Meta().(*ArmClient).roleDefinitionsClient + client := testAccProvider.Meta().(*ArmClient).authorization.RoleDefinitionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, scope, roleDefinitionId) @@ -180,7 +215,7 @@ func testCheckAzureRMRoleDefinitionDestroy(s *terraform.State) error { scope := rs.Primary.Attributes["scope"] roleDefinitionId := rs.Primary.Attributes["role_definition_id"] - client := testAccProvider.Meta().(*ArmClient).roleDefinitionsClient + client := testAccProvider.Meta().(*ArmClient).authorization.RoleDefinitionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, scope, roleDefinitionId) @@ -303,3 +338,23 @@ resource "azurerm_role_definition" "test" { } `, rInt) } + +func testAccAzureRMRoleDefinition_updateEmptyId(rInt int) string { + return fmt.Sprintf(` +data "azurerm_subscription" "primary" {} + +resource "azurerm_role_definition" "test" { + name = "acctestrd-%d" + scope = "${data.azurerm_subscription.primary.id}" + + permissions { + actions = ["*"] + not_actions = ["Microsoft.Authorization/*/read"] + } + + assignable_scopes = [ + "${data.azurerm_subscription.primary.id}", + ] +} +`, rInt) +} diff --git a/azurerm/resource_arm_route.go b/azurerm/resource_arm_route.go index 947649d7b04b..6829dfec8999 100644 --- a/azurerm/resource_arm_route.go +++ b/azurerm/resource_arm_route.go @@ -5,8 +5,10 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" @@ -71,7 +73,7 @@ func resourceArmRoute() *schema.Resource { } func resourceArmRouteCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routesClient + client := meta.(*ArmClient).network.RoutesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -81,7 +83,7 @@ func resourceArmRouteCreateUpdate(d *schema.ResourceData, meta interface{}) erro addressPrefix := d.Get("address_prefix").(string) nextHopType := d.Get("next_hop_type").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, rtName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -94,8 +96,8 @@ func resourceArmRouteCreateUpdate(d *schema.ResourceData, meta interface{}) erro } } - azureRMLockByName(rtName, routeTableResourceName) - defer azureRMUnlockByName(rtName, routeTableResourceName) + locks.ByName(rtName, routeTableResourceName) + defer locks.UnlockByName(rtName, routeTableResourceName) route := network.Route{ Name: &name, @@ -131,10 +133,10 @@ func resourceArmRouteCreateUpdate(d *schema.ResourceData, meta interface{}) erro } func resourceArmRouteRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routesClient + client := meta.(*ArmClient).network.RoutesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -165,10 +167,10 @@ func resourceArmRouteRead(d *schema.ResourceData, meta interface{}) error { } func resourceArmRouteDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routesClient + client := meta.(*ArmClient).network.RoutesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -176,8 +178,8 @@ func resourceArmRouteDelete(d *schema.ResourceData, meta interface{}) error { rtName := id.Path["routeTables"] routeName := id.Path["routes"] - azureRMLockByName(rtName, routeTableResourceName) - defer azureRMUnlockByName(rtName, routeTableResourceName) + locks.ByName(rtName, routeTableResourceName) + defer locks.UnlockByName(rtName, routeTableResourceName) future, err := client.Delete(ctx, resGroup, rtName, routeName) if err != nil { diff --git a/azurerm/resource_arm_route_table.go b/azurerm/resource_arm_route_table.go index 420e4546ea91..4ff2656353ef 100644 --- a/azurerm/resource_arm_route_table.go +++ b/azurerm/resource_arm_route_table.go @@ -6,8 +6,10 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -95,13 +97,13 @@ func resourceArmRouteTable() *schema.Resource { Set: schema.HashString, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmRouteTableCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routeTablesClient + client := meta.(*ArmClient).network.RouteTablesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM Route Table creation.") @@ -109,9 +111,9 @@ func resourceArmRouteTableCreateUpdate(d *schema.ResourceData, meta interface{}) name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) resGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -131,7 +133,7 @@ func resourceArmRouteTableCreateUpdate(d *schema.ResourceData, meta interface{}) Routes: expandRouteTableRoutes(d), DisableBgpRoutePropagation: utils.Bool(d.Get("disable_bgp_route_propagation").(bool)), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, name, routeSet) @@ -157,10 +159,10 @@ func resourceArmRouteTableCreateUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmRouteTableRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routeTablesClient + client := meta.(*ArmClient).network.RouteTablesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -193,16 +195,14 @@ func resourceArmRouteTableRead(d *schema.ResourceData, meta interface{}) error { } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmRouteTableDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).routeTablesClient + client := meta.(*ArmClient).network.RouteTablesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_route_table_test.go b/azurerm/resource_arm_route_table_test.go index b8f7c9c31d67..ab900c8fb440 100644 --- a/azurerm/resource_arm_route_table_test.go +++ b/azurerm/resource_arm_route_table_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -38,7 +39,7 @@ func TestAccAzureRMRouteTable_basic(t *testing.T) { } func TestAccAzureRMRouteTable_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -329,7 +330,6 @@ func TestAccAzureRMRouteTable_withTagsSubnet(t *testing.T) { func testCheckAzureRMRouteTableExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %q", resourceName) @@ -341,7 +341,7 @@ func testCheckAzureRMRouteTableExists(resourceName string) resource.TestCheckFun return fmt.Errorf("Bad: no resource group found in state for route table: %q", name) } - client := testAccProvider.Meta().(*ArmClient).routeTablesClient + client := testAccProvider.Meta().(*ArmClient).network.RouteTablesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name, "") @@ -370,7 +370,7 @@ func testCheckAzureRMRouteTableDisappears(resourceName string) resource.TestChec return fmt.Errorf("Bad: no resource group found in state for route table: %q", name) } - client := testAccProvider.Meta().(*ArmClient).routeTablesClient + client := testAccProvider.Meta().(*ArmClient).network.RouteTablesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, name) @@ -389,7 +389,7 @@ func testCheckAzureRMRouteTableDisappears(resourceName string) resource.TestChec } func testCheckAzureRMRouteTableDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).routeTablesClient + client := testAccProvider.Meta().(*ArmClient).network.RouteTablesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_route_test.go b/azurerm/resource_arm_route_test.go index d92551122711..63f84d9ee450 100644 --- a/azurerm/resource_arm_route_test.go +++ b/azurerm/resource_arm_route_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMRoute_basic(t *testing.T) { } func TestAccAzureRMRoute_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -150,7 +151,6 @@ func TestAccAzureRMRoute_multipleRoutes(t *testing.T) { func testCheckAzureRMRouteExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %q", resourceName) @@ -163,7 +163,7 @@ func testCheckAzureRMRouteExists(resourceName string) resource.TestCheckFunc { return fmt.Errorf("Bad: no resource group found in state for route: %q", name) } - client := testAccProvider.Meta().(*ArmClient).routesClient + client := testAccProvider.Meta().(*ArmClient).network.RoutesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, rtName, name) @@ -180,7 +180,6 @@ func testCheckAzureRMRouteExists(resourceName string) resource.TestCheckFunc { func testCheckAzureRMRouteDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -193,7 +192,7 @@ func testCheckAzureRMRouteDisappears(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for route: %s", name) } - client := testAccProvider.Meta().(*ArmClient).routesClient + client := testAccProvider.Meta().(*ArmClient).network.RoutesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, rtName, name) @@ -210,7 +209,7 @@ func testCheckAzureRMRouteDisappears(resourceName string) resource.TestCheckFunc } func testCheckAzureRMRouteDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).routesClient + client := testAccProvider.Meta().(*ArmClient).network.RoutesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_scheduler_job.go b/azurerm/resource_arm_scheduler_job.go index c29465b9caf8..2202b8363e22 100644 --- a/azurerm/resource_arm_scheduler_job.go +++ b/azurerm/resource_arm_scheduler_job.go @@ -12,6 +12,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/scheduler/mgmt/2016-03-01/scheduler" "github.com/Azure/go-autorest/autorest/date" @@ -295,6 +296,9 @@ func resourceArmSchedulerJobActionWebSchema(propertyName string) *schema.Resourc "headers": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, //authentication requires HTTPS @@ -440,7 +444,6 @@ func resourceArmSchedulerJobActionStorageSchema() *schema.Resource { } func resourceArmSchedulerJobCustomizeDiff(diff *schema.ResourceDiff, _ interface{}) error { - _, hasWeb := diff.GetOk("action_web") _, hasStorage := diff.GetOk("action_storage_queue") if !hasWeb && !hasStorage { @@ -449,7 +452,6 @@ func resourceArmSchedulerJobCustomizeDiff(diff *schema.ResourceDiff, _ interface if b, ok := diff.GetOk("recurrence"); ok { if recurrence, ok := b.([]interface{})[0].(map[string]interface{}); ok { - //if neither count nor end time is set the API will silently fail _, hasCount := recurrence["count"] _, hasEnd := recurrence["end_time"] @@ -463,14 +465,14 @@ func resourceArmSchedulerJobCustomizeDiff(diff *schema.ResourceDiff, _ interface } func resourceArmSchedulerJobCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobsClient + client := meta.(*ArmClient).Scheduler.JobsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) jobCollection := d.Get("job_collection_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, jobCollection, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -520,10 +522,10 @@ func resourceArmSchedulerJobCreateUpdate(d *schema.ResourceData, meta interface{ } func resourceArmSchedulerJobRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobsClient + client := meta.(*ArmClient).Scheduler.JobsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -552,7 +554,6 @@ func resourceArmSchedulerJobRead(d *schema.ResourceData, meta interface{}) error //check & get properties properties := job.Properties if properties != nil { - //action action := properties.Action if action != nil { @@ -614,10 +615,10 @@ func resourceArmSchedulerJobRead(d *schema.ResourceData, meta interface{}) error } func resourceArmSchedulerJobDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobsClient + client := meta.(*ArmClient).Scheduler.JobsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -639,7 +640,6 @@ func resourceArmSchedulerJobDelete(d *schema.ResourceData, meta interface{}) err } func expandAzureArmSchedulerJobAction(d *schema.ResourceData, meta interface{}) *scheduler.JobAction { - action := scheduler.JobAction{} //action @@ -673,7 +673,6 @@ func expandAzureArmSchedulerJobAction(d *schema.ResourceData, meta interface{}) } func expandAzureArmSchedulerJobActionRequest(b interface{}, meta interface{}) (*scheduler.HTTPRequest, scheduler.JobActionType) { - block := b.([]interface{})[0].(map[string]interface{}) url := block["url"].(string) @@ -834,7 +833,6 @@ func expandAzureArmSchedulerJobRecurrence(b interface{}) *scheduler.JobRecurrenc // flatten (API --> terraform) func flattenAzureArmSchedulerJobActionRequest(d *schema.ResourceData, blockName string, request *scheduler.HTTPRequest) []interface{} { - block := map[string]interface{}{} if v := request.URI; v != nil { @@ -857,7 +855,6 @@ func flattenAzureArmSchedulerJobActionRequest(d *schema.ResourceData, blockName } if auth := request.Authentication; auth != nil { - authBlock := map[string]interface{}{} if basic, ok := auth.AsBasicAuthentication(); ok { @@ -871,7 +868,6 @@ func flattenAzureArmSchedulerJobActionRequest(d *schema.ResourceData, blockName if v, ok := d.GetOk(fmt.Sprintf("%s.0.authentication_basic.0.password", blockName)); ok { authBlock["password"] = v.(string) } - } else if cert, ok := auth.AsClientCertAuthentication(); ok { block["authentication_certificate"] = []interface{}{authBlock} @@ -892,7 +888,6 @@ func flattenAzureArmSchedulerJobActionRequest(d *schema.ResourceData, blockName if v, ok := d.GetOk(fmt.Sprintf("%s.0.authentication_certificate.0.password", blockName)); ok { authBlock["password"] = v.(string) } - } else if oauth, ok := auth.AsOAuthAuthentication(); ok { block["authentication_active_directory"] = []interface{}{authBlock} @@ -966,7 +961,6 @@ func flattenAzureArmSchedulerJobSchedule(recurrence *scheduler.JobRecurrence) [] } if schedule := recurrence.Schedule; schedule != nil { - if v := schedule.Minutes; v != nil { block["minutes"] = set.FromInt32Slice(*v) } @@ -988,7 +982,6 @@ func flattenAzureArmSchedulerJobSchedule(recurrence *scheduler.JobRecurrence) [] if monthly := schedule.MonthlyOccurrences; monthly != nil { s := &schema.Set{F: resourceAzureRMSchedulerJobMonthlyOccurrenceHash} for _, e := range *monthly { - m := map[string]interface{}{ "day": string(e.Day), } diff --git a/azurerm/resource_arm_scheduler_job_collection.go b/azurerm/resource_arm_scheduler_job_collection.go index 8d18346e88de..7006917186c2 100644 --- a/azurerm/resource_arm_scheduler_job_collection.go +++ b/azurerm/resource_arm_scheduler_job_collection.go @@ -8,7 +8,10 @@ import ( "regexp" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/scheduler/mgmt/2016-03-01/scheduler" @@ -46,12 +49,12 @@ func resourceArmSchedulerJobCollection() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), - "tags": tagsSchema(), + "tags": tags.Schema(), "sku": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(scheduler.Free), string(scheduler.Standard), @@ -65,7 +68,7 @@ func resourceArmSchedulerJobCollection() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(scheduler.Enabled), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(scheduler.Enabled), string(scheduler.Suspended), @@ -91,7 +94,7 @@ func resourceArmSchedulerJobCollection() *schema.Resource { "max_recurrence_frequency": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(scheduler.Minute), string(scheduler.Hour), @@ -124,17 +127,17 @@ func resourceArmSchedulerJobCollection() *schema.Resource { } func resourceArmSchedulerJobCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobCollectionsClient + client := meta.(*ArmClient).Scheduler.JobCollectionsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) resourceGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) log.Printf("[DEBUG] Creating/updating Scheduler Job Collection %q (resource group %q)", name, resourceGroup) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -149,7 +152,7 @@ func resourceArmSchedulerJobCollectionCreateUpdate(d *schema.ResourceData, meta collection := scheduler.JobCollectionDefinition{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), Properties: &scheduler.JobCollectionProperties{ Sku: &scheduler.Sku{ Name: scheduler.SkuDefinition(d.Get("sku").(string)), @@ -180,10 +183,10 @@ func resourceArmSchedulerJobCollectionCreateUpdate(d *schema.ResourceData, meta } func resourceArmSchedulerJobCollectionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobCollectionsClient + client := meta.(*ArmClient).Scheduler.JobCollectionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -209,7 +212,6 @@ func resourceArmSchedulerJobCollectionRead(d *schema.ResourceData, meta interfac if location := collection.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } - flattenAndSetTags(d, collection.Tags) //resource specific if properties := collection.Properties; properties != nil { @@ -223,14 +225,14 @@ func resourceArmSchedulerJobCollectionRead(d *schema.ResourceData, meta interfac } } - return nil + return tags.FlattenAndSet(d, collection.Tags) } func resourceArmSchedulerJobCollectionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).scheduler.JobCollectionsClient + client := meta.(*ArmClient).Scheduler.JobCollectionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -283,7 +285,6 @@ func expandAzureArmSchedulerJobCollectionQuota(d *schema.ResourceData) *schedule } func flattenAzureArmSchedulerJobCollectionQuota(quota *scheduler.JobCollectionQuota) []interface{} { - if quota == nil { return nil } diff --git a/azurerm/resource_arm_scheduler_job_collection_test.go b/azurerm/resource_arm_scheduler_job_collection_test.go index 34e95d3afea7..e0b32d2b1e9a 100644 --- a/azurerm/resource_arm_scheduler_job_collection_test.go +++ b/azurerm/resource_arm_scheduler_job_collection_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMSchedulerJobCollection_basic(t *testing.T) { } func TestAccAzureRMSchedulerJobCollection_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -97,7 +98,7 @@ func testCheckAzureRMSchedulerJobCollectionDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).scheduler.JobCollectionsClient + client := testAccProvider.Meta().(*ArmClient).Scheduler.JobCollectionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -130,7 +131,7 @@ func testCheckAzureRMSchedulerJobCollectionExists(resourceName string) resource. return fmt.Errorf("Bad: no resource group found in state for Scheduler Job Collection: %q", name) } - client := testAccProvider.Meta().(*ArmClient).scheduler.JobCollectionsClient + client := testAccProvider.Meta().(*ArmClient).Scheduler.JobCollectionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) @@ -172,7 +173,8 @@ resource "azurerm_scheduler_job_collection" "import" { location = "${azurerm_scheduler_job_collection.test.location}" resource_group_name = "${azurerm_scheduler_job_collection.test.resource_group_name}" sku = "${azurerm_scheduler_job_collection.test.sku}" - %s + + %s } `, testAccAzureRMSchedulerJobCollection_basic(rInt, location, additional), additional) } diff --git a/azurerm/resource_arm_scheduler_job_test.go b/azurerm/resource_arm_scheduler_job_test.go index c97fd13a3a6c..b929669632c3 100644 --- a/azurerm/resource_arm_scheduler_job_test.go +++ b/azurerm/resource_arm_scheduler_job_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -41,7 +42,7 @@ func TestAccAzureRMSchedulerJob_web_basic(t *testing.T) { } func TestAccAzureRMSchedulerJob_web_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -514,7 +515,7 @@ func testCheckAzureRMSchedulerJobDestroy(s *terraform.State) error { resourceGroup := rs.Primary.Attributes["resource_group_name"] jobCollection := rs.Primary.Attributes["job_collection_name"] - client := testAccProvider.Meta().(*ArmClient).scheduler.JobsClient + client := testAccProvider.Meta().(*ArmClient).Scheduler.JobsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, jobCollection, name) @@ -548,7 +549,7 @@ func testCheckAzureRMSchedulerJobExists(resourceName string) resource.TestCheckF return fmt.Errorf("Bad: no resource group found in state for Scheduler Job: %q", name) } - client := testAccProvider.Meta().(*ArmClient).scheduler.JobsClient + client := testAccProvider.Meta().(*ArmClient).Scheduler.JobsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, jobCollection, name) diff --git a/azurerm/resource_arm_search_service.go b/azurerm/resource_arm_search_service.go index 328c110f2d0a..885f5c0df504 100644 --- a/azurerm/resource_arm_search_service.go +++ b/azurerm/resource_arm_search_service.go @@ -6,6 +6,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2015-08-19/search" "github.com/hashicorp/terraform/helper/schema" @@ -17,6 +19,7 @@ func resourceArmSearchService() *schema.Resource { return &schema.Resource{ Create: resourceArmSearchServiceCreateUpdate, Read: resourceArmSearchServiceRead, + Update: resourceArmSearchServiceCreateUpdate, Delete: resourceArmSearchServiceDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -70,22 +73,22 @@ func resourceArmSearchService() *schema.Resource { Computed: true, }, - "tags": tagsForceNewSchema(), + "tags": tags.Schema(), }, } } func resourceArmSearchServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).search.ServicesClient + client := meta.(*ArmClient).Search.ServicesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) resourceGroup := d.Get("resource_group_name").(string) skuName := d.Get("sku").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name, nil) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -104,7 +107,7 @@ func resourceArmSearchServiceCreateUpdate(d *schema.ResourceData, meta interface Name: search.SkuName(skuName), }, ServiceProperties: &search.ServiceProperties{}, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if v, ok := d.GetOk("replica_count"); ok { @@ -132,10 +135,10 @@ func resourceArmSearchServiceCreateUpdate(d *schema.ResourceData, meta interface } func resourceArmSearchServiceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).search.ServicesClient + client := meta.(*ArmClient).Search.ServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -173,23 +176,21 @@ func resourceArmSearchServiceRead(d *schema.ResourceData, meta interface{}) erro } } - adminKeysClient := meta.(*ArmClient).search.AdminKeysClient + adminKeysClient := meta.(*ArmClient).Search.AdminKeysClient adminKeysResp, err := adminKeysClient.Get(ctx, resourceGroup, name, nil) if err == nil { d.Set("primary_key", adminKeysResp.PrimaryKey) d.Set("secondary_key", adminKeysResp.SecondaryKey) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSearchServiceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).search.ServicesClient + client := meta.(*ArmClient).Search.ServicesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_search_service_test.go b/azurerm/resource_arm_search_service_test.go index c68c433c3565..712c0cb1dd8a 100644 --- a/azurerm/resource_arm_search_service_test.go +++ b/azurerm/resource_arm_search_service_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMSearchService_basic(t *testing.T) { } func TestAccAzureRMSearchService_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -92,9 +93,37 @@ func TestAccAzureRMSearchService_complete(t *testing.T) { }) } +func TestAccAzureRMSearchService_tagUpdate(t *testing.T) { + resourceName := "azurerm_search_service.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSearchServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSearchService_withCustomTagValue(ri, testLocation(), "staging"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSearchServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "staging"), + ), + }, + { + Config: testAccAzureRMSearchService_withCustomTagValue(ri, testLocation(), "production"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSearchServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "production"), + ), + }, + }, + }) +} + func testCheckAzureRMSearchServiceExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -103,7 +132,7 @@ func testCheckAzureRMSearchServiceExists(resourceName string) resource.TestCheck resourceGroup := rs.Primary.Attributes["resource_group_name"] searchName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).search.ServicesClient + client := testAccProvider.Meta().(*ArmClient).Search.ServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, searchName, nil) @@ -128,7 +157,7 @@ func testCheckAzureRMSearchServiceDestroy(s *terraform.State) error { resourceGroup := rs.Primary.Attributes["resource_group_name"] searchName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).search.ServicesClient + client := testAccProvider.Meta().(*ArmClient).Search.ServicesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, searchName, nil) @@ -146,7 +175,7 @@ func testCheckAzureRMSearchServiceDestroy(s *terraform.State) error { return nil } -func testAccAzureRMSearchService_basic(rInt int, location string) string { +func testAccAzureRMSearchService_withCustomTagValue(rInt int, location string, tagValue string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" @@ -160,10 +189,14 @@ resource "azurerm_search_service" "test" { sku = "standard" tags = { - environment = "staging" + environment = "%s" } } -`, rInt, location, rInt) +`, rInt, location, rInt, tagValue) +} + +func testAccAzureRMSearchService_basic(rInt int, location string) string { + return testAccAzureRMSearchService_withCustomTagValue(rInt, location, "staging") } func testAccAzureRMSearchService_requiresImport(rInt int, location string) string { diff --git a/azurerm/resource_arm_security_center_contact.go b/azurerm/resource_arm_security_center_contact.go index f5e29fc266dc..8fc9df0469a5 100644 --- a/azurerm/resource_arm_security_center_contact.go +++ b/azurerm/resource_arm_security_center_contact.go @@ -5,6 +5,7 @@ import ( "log" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v1.0/security" "github.com/hashicorp/terraform/helper/schema" @@ -38,7 +39,7 @@ func resourceArmSecurityCenterContact() *schema.Resource { "phone": { Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validate.NoEmptyStrings, }, @@ -56,12 +57,12 @@ func resourceArmSecurityCenterContact() *schema.Resource { } func resourceArmSecurityCenterContactCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterContactsClient + client := meta.(*ArmClient).SecurityCenter.ContactsClient ctx := meta.(*ArmClient).StopContext name := securityCenterContactName - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -94,7 +95,6 @@ func resourceArmSecurityCenterContactCreateUpdate(d *schema.ResourceData, meta i } if d.IsNewResource() { - if _, err := client.Create(ctx, name, contact); err != nil { return fmt.Errorf("Error creating Security Center Contact: %+v", err) } @@ -118,7 +118,7 @@ func resourceArmSecurityCenterContactCreateUpdate(d *schema.ResourceData, meta i } func resourceArmSecurityCenterContactRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterContactsClient + client := meta.(*ArmClient).SecurityCenter.ContactsClient ctx := meta.(*ArmClient).StopContext name := securityCenterContactName @@ -145,7 +145,7 @@ func resourceArmSecurityCenterContactRead(d *schema.ResourceData, meta interface } func resourceArmSecurityCenterContactDelete(_ *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterContactsClient + client := meta.(*ArmClient).SecurityCenter.ContactsClient ctx := meta.(*ArmClient).StopContext name := securityCenterContactName diff --git a/azurerm/resource_arm_security_center_contact_test.go b/azurerm/resource_arm_security_center_contact_test.go index 6ae8599c4842..a64492f74d60 100644 --- a/azurerm/resource_arm_security_center_contact_test.go +++ b/azurerm/resource_arm_security_center_contact_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -16,6 +17,7 @@ func TestAccAzureRMSecurityCenter_contact(t *testing.T) { "basic": testAccAzureRMSecurityCenterContact_basic, "update": testAccAzureRMSecurityCenterContact_update, "requiresImport": testAccAzureRMSecurityCenterContact_requiresImport, + "phoneOptional": testAccAzureRMSecurityCenterContact_phoneOptional, }, } @@ -60,7 +62,7 @@ func testAccAzureRMSecurityCenterContact_basic(t *testing.T) { } func testAccAzureRMSecurityCenterContact_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -127,9 +129,36 @@ func testAccAzureRMSecurityCenterContact_update(t *testing.T) { }) } +func testAccAzureRMSecurityCenterContact_phoneOptional(t *testing.T) { + resourceName := "azurerm_security_center_contact.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSecurityCenterContactDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSecurityCenterContact_templateWithoutPhone("basic@example.com", true, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterContactExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "email", "basic@example.com"), + resource.TestCheckResourceAttr(resourceName, "phone", ""), + resource.TestCheckResourceAttr(resourceName, "alert_notifications", "true"), + resource.TestCheckResourceAttr(resourceName, "alerts_to_admins", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMSecurityCenterContactExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).securityCenterContactsClient + client := testAccProvider.Meta().(*ArmClient).SecurityCenter.ContactsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext rs, ok := s.RootModule().Resources[resourceName] @@ -153,7 +182,7 @@ func testCheckAzureRMSecurityCenterContactExists(resourceName string) resource.T } func testCheckAzureRMSecurityCenterContactDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).securityCenterContactsClient + client := testAccProvider.Meta().(*ArmClient).SecurityCenter.ContactsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, res := range s.RootModule().Resources { if res.Type != "azurerm_security_center_contact" { @@ -183,6 +212,17 @@ resource "azurerm_security_center_contact" "test" { `, email, phone, notifications, adminAlerts) } +func testAccAzureRMSecurityCenterContact_templateWithoutPhone(email string, notifications, adminAlerts bool) string { + return fmt.Sprintf(` +resource "azurerm_security_center_contact" "test" { + email = "%s" + + alert_notifications = %t + alerts_to_admins = %t +} +`, email, notifications, adminAlerts) +} + func testAccAzureRMSecurityCenterContact_requiresImportCfg(email, phone string, notifications, adminAlerts bool) string { return fmt.Sprintf(` %s diff --git a/azurerm/resource_arm_security_center_subscription_pricing.go b/azurerm/resource_arm_security_center_subscription_pricing.go index 55ff2f7bb6e1..04733e68fb79 100644 --- a/azurerm/resource_arm_security_center_subscription_pricing.go +++ b/azurerm/resource_arm_security_center_subscription_pricing.go @@ -39,7 +39,7 @@ func resourceArmSecurityCenterSubscriptionPricing() *schema.Resource { } func resourceArmSecurityCenterSubscriptionPricingUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterPricingClient + client := meta.(*ArmClient).SecurityCenter.PricingClient ctx := meta.(*ArmClient).StopContext name := securityCenterSubscriptionPricingName @@ -71,7 +71,7 @@ func resourceArmSecurityCenterSubscriptionPricingUpdate(d *schema.ResourceData, } func resourceArmSecurityCenterSubscriptionPricingRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterPricingClient + client := meta.(*ArmClient).SecurityCenter.PricingClient ctx := meta.(*ArmClient).StopContext resp, err := client.GetSubscriptionPricing(ctx, securityCenterSubscriptionPricingName) diff --git a/azurerm/resource_arm_security_center_subscription_pricing_test.go b/azurerm/resource_arm_security_center_subscription_pricing_test.go index 38a9235b83e9..17a96c467af1 100644 --- a/azurerm/resource_arm_security_center_subscription_pricing_test.go +++ b/azurerm/resource_arm_security_center_subscription_pricing_test.go @@ -46,7 +46,7 @@ func testAccAzureRMSecurityCenterSubscriptionPricing_update(t *testing.T) { func testCheckAzureRMSecurityCenterSubscriptionPricingExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).securityCenterPricingClient + client := testAccProvider.Meta().(*ArmClient).SecurityCenter.PricingClient ctx := testAccProvider.Meta().(*ArmClient).StopContext rs, ok := s.RootModule().Resources[resourceName] diff --git a/azurerm/resource_arm_security_center_workspace.go b/azurerm/resource_arm_security_center_workspace.go index de88a22546db..c4fab1747d55 100644 --- a/azurerm/resource_arm_security_center_workspace.go +++ b/azurerm/resource_arm_security_center_workspace.go @@ -11,6 +11,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,13 +47,13 @@ func resourceArmSecurityCenterWorkspace() *schema.Resource { } func resourceArmSecurityCenterWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - priceClient := meta.(*ArmClient).securityCenterPricingClient - client := meta.(*ArmClient).securityCenterWorkspaceClient + priceClient := meta.(*ArmClient).SecurityCenter.PricingClient + client := meta.(*ArmClient).SecurityCenter.WorkspaceClient ctx := meta.(*ArmClient).StopContext name := securityCenterWorkspaceName - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -103,7 +104,6 @@ func resourceArmSecurityCenterWorkspaceCreateUpdate(d *schema.ResourceData, meta Timeout: 30 * time.Minute, MinTimeout: 30 * time.Second, Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, name) if err2 != nil { return resp, "Error", fmt.Errorf("Error reading Security Center Workspace: %+v", err2) @@ -132,7 +132,7 @@ func resourceArmSecurityCenterWorkspaceCreateUpdate(d *schema.ResourceData, meta } func resourceArmSecurityCenterWorkspaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterWorkspaceClient + client := meta.(*ArmClient).SecurityCenter.WorkspaceClient ctx := meta.(*ArmClient).StopContext resp, err := client.Get(ctx, securityCenterWorkspaceName) @@ -155,7 +155,7 @@ func resourceArmSecurityCenterWorkspaceRead(d *schema.ResourceData, meta interfa } func resourceArmSecurityCenterWorkspaceDelete(_ *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).securityCenterWorkspaceClient + client := meta.(*ArmClient).SecurityCenter.WorkspaceClient ctx := meta.(*ArmClient).StopContext resp, err := client.Delete(ctx, securityCenterWorkspaceName) diff --git a/azurerm/resource_arm_security_center_workspace_test.go b/azurerm/resource_arm_security_center_workspace_test.go index f2b4747ed1d0..b536a46165bd 100644 --- a/azurerm/resource_arm_security_center_workspace_test.go +++ b/azurerm/resource_arm_security_center_workspace_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,7 +44,7 @@ func testAccAzureRMSecurityCenterWorkspace_basic(t *testing.T) { } func testAccAzureRMSecurityCenterWorkspace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -116,7 +117,7 @@ func testAccAzureRMSecurityCenterWorkspace_update(t *testing.T) { func testCheckAzureRMSecurityCenterWorkspaceExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).securityCenterWorkspaceClient + client := testAccProvider.Meta().(*ArmClient).SecurityCenter.WorkspaceClient ctx := testAccProvider.Meta().(*ArmClient).StopContext rs, ok := s.RootModule().Resources[resourceName] @@ -140,7 +141,7 @@ func testCheckAzureRMSecurityCenterWorkspaceExists(resourceName string) resource } func testCheckAzureRMSecurityCenterWorkspaceDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).securityCenterWorkspaceClient + client := testAccProvider.Meta().(*ArmClient).SecurityCenter.WorkspaceClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, res := range s.RootModule().Resources { diff --git a/azurerm/resource_arm_service_fabric_cluster.go b/azurerm/resource_arm_service_fabric_cluster.go index 8ac65a5460f0..6d6f11b3d587 100644 --- a/azurerm/resource_arm_service_fabric_cluster.go +++ b/azurerm/resource_arm_service_fabric_cluster.go @@ -11,14 +11,16 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func resourceArmServiceFabricCluster() *schema.Resource { return &schema.Resource{ - Create: resourceArmServiceFabricClusterCreate, + Create: resourceArmServiceFabricClusterCreateUpdate, Read: resourceArmServiceFabricClusterRead, - Update: resourceArmServiceFabricClusterUpdate, + Update: resourceArmServiceFabricClusterCreateUpdate, Delete: resourceArmServiceFabricClusterDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -51,8 +53,8 @@ func resourceArmServiceFabricCluster() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(servicefabric.Automatic), - string(servicefabric.Manual), + string(servicefabric.UpgradeModeAutomatic), + string(servicefabric.UpgradeModeManual), }, false), }, @@ -84,33 +86,33 @@ func resourceArmServiceFabricCluster() *schema.Resource { "azure_active_directory": { Type: schema.TypeList, Optional: true, - ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "tenant_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.UUID, }, "cluster_application_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.UUID, }, "client_application_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.UUID, }, }, }, }, "certificate": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"certificate_common_names"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "thumbprint": { @@ -129,6 +131,41 @@ func resourceArmServiceFabricCluster() *schema.Resource { }, }, + "certificate_common_names": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"certificate"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "common_names": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_common_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "certificate_issuer_thumbprint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "x509_store_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "reverse_proxy_certificate": { Type: schema.TypeList, Optional: true, @@ -172,34 +209,28 @@ func resourceArmServiceFabricCluster() *schema.Resource { "diagnostics_config": { Type: schema.TypeList, Optional: true, - ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "storage_account_name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "protected_account_key_name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "blob_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "queue_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "table_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, }, }, @@ -217,6 +248,9 @@ func resourceArmServiceFabricCluster() *schema.Resource { "parameters": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, }, @@ -230,15 +264,20 @@ func resourceArmServiceFabricCluster() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "placement_properties": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "capacities": { Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, "instance_count": { Type: schema.TypeInt, @@ -247,17 +286,14 @@ func resourceArmServiceFabricCluster() *schema.Resource { "is_primary": { Type: schema.TypeBool, Required: true, - ForceNew: true, }, "client_endpoint_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "http_endpoint_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "reverse_proxy_endpoint_port": { Type: schema.TypeInt, @@ -268,7 +304,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(servicefabric.Bronze), - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(servicefabric.Bronze), string(servicefabric.Gold), @@ -279,7 +314,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { "application_ports": { Type: schema.TypeList, Optional: true, - ForceNew: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ @@ -287,12 +321,10 @@ func resourceArmServiceFabricCluster() *schema.Resource { "start_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "end_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, }, }, @@ -301,7 +333,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { "ephemeral_ports": { Type: schema.TypeList, Optional: true, - ForceNew: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ @@ -309,12 +340,10 @@ func resourceArmServiceFabricCluster() *schema.Resource { "start_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "end_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, }, }, @@ -323,7 +352,7 @@ func resourceArmServiceFabricCluster() *schema.Resource { }, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "cluster_endpoint": { Type: schema.TypeString, @@ -333,8 +362,8 @@ func resourceArmServiceFabricCluster() *schema.Resource { } } -func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).serviceFabricClustersClient +func resourceArmServiceFabricClusterCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).ServiceFabric.ClustersClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Service Fabric Cluster creation.") @@ -347,9 +376,9 @@ func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interfac upgradeMode := d.Get("upgrade_mode").(string) clusterCodeVersion := d.Get("cluster_code_version").(string) vmImage := d.Get("vm_image").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -368,9 +397,6 @@ func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interfac azureActiveDirectoryRaw := d.Get("azure_active_directory").([]interface{}) azureActiveDirectory := expandServiceFabricClusterAzureActiveDirectory(azureActiveDirectoryRaw) - certificateRaw := d.Get("certificate").([]interface{}) - certificate := expandServiceFabricClusterCertificate(certificateRaw) - reverseProxyCertificateRaw := d.Get("reverse_proxy_certificate").([]interface{}) reverseProxyCertificate := expandServiceFabricClusterReverseProxyCertificate(reverseProxyCertificateRaw) @@ -388,11 +414,11 @@ func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interfac cluster := servicefabric.Cluster{ Location: utils.String(location), - Tags: expandTags(tags), + Tags: tags.Expand(t), ClusterProperties: &servicefabric.ClusterProperties{ AddOnFeatures: addOnFeatures, AzureActiveDirectory: azureActiveDirectory, - Certificate: certificate, + CertificateCommonNames: expandServiceFabricClusterCertificateCommonNames(d), ReverseProxyCertificate: reverseProxyCertificate, ClientCertificateThumbprints: clientCertificateThumbprints, DiagnosticsStorageAccountConfig: diagnostics, @@ -405,6 +431,11 @@ func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interfac }, } + if certificateRaw, ok := d.GetOk("certificate"); ok { + certificate := expandServiceFabricClusterCertificate(certificateRaw.([]interface{})) + cluster.ClusterProperties.Certificate = certificate + } + if clusterCodeVersion != "" { cluster.ClusterProperties.ClusterCodeVersion = utils.String(clusterCodeVersion) } @@ -431,72 +462,11 @@ func resourceArmServiceFabricClusterCreate(d *schema.ResourceData, meta interfac return resourceArmServiceFabricClusterRead(d, meta) } -func resourceArmServiceFabricClusterUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).serviceFabricClustersClient - ctx := meta.(*ArmClient).StopContext - - log.Printf("[INFO] preparing arguments for Service Fabric Cluster update.") - - resourceGroup := d.Get("resource_group_name").(string) - name := d.Get("name").(string) - reliabilityLevel := d.Get("reliability_level").(string) - upgradeMode := d.Get("upgrade_mode").(string) - clusterCodeVersion := d.Get("cluster_code_version").(string) - tags := d.Get("tags").(map[string]interface{}) - - addOnFeaturesRaw := d.Get("add_on_features").(*schema.Set).List() - addOnFeatures := expandServiceFabricClusterAddOnFeatures(addOnFeaturesRaw) - - certificateRaw := d.Get("certificate").([]interface{}) - certificate := expandServiceFabricClusterCertificate(certificateRaw) - - reverseProxyCertificateRaw := d.Get("reverse_proxy_certificate").([]interface{}) - reverseProxyCertificate := expandServiceFabricClusterReverseProxyCertificate(reverseProxyCertificateRaw) - - clientCertificateThumbprintsRaw := d.Get("client_certificate_thumbprint").([]interface{}) - clientCertificateThumbprints := expandServiceFabricClusterClientCertificateThumbprints(clientCertificateThumbprintsRaw) - - fabricSettingsRaw := d.Get("fabric_settings").([]interface{}) - fabricSettings := expandServiceFabricClusterFabricSettings(fabricSettingsRaw) - - nodeTypesRaw := d.Get("node_type").([]interface{}) - nodeTypes := expandServiceFabricClusterNodeTypes(nodeTypesRaw) - - parameters := servicefabric.ClusterUpdateParameters{ - ClusterPropertiesUpdateParameters: &servicefabric.ClusterPropertiesUpdateParameters{ - AddOnFeatures: addOnFeatures, - Certificate: certificate, - ReverseProxyCertificate: reverseProxyCertificate, - ClientCertificateThumbprints: clientCertificateThumbprints, - FabricSettings: fabricSettings, - NodeTypes: nodeTypes, - ReliabilityLevel: servicefabric.ReliabilityLevel1(reliabilityLevel), - UpgradeMode: servicefabric.UpgradeMode1(upgradeMode), - }, - Tags: expandTags(tags), - } - - if clusterCodeVersion != "" { - parameters.ClusterPropertiesUpdateParameters.ClusterCodeVersion = utils.String(clusterCodeVersion) - } - - future, err := client.Update(ctx, resourceGroup, name, parameters) - if err != nil { - return fmt.Errorf("Error updating Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for update of Service Fabric Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - return resourceArmServiceFabricClusterRead(d, meta) -} - func resourceArmServiceFabricClusterRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).serviceFabricClustersClient + client := meta.(*ArmClient).ServiceFabric.ClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -544,6 +514,11 @@ func resourceArmServiceFabricClusterRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error setting `certificate`: %+v", err) } + certificateCommonNames := flattenServiceFabricClusterCertificateCommonNames(props.CertificateCommonNames) + if err := d.Set("certificate_common_names", certificateCommonNames); err != nil { + return fmt.Errorf("Error setting `certificate_common_names`: %+v", err) + } + reverseProxyCertificate := flattenServiceFabricClusterReverseProxyCertificate(props.ReverseProxyCertificate) if err := d.Set("reverse_proxy_certificate", reverseProxyCertificate); err != nil { return fmt.Errorf("Error setting `reverse_proxy_certificate`: %+v", err) @@ -570,16 +545,14 @@ func resourceArmServiceFabricClusterRead(d *schema.ResourceData, meta interface{ } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmServiceFabricClusterDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).serviceFabricClustersClient + client := meta.(*ArmClient).ServiceFabric.ClustersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -706,6 +679,70 @@ func flattenServiceFabricClusterCertificate(input *servicefabric.CertificateDesc return results } +func expandServiceFabricClusterCertificateCommonNames(d *schema.ResourceData) *servicefabric.ServerCertificateCommonNames { + i := d.Get("certificate_common_names").([]interface{}) + if len(i) <= 0 || i[0] == nil { + return nil + } + input := i[0].(map[string]interface{}) + + commonNamesRaw := input["common_names"].(*schema.Set).List() + commonNames := make([]servicefabric.ServerCertificateCommonName, 0) + + for _, commonName := range commonNamesRaw { + commonNameDetails := commonName.(map[string]interface{}) + certificateCommonName := commonNameDetails["certificate_common_name"].(string) + certificateIssuerThumbprint := commonNameDetails["certificate_issuer_thumbprint"].(string) + + commonName := servicefabric.ServerCertificateCommonName{ + CertificateCommonName: &certificateCommonName, + CertificateIssuerThumbprint: &certificateIssuerThumbprint, + } + + commonNames = append(commonNames, commonName) + } + + x509StoreName := input["x509_store_name"].(string) + + output := servicefabric.ServerCertificateCommonNames{ + CommonNames: &commonNames, + X509StoreName: servicefabric.X509StoreName1(x509StoreName), + } + + return &output +} + +func flattenServiceFabricClusterCertificateCommonNames(in *servicefabric.ServerCertificateCommonNames) []interface{} { + if in == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + if commonNames := in.CommonNames; commonNames != nil { + common_names := make([]map[string]interface{}, 0) + for _, i := range *commonNames { + commonName := make(map[string]interface{}) + + if i.CertificateCommonName != nil { + commonName["certificate_common_name"] = *i.CertificateCommonName + } + + if i.CertificateIssuerThumbprint != nil { + commonName["certificate_issuer_thumbprint"] = *i.CertificateIssuerThumbprint + } + + common_names = append(common_names, commonName) + } + + output["common_names"] = common_names + } + + output["x509_store_name"] = string(in.X509StoreName) + + return []interface{}{output} +} + func expandServiceFabricClusterReverseProxyCertificate(input []interface{}) *servicefabric.CertificateDescription { if len(input) == 0 { return nil diff --git a/azurerm/resource_arm_service_fabric_cluster_test.go b/azurerm/resource_arm_service_fabric_cluster_test.go index 555b85c45c9a..4cddb95ec36a 100644 --- a/azurerm/resource_arm_service_fabric_cluster_test.go +++ b/azurerm/resource_arm_service_fabric_cluster_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMServiceFabricCluster_basic(t *testing.T) { @@ -45,8 +46,59 @@ func TestAccAzureRMServiceFabricCluster_basic(t *testing.T) { }) } +func TestAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_basic(ri, testLocation(), 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "http://example:80"), + resource.TestCheckResourceAttr(resourceName, "add_on_features.#", "0"), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "reverse_proxy_certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "client_certificate_thumbprint.#", "0"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "node_type.#", "1"), + resource.TestCheckResourceAttr(resourceName, "node_type.0.is_primary", "true"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(ri, testLocation(), 3, 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "http://example:80"), + resource.TestCheckResourceAttr(resourceName, "add_on_features.#", "0"), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "reverse_proxy_certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "client_certificate_thumbprint.#", "0"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "node_type.#", "2"), + resource.TestCheckResourceAttr(resourceName, "node_type.0.is_primary", "true"), + resource.TestCheckResourceAttr(resourceName, "node_type.1.is_primary", "false"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -404,6 +456,35 @@ func TestAccAzureRMServiceFabricCluster_readerAdminClientCertificateThumbprint(t }) } +func TestAccAzureRMServiceFabricCluster_certificateCommonNames(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_certificateCommonNames(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate_common_names.0.common_names.2962847220.certificate_common_name", "example"), + resource.TestCheckResourceAttr(resourceName, "certificate_common_names.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:80"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_azureActiveDirectory(t *testing.T) { resourceName := "azurerm_service_fabric_cluster.test" ri := tf.AccRandTimeInt() @@ -426,7 +507,54 @@ func TestAccAzureRMServiceFabricCluster_azureActiveDirectory(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.client_application_id"), resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), - resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:80"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_azureActiveDirectory(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "1"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.thumbprint", "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.tenant_id"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.cluster_application_id"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.client_application_id"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "1"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.thumbprint", "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), ), }, { @@ -469,6 +597,44 @@ func TestAccAzureRMServiceFabricCluster_diagnosticsConfig(t *testing.T) { }) } +func TestAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_diagnosticsConfig(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.storage_account_name"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.protected_account_key_name"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.blob_endpoint"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.queue_endpoint"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.table_endpoint"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_fabricSettings(t *testing.T) { resourceName := "azurerm_service_fabric_cluster.test" ri := tf.AccRandTimeInt() @@ -669,7 +835,7 @@ func TestAccAzureRMServiceFabricCluster_tags(t *testing.T) { } func testCheckAzureRMServiceFabricClusterDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).serviceFabricClustersClient + client := testAccProvider.Meta().(*ArmClient).ServiceFabric.ClustersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -708,7 +874,7 @@ func testCheckAzureRMServiceFabricClusterExists(resourceName string) resource.Te return fmt.Errorf("Bad: no resource group found in state for Service Fabric Cluster %q", clusterName) } - client := testAccProvider.Meta().(*ArmClient).serviceFabricClustersClient + client := testAccProvider.Meta().(*ArmClient).ServiceFabric.ClustersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, clusterName) @@ -738,8 +904,8 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = %d @@ -751,6 +917,41 @@ resource "azurerm_service_fabric_cluster" "test" { `, rInt, location, rInt, count) } +func testAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(rInt int, location string, count int, secondary_count int) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "http://example:80" + + node_type { + name = "first" + instance_count = %d + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } + + node_type { + name = "second" + instance_count = %d + is_primary = false + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rInt, count, secondary_count) +} + func testAccAzureRMServiceFabricCluster_requiresImport(rInt int, location string, count int) string { return fmt.Sprintf(` %s @@ -762,8 +963,8 @@ resource "azurerm_service_fabric_cluster" "import" { reliability_level = "${azurerm_service_fabric_cluster.test.reliability_level}" upgrade_mode = "${azurerm_service_fabric_cluster.test.upgrade_mode}" vm_image = "${azurerm_service_fabric_cluster.test.vm_image}" - management_endpoint = "${azurerm_service_fabric_cluster.test.management_endpoint}" - + management_endpoint = "${azurerm_service_fabric_cluster.test.management_endpoint}" + node_type { name = "first" instance_count = %d @@ -790,8 +991,8 @@ resource "azurerm_service_fabric_cluster" "test" { upgrade_mode = "Manual" cluster_code_version = "%[3]s" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 @@ -818,8 +1019,8 @@ resource "azurerm_service_fabric_cluster" "test" { upgrade_mode = "Automatic" vm_image = "Windows" management_endpoint = "http://example:80" - add_on_features = ["DnsService", "RepairManager"] - + add_on_features = ["DnsService", "RepairManager"] + node_type { name = "first" instance_count = 3 @@ -845,21 +1046,21 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -885,26 +1086,26 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + reverse_proxy_certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -931,26 +1132,26 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = true - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -976,31 +1177,74 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = true - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = false - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + + node_type { + name = "first" + instance_count = 3 + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMServiceFabricCluster_certificateCommonNames(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "https://example:80" + + certificate_common_names { + common_names { + certificate_common_name = "example" + } + + x509_store_name = "My" + } + + fabric_settings { + name = "Security" + + parameters = { + "ClusterProtectionLevel" = "EncryptAndSign" + } + } + node_type { name = "first" instance_count = 3 @@ -1021,13 +1265,60 @@ resource "azurerm_resource_group" "test" { data "azurerm_client_config" "current" {} -resource "azuread_application" "test" { - name = "${azurerm_resource_group.test.name}-AAD" - homepage = "https://example:80/Explorer/index.html" - identifier_uris = ["https://acctestAAD-app"] - reply_urls = ["https://acctestAAD-app"] +resource "azuread_application" "cluster_explorer" { + name = "${azurerm_resource_group.test.name}-explorer-AAD" + homepage = "https://example:19080/Explorer/index.html" + identifier_uris = ["https://example:19080/Explorer/index.html"] + reply_urls = ["https://example:19080/Explorer/index.html"] available_to_other_tenants = false oauth2_allow_implicit_flow = true + + # https://blogs.msdn.microsoft.com/aaddevsup/2018/06/06/guid-table-for-windows-azure-active-directory-permissions/ + # https://shawntabrizi.com/aad/common-microsoft-resources-azure-active-directory/ + required_resource_access { + resource_app_id = "00000002-0000-0000-c000-000000000000" + + resource_access { + id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" + type = "Scope" + } + } +} + +resource "azuread_service_principal" "cluster_explorer" { + application_id = "${azuread_application.cluster_explorer.application_id}" +} + +resource "azuread_application" "cluster_console" { + name = "${azurerm_resource_group.test.name}-console-AAD" + type = "native" + reply_urls = ["urn:ietf:wg:oauth:2.0:oob"] + available_to_other_tenants = false + oauth2_allow_implicit_flow = true + + # https://blogs.msdn.microsoft.com/aaddevsup/2018/06/06/guid-table-for-windows-azure-active-directory-permissions/ + # https://shawntabrizi.com/aad/common-microsoft-resources-azure-active-directory/ + required_resource_access { + resource_app_id = "00000002-0000-0000-c000-000000000000" + + resource_access { + id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" + type = "Scope" + } + } + + required_resource_access { + resource_app_id = "${azuread_application.cluster_explorer.application_id}" + + resource_access { + id = "${azuread_application.cluster_explorer.oauth2_permissions.0.id}" + type = "Scope" + } + } +} + +resource "azuread_service_principal" "cluster_console" { + application_id = "${azuread_application.cluster_console.application_id}" } resource "azurerm_service_fabric_cluster" "test" { @@ -1037,33 +1328,75 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:19080" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + azure_active_directory { tenant_id = "${data.azurerm_client_config.current.tenant_id}" - cluster_application_id = "${azuread_application.test.application_id}" - client_application_id = "00000000-0000-0000-0000-000000000000" - } - + cluster_application_id = "${azuread_application.cluster_explorer.application_id}" + client_application_id = "${azuread_application.cluster_console.application_id}" + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { - name = "first" + name = "system" instance_count = 3 is_primary = true - client_endpoint_port = 2020 - http_endpoint_port = 80 + client_endpoint_port = 19000 + http_endpoint_port = 19080 + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "https://example:19080" + + certificate { + thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" + x509_store_name = "My" + } + + fabric_settings { + name = "Security" + + parameters = { + "ClusterProtectionLevel" = "EncryptAndSign" + } + } + + node_type { + name = "system" + instance_count = 3 + is_primary = true + client_endpoint_port = 19000 + http_endpoint_port = 19080 } } `, rInt, location, rInt) @@ -1091,16 +1424,51 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + diagnostics_config { storage_account_name = "${azurerm_storage_account.test.name}" protected_account_key_name = "StorageAccountKey1" blob_endpoint = "${azurerm_storage_account.test.primary_blob_endpoint}" queue_endpoint = "${azurerm_storage_account.test.primary_queue_endpoint}" table_endpoint = "${azurerm_storage_account.test.primary_table_endpoint}" - } - + } + + node_type { + name = "first" + instance_count = 3 + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rString, rInt) +} + +func testAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(rInt int, rString, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 @@ -1126,16 +1494,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "None" } - } - + } + node_type { name = "first" instance_count = 3 @@ -1161,20 +1529,20 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 - http_endpoint_port = 80 - + http_endpoint_port = 80 + application_ports { start_port = 20000 end_port = 29999 - } - + } + ephemeral_ports { start_port = 30000 end_port = 39999 @@ -1198,16 +1566,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 http_endpoint_port = 80 - } - + } + node_type { name = "second" instance_count = 4 @@ -1225,6 +1593,7 @@ resource "azurerm_resource_group" "test" { name = "acctestRG-%d" location = "%s" } + resource "azurerm_service_fabric_cluster" "test" { name = "acctest-%d" resource_group_name = "${azurerm_resource_group.test.name}" @@ -1233,20 +1602,25 @@ resource "azurerm_service_fabric_cluster" "test" { upgrade_mode = "Automatic" vm_image = "Windows" management_endpoint = "http://example:80" + node_type { - name = "first" - placement_properties = { - "HasSSD" = "true" - } - capacities = { - "ClientConnections" = "20000" - "MemoryGB" = "8" - } + name = "first" + + placement_properties = { + "HasSSD" = "true" + } + + capacities = { + "ClientConnections" = "20000" + "MemoryGB" = "8" + } + instance_count = 3 is_primary = true client_endpoint_port = 2020 - http_endpoint_port = 80 + http_endpoint_port = 80 } + tags = { Hello = "World" } @@ -1268,16 +1642,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 http_endpoint_port = 80 - } - + } + tags = { Hello = "World" } diff --git a/azurerm/resource_arm_servicebus_namespace.go b/azurerm/resource_arm_servicebus_namespace.go index 546115c310c8..82fe5b97b703 100644 --- a/azurerm/resource_arm_servicebus_namespace.go +++ b/azurerm/resource_arm_servicebus_namespace.go @@ -7,9 +7,12 @@ import ( "strings" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" - "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" + "github.com/Azure/azure-sdk-for-go/services/preview/servicebus/mgmt/2018-01-01-preview/servicebus" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" @@ -59,7 +62,7 @@ func resourceArmServiceBusNamespace() *schema.Resource { string(servicebus.Standard), string(servicebus.Premium), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "capacity": { @@ -93,13 +96,19 @@ func resourceArmServiceBusNamespace() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "zone_redundant": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "tags": tags.Schema(), }, } } func resourceArmServiceBusNamespaceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClientPreview ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Namespace creation.") @@ -108,9 +117,9 @@ func resourceArmServiceBusNamespaceCreateUpdate(d *schema.ResourceData, meta int location := azure.NormalizeLocation(d.Get("location").(string)) resourceGroup := d.Get("resource_group_name").(string) sku := d.Get("sku").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -129,7 +138,7 @@ func resourceArmServiceBusNamespaceCreateUpdate(d *schema.ResourceData, meta int Name: servicebus.SkuName(sku), Tier: servicebus.SkuTier(sku), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if capacity := d.Get("capacity"); capacity != nil { @@ -142,6 +151,13 @@ func resourceArmServiceBusNamespaceCreateUpdate(d *schema.ResourceData, meta int parameters.Sku.Capacity = utils.Int32(int32(capacity.(int))) } + if zoneRedundant, ok := d.GetOkExists("zone_redundant"); ok { + properties := servicebus.SBNamespaceProperties{ + ZoneRedundant: utils.Bool(zoneRedundant.(bool)), + } + parameters.SBNamespaceProperties = &properties + } + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) if err != nil { return err @@ -166,10 +182,11 @@ func resourceArmServiceBusNamespaceCreateUpdate(d *schema.ResourceData, meta int } func resourceArmServiceBusNamespaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClientPreview + clientStable := meta.(*ArmClient).ServiceBus.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -196,7 +213,11 @@ func resourceArmServiceBusNamespaceRead(d *schema.ResourceData, meta interface{} d.Set("capacity", sku.Capacity) } - keys, err := client.ListKeys(ctx, resourceGroup, name, serviceBusNamespaceDefaultAuthorizationRule) + if properties := resp.SBNamespaceProperties; properties != nil { + d.Set("zone_redundant", properties.ZoneRedundant) + } + + keys, err := clientStable.ListKeys(ctx, resourceGroup, name, serviceBusNamespaceDefaultAuthorizationRule) if err != nil { log.Printf("[WARN] Unable to List default keys for Namespace %q (Resource Group %q): %+v", name, resourceGroup, err) } else { @@ -206,16 +227,14 @@ func resourceArmServiceBusNamespaceRead(d *schema.ResourceData, meta interface{} d.Set("default_secondary_key", keys.SecondaryKey) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmServiceBusNamespaceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClientPreview ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_namespace_authorization_rule.go b/azurerm/resource_arm_servicebus_namespace_authorization_rule.go index d7a141505a2e..4c830aac81a2 100644 --- a/azurerm/resource_arm_servicebus_namespace_authorization_rule.go +++ b/azurerm/resource_arm_servicebus_namespace_authorization_rule.go @@ -5,6 +5,7 @@ import ( "log" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" "github.com/hashicorp/terraform/helper/schema" @@ -47,7 +48,7 @@ func resourceArmServiceBusNamespaceAuthorizationRule() *schema.Resource { } func resourceArmServiceBusNamespaceAuthorizationRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Namespace Authorization Rule creation.") @@ -56,7 +57,7 @@ func resourceArmServiceBusNamespaceAuthorizationRuleCreateUpdate(d *schema.Resou resourceGroup := d.Get("resource_group_name").(string) namespaceName := d.Get("namespace_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -95,10 +96,10 @@ func resourceArmServiceBusNamespaceAuthorizationRuleCreateUpdate(d *schema.Resou } func resourceArmServiceBusNamespaceAuthorizationRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -141,10 +142,10 @@ func resourceArmServiceBusNamespaceAuthorizationRuleRead(d *schema.ResourceData, } func resourceArmServiceBusNamespaceAuthorizationRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.NamespacesClient + client := meta.(*ArmClient).ServiceBus.NamespacesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_namespace_authorization_rule_test.go b/azurerm/resource_arm_servicebus_namespace_authorization_rule_test.go index 5f7187d7f7cc..747d10016e9c 100644 --- a/azurerm/resource_arm_servicebus_namespace_authorization_rule_test.go +++ b/azurerm/resource_arm_servicebus_namespace_authorization_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -102,7 +103,7 @@ func TestAccAzureRMServiceBusNamespaceAuthorizationRule_rightsUpdate(t *testing. }) } func TestAccAzureRMServiceBusNamespaceAuthorizationRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -132,7 +133,7 @@ func TestAccAzureRMServiceBusNamespaceAuthorizationRule_requiresImport(t *testin } func testCheckAzureRMServiceBusNamespaceAuthorizationRuleDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).servicebus.NamespacesClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.NamespacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -170,7 +171,7 @@ func testCheckAzureRMServiceBusNamespaceAuthorizationRuleExists(resourceName str return fmt.Errorf("Bad: no resource group found in state for ServiceBus Namespace: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).servicebus.NamespacesClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.NamespacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, name) if err != nil { diff --git a/azurerm/resource_arm_servicebus_namespace_test.go b/azurerm/resource_arm_servicebus_namespace_test.go index ea5045470d30..1b9440b0f4a4 100644 --- a/azurerm/resource_arm_servicebus_namespace_test.go +++ b/azurerm/resource_arm_servicebus_namespace_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMServiceBusNamespace_basic(t *testing.T) { @@ -36,7 +37,7 @@ func TestAccAzureRMServiceBusNamespace_basic(t *testing.T) { }) } func TestAccAzureRMServiceBusNamespace_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -180,8 +181,34 @@ func TestAccAzureRMServiceBusNamespace_premiumCapacity(t *testing.T) { }) } +func TestAccAzureRMServiceBusNamespace_zoneRedundant(t *testing.T) { + resourceName := "azurerm_servicebus_namespace.test" + ri := tf.AccRandTimeInt() + config := testAccAzureRMServiceBusNamespace_zoneRedundant(ri, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusNamespaceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "zone_redundant", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMServiceBusNamespaceDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).servicebus.NamespacesClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.NamespacesClientPreview ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -220,7 +247,7 @@ func testCheckAzureRMServiceBusNamespaceExists(resourceName string) resource.Tes return fmt.Errorf("Bad: no resource group found in state for Service Bus Namespace: %s", namespaceName) } - client := testAccProvider.Meta().(*ArmClient).servicebus.NamespacesClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.NamespacesClientPreview ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, namespaceName) @@ -331,3 +358,21 @@ resource "azurerm_servicebus_namespace" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMServiceBusNamespace_zoneRedundant(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 + zone_redundant = true +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_servicebus_queue.go b/azurerm/resource_arm_servicebus_queue.go index 24509721c177..984f3860c24d 100644 --- a/azurerm/resource_arm_servicebus_queue.go +++ b/azurerm/resource_arm_servicebus_queue.go @@ -9,6 +9,8 @@ import ( "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -45,21 +47,21 @@ func resourceArmServiceBusQueue() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "default_message_ttl": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "duplicate_detection_history_time_window": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "enable_express": { @@ -132,7 +134,7 @@ func resourceArmServiceBusQueue() *schema.Resource { } func resourceArmServiceBusQueueCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Queue creation/update.") @@ -148,7 +150,7 @@ func resourceArmServiceBusQueueCreateUpdate(d *schema.ResourceData, meta interfa requiresSession := d.Get("requires_session").(bool) deadLetteringOnMessageExpiration := d.Get("dead_lettering_on_message_expiration").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -192,7 +194,7 @@ func resourceArmServiceBusQueueCreateUpdate(d *schema.ResourceData, meta interfa // We need to retrieve the namespace because Premium namespace works differently from Basic and Standard, // so it needs different rules applied to it. - namespacesClient := meta.(*ArmClient).servicebus.NamespacesClient + namespacesClient := meta.(*ArmClient).ServiceBus.NamespacesClient namespace, err := namespacesClient.Get(ctx, resourceGroup, namespaceName) if err != nil { return fmt.Errorf("Error retrieving ServiceBus Namespace %q (Resource Group %q): %+v", resourceGroup, namespaceName, err) @@ -222,10 +224,10 @@ func resourceArmServiceBusQueueCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmServiceBusQueueRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -265,7 +267,7 @@ func resourceArmServiceBusQueueRead(d *schema.ResourceData, meta interface{}) er // If the queue is NOT in a premium namespace (ie. it is Basic or Standard) and partitioning is enabled // then the max size returned by the API will be 16 times greater than the value set. if *props.EnablePartitioning { - namespacesClient := meta.(*ArmClient).servicebus.NamespacesClient + namespacesClient := meta.(*ArmClient).ServiceBus.NamespacesClient namespace, err := namespacesClient.Get(ctx, resourceGroup, namespaceName) if err != nil { return err @@ -285,10 +287,10 @@ func resourceArmServiceBusQueueRead(d *schema.ResourceData, meta interface{}) er } func resourceArmServiceBusQueueDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_queue_authorization_rule.go b/azurerm/resource_arm_servicebus_queue_authorization_rule.go index e5b489722805..c6a10377418d 100644 --- a/azurerm/resource_arm_servicebus_queue_authorization_rule.go +++ b/azurerm/resource_arm_servicebus_queue_authorization_rule.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -52,7 +53,7 @@ func resourceArmServiceBusQueueAuthorizationRule() *schema.Resource { } func resourceArmServiceBusQueueAuthorizationRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Queue Authorization Rule creation.") @@ -62,7 +63,7 @@ func resourceArmServiceBusQueueAuthorizationRuleCreateUpdate(d *schema.ResourceD namespaceName := d.Get("namespace_name").(string) queueName := d.Get("queue_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, queueName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -101,10 +102,10 @@ func resourceArmServiceBusQueueAuthorizationRuleCreateUpdate(d *schema.ResourceD } func resourceArmServiceBusQueueAuthorizationRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -150,10 +151,10 @@ func resourceArmServiceBusQueueAuthorizationRuleRead(d *schema.ResourceData, met } func resourceArmServiceBusQueueAuthorizationRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.QueuesClient + client := meta.(*ArmClient).ServiceBus.QueuesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_queue_authorization_rule_test.go b/azurerm/resource_arm_servicebus_queue_authorization_rule_test.go index c674bb7d0ca7..4b610ee6c422 100644 --- a/azurerm/resource_arm_servicebus_queue_authorization_rule_test.go +++ b/azurerm/resource_arm_servicebus_queue_authorization_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -102,7 +103,7 @@ func TestAccAzureRMServiceBusQueueAuthorizationRule_rightsUpdate(t *testing.T) { }) } func TestAccAzureRMServiceBusQueueAuthorizationRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -132,7 +133,7 @@ func TestAccAzureRMServiceBusQueueAuthorizationRule_requiresImport(t *testing.T) } func testCheckAzureRMServiceBusQueueAuthorizationRuleDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).servicebus.QueuesClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -172,7 +173,7 @@ func testCheckAzureRMServiceBusQueueAuthorizationRuleExists(resourceName string) return fmt.Errorf("Bad: no resource group found in state for ServiceBus Queue Authorization Rule: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).servicebus.QueuesClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, queueName, name) diff --git a/azurerm/resource_arm_servicebus_queue_test.go b/azurerm/resource_arm_servicebus_queue_test.go index c70f17ca27e8..d4a81e080efe 100644 --- a/azurerm/resource_arm_servicebus_queue_test.go +++ b/azurerm/resource_arm_servicebus_queue_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMServiceBusQueue_basic(t *testing.T) { }) } func TestAccAzureRMServiceBusQueue_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -354,7 +355,7 @@ func TestAccAzureRMServiceBusQueue_maxDeliveryCount(t *testing.T) { } func testCheckAzureRMServiceBusQueueDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).servicebus.QueuesClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -397,7 +398,7 @@ func testCheckAzureRMServiceBusQueueExists(resourceName string) resource.TestChe return fmt.Errorf("Bad: no resource group found in state for queue: %s", queueName) } - client := testAccProvider.Meta().(*ArmClient).servicebus.QueuesClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.QueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, namespaceName, queueName) diff --git a/azurerm/resource_arm_servicebus_subscription.go b/azurerm/resource_arm_servicebus_subscription.go index df42db09324e..9783e226c2f4 100644 --- a/azurerm/resource_arm_servicebus_subscription.go +++ b/azurerm/resource_arm_servicebus_subscription.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -103,7 +104,7 @@ func resourceArmServiceBusSubscription() *schema.Resource { } func resourceArmServiceBusSubscriptionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionsClient + client := meta.(*ArmClient).ServiceBus.SubscriptionsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ARM ServiceBus Subscription creation.") @@ -117,7 +118,7 @@ func resourceArmServiceBusSubscriptionCreateUpdate(d *schema.ResourceData, meta maxDeliveryCount := int32(d.Get("max_delivery_count").(int)) requiresSession := d.Get("requires_session").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, namespaceName, topicName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -173,10 +174,10 @@ func resourceArmServiceBusSubscriptionCreateUpdate(d *schema.ResourceData, meta } func resourceArmServiceBusSubscriptionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionsClient + client := meta.(*ArmClient).ServiceBus.SubscriptionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -217,10 +218,10 @@ func resourceArmServiceBusSubscriptionRead(d *schema.ResourceData, meta interfac } func resourceArmServiceBusSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionsClient + client := meta.(*ArmClient).ServiceBus.SubscriptionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_subscription_rule.go b/azurerm/resource_arm_servicebus_subscription_rule.go index ece0c74f001f..d6e1ed34f927 100644 --- a/azurerm/resource_arm_servicebus_subscription_rule.go +++ b/azurerm/resource_arm_servicebus_subscription_rule.go @@ -9,7 +9,9 @@ import ( "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -61,7 +63,7 @@ func resourceArmServiceBusSubscriptionRule() *schema.Resource { string(servicebus.FilterTypeSQLFilter), string(servicebus.FilterTypeCorrelationFilter), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "action": { @@ -121,7 +123,7 @@ func resourceArmServiceBusSubscriptionRule() *schema.Resource { } func resourceArmServiceBusSubscriptionRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionRulesClient + client := meta.(*ArmClient).ServiceBus.SubscriptionRulesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Service Bus Subscription Rule creation.") @@ -132,7 +134,7 @@ func resourceArmServiceBusSubscriptionRuleCreateUpdate(d *schema.ResourceData, m resourceGroup := d.Get("resource_group_name").(string) filterType := d.Get("filter_type").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, namespaceName, topicName, subscriptionName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -191,10 +193,10 @@ func resourceArmServiceBusSubscriptionRuleCreateUpdate(d *schema.ResourceData, m } func resourceArmServiceBusSubscriptionRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionRulesClient + client := meta.(*ArmClient).ServiceBus.SubscriptionRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -240,10 +242,10 @@ func resourceArmServiceBusSubscriptionRuleRead(d *schema.ResourceData, meta inte } func resourceArmServiceBusSubscriptionRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.SubscriptionRulesClient + client := meta.(*ArmClient).ServiceBus.SubscriptionRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_subscription_rule_test.go b/azurerm/resource_arm_servicebus_subscription_rule_test.go index 9645e5433f6c..9afb4602dac8 100644 --- a/azurerm/resource_arm_servicebus_subscription_rule_test.go +++ b/azurerm/resource_arm_servicebus_subscription_rule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -29,7 +30,7 @@ func TestAccAzureRMServiceBusSubscriptionRule_basicSqlFilter(t *testing.T) { }) } func TestAccAzureRMServiceBusSubscriptionRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -218,7 +219,7 @@ func testCheckAzureRMServiceBusSubscriptionRuleExists(resourceName string) resou return fmt.Errorf("Bad: no resource group found in state for Subscription Rule: %q", ruleName) } - client := testAccProvider.Meta().(*ArmClient).servicebus.SubscriptionRulesClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.SubscriptionRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, namespaceName, topicName, subscriptionName, ruleName) diff --git a/azurerm/resource_arm_servicebus_subscription_test.go b/azurerm/resource_arm_servicebus_subscription_test.go index cc4bc686c2ce..2f7acdf8a579 100644 --- a/azurerm/resource_arm_servicebus_subscription_test.go +++ b/azurerm/resource_arm_servicebus_subscription_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMServiceBusSubscription_basic(t *testing.T) { } func TestAccAzureRMServiceBusSubscription_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -190,7 +191,7 @@ func TestAccAzureRMServiceBusSubscription_updateForwardTo(t *testing.T) { } func testCheckAzureRMServiceBusSubscriptionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).servicebus.SubscriptionsClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.SubscriptionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -232,10 +233,10 @@ func testCheckAzureRMServiceBusSubscriptionExists(resourceName string) resource. namespaceName := rs.Primary.Attributes["namespace_name"] resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for subscription: %q", topicName) + return fmt.Errorf("Bad: no resource group found in state for Subscription: %q", topicName) } - client := testAccProvider.Meta().(*ArmClient).servicebus.SubscriptionsClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.SubscriptionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, namespaceName, topicName, subscriptionName) diff --git a/azurerm/resource_arm_servicebus_topic.go b/azurerm/resource_arm_servicebus_topic.go index 9a62f77478cb..0e10c9b98b0a 100644 --- a/azurerm/resource_arm_servicebus_topic.go +++ b/azurerm/resource_arm_servicebus_topic.go @@ -8,7 +8,10 @@ import ( "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -50,28 +53,28 @@ func resourceArmServiceBusTopic() *schema.Resource { string(servicebus.Active), string(servicebus.Disabled), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "auto_delete_on_idle": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "default_message_ttl": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "duplicate_detection_history_time_window": { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateIso8601Duration(), + ValidateFunc: validate.ISO8601Duration, }, "enable_batched_operations": { @@ -118,7 +121,7 @@ func resourceArmServiceBusTopic() *schema.Resource { } func resourceArmServiceBusTopicCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure ServiceBus Topic creation.") @@ -134,7 +137,7 @@ func resourceArmServiceBusTopicCreateUpdate(d *schema.ResourceData, meta interfa requiresDuplicateDetection := d.Get("requires_duplicate_detection").(bool) supportOrdering := d.Get("support_ordering").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, namespaceName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -190,10 +193,10 @@ func resourceArmServiceBusTopicCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmServiceBusTopicRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -220,7 +223,7 @@ func resourceArmServiceBusTopicRead(d *schema.ResourceData, meta interface{}) er d.Set("default_message_ttl", props.DefaultMessageTimeToLive) if window := props.DuplicateDetectionHistoryTimeWindow; window != nil && *window != "" { - d.Set("duplicate_detection_history_time_window", *window) + d.Set("duplicate_detection_history_time_window", window) } d.Set("enable_batched_operations", props.EnableBatchedOperations) @@ -235,7 +238,7 @@ func resourceArmServiceBusTopicRead(d *schema.ResourceData, meta interface{}) er // if the topic is in a premium namespace and partitioning is enabled then the // max size returned by the API will be 16 times greater than the value set if partitioning := props.EnablePartitioning; partitioning != nil && *partitioning { - namespacesClient := meta.(*ArmClient).servicebus.NamespacesClient + namespacesClient := meta.(*ArmClient).ServiceBus.NamespacesClient namespace, err := namespacesClient.Get(ctx, resourceGroup, namespaceName) if err != nil { return err @@ -255,10 +258,10 @@ func resourceArmServiceBusTopicRead(d *schema.ResourceData, meta interface{}) er } func resourceArmServiceBusTopicDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_topic_authorization_rule.go b/azurerm/resource_arm_servicebus_topic_authorization_rule.go index 93b3f0def088..698d1689f02f 100644 --- a/azurerm/resource_arm_servicebus_topic_authorization_rule.go +++ b/azurerm/resource_arm_servicebus_topic_authorization_rule.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -53,7 +54,7 @@ func resourceArmServiceBusTopicAuthorizationRule() *schema.Resource { } func resourceArmServiceBusTopicAuthorizationRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Topic Authorization Rule creation.") @@ -62,7 +63,7 @@ func resourceArmServiceBusTopicAuthorizationRuleCreateUpdate(d *schema.ResourceD topicName := d.Get("topic_name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.GetAuthorizationRule(ctx, resourceGroup, namespaceName, topicName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -101,10 +102,10 @@ func resourceArmServiceBusTopicAuthorizationRuleCreateUpdate(d *schema.ResourceD } func resourceArmServiceBusTopicAuthorizationRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -149,10 +150,10 @@ func resourceArmServiceBusTopicAuthorizationRuleRead(d *schema.ResourceData, met } func resourceArmServiceBusTopicAuthorizationRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).servicebus.TopicsClient + client := meta.(*ArmClient).ServiceBus.TopicsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go b/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go index 525f46487ec4..580a05fea0d3 100644 --- a/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go +++ b/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -60,7 +61,7 @@ func testAccAzureRMServiceBusTopicAuthorizationRule(t *testing.T, listen, send, } func TestAccAzureRMServiceBusTopicAuthorizationRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -132,7 +133,7 @@ func TestAccAzureRMServiceBusTopicAuthorizationRule_rightsUpdate(t *testing.T) { } func testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).servicebus.TopicsClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.TopicsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -172,7 +173,7 @@ func testCheckAzureRMServiceBusTopicAuthorizationRuleExists(resourceName string) return fmt.Errorf("Bad: no resource group found in state for ServiceBus Topic: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).servicebus.TopicsClient + conn := testAccProvider.Meta().(*ArmClient).ServiceBus.TopicsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, topicName, name) if err != nil { diff --git a/azurerm/resource_arm_servicebus_topic_test.go b/azurerm/resource_arm_servicebus_topic_test.go index 92f21af78e7f..5ecae3d548b5 100644 --- a/azurerm/resource_arm_servicebus_topic_test.go +++ b/azurerm/resource_arm_servicebus_topic_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMServiceBusTopic_basic(t *testing.T) { }) } func TestAccAzureRMServiceBusTopic_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -280,7 +281,7 @@ func TestAccAzureRMServiceBusTopic_isoTimeSpanAttributes(t *testing.T) { } func testCheckAzureRMServiceBusTopicDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).servicebus.TopicsClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.TopicsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -323,7 +324,7 @@ func testCheckAzureRMServiceBusTopicExists(resourceName string) resource.TestChe return fmt.Errorf("Bad: no resource group found in state for topic: %s", topicName) } - client := testAccProvider.Meta().(*ArmClient).servicebus.TopicsClient + client := testAccProvider.Meta().(*ArmClient).ServiceBus.TopicsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, namespaceName, topicName) diff --git a/azurerm/resource_arm_shared_image.go b/azurerm/resource_arm_shared_image.go index 689d4f7bc6cb..dc185a23a302 100644 --- a/azurerm/resource_arm_shared_image.go +++ b/azurerm/resource_arm_shared_image.go @@ -4,13 +4,15 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -94,13 +96,13 @@ func resourceArmSharedImage() *schema.Resource { Optional: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImagesClient + client := meta.(*ArmClient).compute.GalleryImagesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Shared Image creation.") @@ -116,9 +118,9 @@ func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{} releaseNoteURI := d.Get("release_note_uri").(string) osType := d.Get("os_type").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, galleryName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -144,7 +146,7 @@ func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{} OsType: compute.OperatingSystemTypes(osType), OsState: compute.Generalized, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, galleryName, name, image) @@ -171,10 +173,10 @@ func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{} } func resourceArmSharedImageRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImagesClient + client := meta.(*ArmClient).compute.GalleryImagesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -214,16 +216,14 @@ func resourceArmSharedImageRead(d *schema.ResourceData, meta interface{}) error } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSharedImageDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImagesClient + client := meta.(*ArmClient).compute.GalleryImagesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_shared_image_gallery.go b/azurerm/resource_arm_shared_image_gallery.go index 7db4e02cd1c3..275696fa0fc3 100644 --- a/azurerm/resource_arm_shared_image_gallery.go +++ b/azurerm/resource_arm_shared_image_gallery.go @@ -4,12 +4,14 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "github.com/hashicorp/terraform/helper/schema" "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/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -40,7 +42,7 @@ func resourceArmSharedImageGallery() *schema.Resource { Optional: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), "unique_name": { Type: schema.TypeString, @@ -51,7 +53,7 @@ func resourceArmSharedImageGallery() *schema.Resource { } func resourceArmSharedImageGalleryCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleriesClient + client := meta.(*ArmClient).compute.GalleriesClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Image Gallery creation.") @@ -61,9 +63,9 @@ func resourceArmSharedImageGalleryCreateUpdate(d *schema.ResourceData, meta inte resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) description := d.Get("description").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -81,7 +83,7 @@ func resourceArmSharedImageGalleryCreateUpdate(d *schema.ResourceData, meta inte GalleryProperties: &compute.GalleryProperties{ Description: utils.String(description), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, gallery) @@ -108,10 +110,10 @@ func resourceArmSharedImageGalleryCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmSharedImageGalleryRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleriesClient + client := meta.(*ArmClient).compute.GalleriesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -143,16 +145,14 @@ func resourceArmSharedImageGalleryRead(d *schema.ResourceData, meta interface{}) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSharedImageGalleryDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleriesClient + client := meta.(*ArmClient).compute.GalleriesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_shared_image_gallery_test.go b/azurerm/resource_arm_shared_image_gallery_test.go index b0b07d2c067d..5819877553a7 100644 --- a/azurerm/resource_arm_shared_image_gallery_test.go +++ b/azurerm/resource_arm_shared_image_gallery_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +38,7 @@ func TestAccAzureRMSharedImageGallery_basic(t *testing.T) { }) } func TestAccAzureRMSharedImageGallery_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -93,7 +94,7 @@ func TestAccAzureRMSharedImageGallery_complete(t *testing.T) { } func testCheckAzureRMSharedImageGalleryDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).galleriesClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleriesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -134,7 +135,7 @@ func testCheckAzureRMSharedImageGalleryExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Shared Image Gallery: %s", galleryName) } - client := testAccProvider.Meta().(*ArmClient).galleriesClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleriesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, galleryName) diff --git a/azurerm/resource_arm_shared_image_test.go b/azurerm/resource_arm_shared_image_test.go index 50e44af12ce9..7a18f6b874fd 100644 --- a/azurerm/resource_arm_shared_image_test.go +++ b/azurerm/resource_arm_shared_image_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMSharedImage_basic(t *testing.T) { }) } func TestAccAzureRMSharedImage_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -95,7 +96,7 @@ func TestAccAzureRMSharedImage_complete(t *testing.T) { } func testCheckAzureRMSharedImageDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).galleryImagesClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleryImagesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -138,7 +139,7 @@ func testCheckAzureRMSharedImageExists(resourceName string) resource.TestCheckFu return fmt.Errorf("Bad: no resource group found in state for Shared Image: %s", imageName) } - client := testAccProvider.Meta().(*ArmClient).galleryImagesClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleryImagesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, galleryName, imageName) diff --git a/azurerm/resource_arm_shared_image_version.go b/azurerm/resource_arm_shared_image_version.go index 5ccbd3b8c97f..bc3e3c7742c1 100644 --- a/azurerm/resource_arm_shared_image_version.go +++ b/azurerm/resource_arm_shared_image_version.go @@ -4,12 +4,14 @@ import ( "fmt" "log" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "github.com/hashicorp/terraform/helper/schema" "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/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -81,13 +83,13 @@ func resourceArmSharedImageVersion() *schema.Resource { Default: false, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImageVersionsClient + client := meta.(*ArmClient).compute.GalleryImageVersionsClient ctx := meta.(*ArmClient).StopContext imageVersion := d.Get("name").(string) @@ -98,7 +100,7 @@ func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta inte managedImageId := d.Get("managed_image_id").(string) excludeFromLatest := d.Get("exclude_from_latest").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, galleryName, imageName, imageVersion, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -112,7 +114,7 @@ func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta inte } targetRegions := expandSharedImageVersionTargetRegions(d) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) version := compute.GalleryImageVersion{ Location: utils.String(location), @@ -120,14 +122,14 @@ func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta inte PublishingProfile: &compute.GalleryImageVersionPublishingProfile{ ExcludeFromLatest: utils.Bool(excludeFromLatest), TargetRegions: targetRegions, - Source: &compute.GalleryArtifactSource{ - ManagedImage: &compute.ManagedArtifact{ - ID: utils.String(managedImageId), - }, + }, + StorageProfile: &compute.GalleryImageVersionStorageProfile{ + Source: &compute.GalleryArtifactVersionSource{ + ID: utils.String(managedImageId), }, }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resourceGroup, galleryName, imageName, imageVersion, version) @@ -139,8 +141,6 @@ func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta inte return fmt.Errorf("Error waiting for the creation of Shared Image Version %q (Image %q / Gallery %q / Resource Group %q): %+v", imageVersion, imageName, galleryName, resourceGroup, err) } - // TODO: poll? - read, err := client.Get(ctx, resourceGroup, galleryName, imageName, imageVersion, "") if err != nil { return fmt.Errorf("Error retrieving Shared Image Version %q (Image %q / Gallery %q / Resource Group %q): %+v", imageVersion, imageName, galleryName, resourceGroup, err) @@ -152,10 +152,10 @@ func resourceArmSharedImageVersionCreateUpdate(d *schema.ResourceData, meta inte } func resourceArmSharedImageVersionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImageVersionsClient + client := meta.(*ArmClient).compute.GalleryImageVersionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -192,25 +192,23 @@ func resourceArmSharedImageVersionRead(d *schema.ResourceData, meta interface{}) if err := d.Set("target_region", flattenedRegions); err != nil { return fmt.Errorf("Error setting `target_region`: %+v", err) } + } + if profile := props.StorageProfile; profile != nil { if source := profile.Source; source != nil { - if image := source.ManagedImage; image != nil { - d.Set("managed_image_id", image.ID) - } + d.Set("managed_image_id", source.ID) } } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSharedImageVersionDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).galleryImageVersionsClient + client := meta.(*ArmClient).compute.GalleryImageVersionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_shared_image_version_test.go b/azurerm/resource_arm_shared_image_version_test.go index 9af480b23ea4..b765b74050e6 100644 --- a/azurerm/resource_arm_shared_image_version_test.go +++ b/azurerm/resource_arm_shared_image_version_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -61,7 +62,7 @@ func TestAccAzureRMSharedImageVersion_basic(t *testing.T) { }) } func TestAccAzureRMSharedImageVersion_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -105,7 +106,7 @@ func TestAccAzureRMSharedImageVersion_requiresImport(t *testing.T) { } func testCheckAzureRMSharedImageVersionDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).galleryImageVersionsClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleryImageVersionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -149,7 +150,7 @@ func testCheckAzureRMSharedImageVersionExists(resourceName string) resource.Test return fmt.Errorf("Bad: no resource group found in state for Shared Image Version: %s", imageName) } - client := testAccProvider.Meta().(*ArmClient).galleryImageVersionsClient + client := testAccProvider.Meta().(*ArmClient).compute.GalleryImageVersionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, galleryName, imageName, imageVersion, "") diff --git a/azurerm/resource_arm_signalr_service.go b/azurerm/resource_arm_signalr_service.go index ffae90ae6b27..480e0d45f4d5 100644 --- a/azurerm/resource_arm_signalr_service.go +++ b/azurerm/resource_arm_signalr_service.go @@ -11,6 +11,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -105,13 +107,13 @@ func resourceArmSignalRService() *schema.Resource { Sensitive: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSignalRServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).signalr.Client + client := meta.(*ArmClient).SignalR.Client ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -119,10 +121,10 @@ func resourceArmSignalRServiceCreateUpdate(d *schema.ResourceData, meta interfac resourceGroup := d.Get("resource_group_name").(string) sku := d.Get("sku").([]interface{}) - tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + expandedTags := tags.Expand(t) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -162,10 +164,10 @@ func resourceArmSignalRServiceCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmSignalRServiceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).signalr.Client + client := meta.(*ArmClient).SignalR.Client ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -209,16 +211,14 @@ func resourceArmSignalRServiceRead(d *schema.ResourceData, meta interface{}) err d.Set("secondary_access_key", keys.SecondaryKey) d.Set("secondary_connection_string", keys.SecondaryConnectionString) - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSignalRServiceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).signalr.Client + client := meta.(*ArmClient).SignalR.Client ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_signalr_service_test.go b/azurerm/resource_arm_signalr_service_test.go index f2fef79e75bf..ab1844425d08 100644 --- a/azurerm/resource_arm_signalr_service_test.go +++ b/azurerm/resource_arm_signalr_service_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMSignalRService_basic(t *testing.T) { @@ -44,7 +45,7 @@ func TestAccAzureRMSignalRService_basic(t *testing.T) { }) } func TestAccAzureRMSignalRService_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -398,7 +399,7 @@ resource "azurerm_signalr_service" "test" { } func testCheckAzureRMSignalRServiceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).signalr.Client + conn := testAccProvider.Meta().(*ArmClient).SignalR.Client ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_signalr_service" { @@ -433,7 +434,7 @@ func testCheckAzureRMSignalRServiceExists(resourceName string) resource.TestChec return fmt.Errorf("Bad: no resource group found in state for SignalR service: %s", name) } - conn := testAccProvider.Meta().(*ArmClient).signalr.Client + conn := testAccProvider.Meta().(*ArmClient).SignalR.Client ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_snapshot.go b/azurerm/resource_arm_snapshot.go index ab586faf5338..2a26b99f0680 100644 --- a/azurerm/resource_arm_snapshot.go +++ b/azurerm/resource_arm_snapshot.go @@ -5,11 +5,14 @@ import ( "log" "regexp" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" "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/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -42,7 +45,7 @@ func resourceArmSnapshot() *schema.Resource { string(compute.Copy), string(compute.Import), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "source_uri": { @@ -71,22 +74,22 @@ func resourceArmSnapshot() *schema.Resource { "encryption_settings": encryptionSettingsSchema(), - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSnapshotCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).snapshotsClient + client := meta.(*ArmClient).compute.SnapshotsClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) createOption := d.Get("create_option").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -106,7 +109,7 @@ func resourceArmSnapshotCreateUpdate(d *schema.ResourceData, meta interface{}) e CreateOption: compute.DiskCreateOption(createOption), }, }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if v, ok := d.GetOk("source_uri"); ok { @@ -129,7 +132,7 @@ func resourceArmSnapshotCreateUpdate(d *schema.ResourceData, meta interface{}) e if v, ok := d.GetOk("encryption_settings"); ok { encryptionSettings := v.([]interface{}) settings := encryptionSettings[0].(map[string]interface{}) - properties.EncryptionSettings = expandManagedDiskEncryptionSettings(settings) + properties.EncryptionSettingsCollection = expandManagedDiskEncryptionSettings(settings) } future, err := client.CreateOrUpdate(ctx, resourceGroup, name, properties) @@ -152,10 +155,10 @@ func resourceArmSnapshotCreateUpdate(d *schema.ResourceData, meta interface{}) e } func resourceArmSnapshotRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).snapshotsClient + client := meta.(*ArmClient).compute.SnapshotsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -181,7 +184,6 @@ func resourceArmSnapshotRead(d *schema.ResourceData, meta interface{}) error { } if props := resp.SnapshotProperties; props != nil { - if data := props.CreationData; data != nil { d.Set("create_option", string(data.CreateOption)) @@ -194,21 +196,19 @@ func resourceArmSnapshotRead(d *schema.ResourceData, meta interface{}) error { d.Set("disk_size_gb", int(*props.DiskSizeGB)) } - if props.EncryptionSettings != nil { - d.Set("encryption_settings", flattenManagedDiskEncryptionSettings(props.EncryptionSettings)) + if err := d.Set("encryption_settings", flattenManagedDiskEncryptionSettings(props.EncryptionSettingsCollection)); err != nil { + return fmt.Errorf("Error setting `encryption_settings`: %+v", err) } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSnapshotDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).snapshotsClient + client := meta.(*ArmClient).compute.SnapshotsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_snapshot_test.go b/azurerm/resource_arm_snapshot_test.go index 4661aaa51258..67a1a116954b 100644 --- a/azurerm/resource_arm_snapshot_test.go +++ b/azurerm/resource_arm_snapshot_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -84,7 +85,7 @@ func TestAccAzureRMSnapshot_fromManagedDisk(t *testing.T) { }) } func TestAccAzureRMSnapshot_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -234,7 +235,7 @@ func testCheckAzureRMSnapshotDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - client := testAccProvider.Meta().(*ArmClient).snapshotsClient + client := testAccProvider.Meta().(*ArmClient).compute.SnapshotsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -264,7 +265,7 @@ func testCheckAzureRMSnapshotExists(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for Snapshot: %q", name) } - client := testAccProvider.Meta().(*ArmClient).snapshotsClient + client := testAccProvider.Meta().(*ArmClient).compute.SnapshotsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, name) diff --git a/azurerm/resource_arm_sql_administrator.go b/azurerm/resource_arm_sql_administrator.go index 2499e8ac0425..ac0ace4f58f5 100644 --- a/azurerm/resource_arm_sql_administrator.go +++ b/azurerm/resource_arm_sql_administrator.go @@ -9,6 +9,8 @@ import ( uuid "github.com/satori/go.uuid" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -39,20 +41,20 @@ func resourceArmSqlAdministrator() *schema.Resource { "object_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, "tenant_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validateUUID, + ValidateFunc: validate.UUID, }, }, } } func resourceArmSqlActiveDirectoryAdministratorCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + client := meta.(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := meta.(*ArmClient).StopContext serverName := d.Get("server_name").(string) @@ -61,7 +63,7 @@ func resourceArmSqlActiveDirectoryAdministratorCreateUpdate(d *schema.ResourceDa objectId := uuid.FromStringOrNil(d.Get("object_id").(string)) tenantId := uuid.FromStringOrNil(d.Get("tenant_id").(string)) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, serverName) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -103,10 +105,10 @@ func resourceArmSqlActiveDirectoryAdministratorCreateUpdate(d *schema.ResourceDa } func resourceArmSqlActiveDirectoryAdministratorRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + client := meta.(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -135,10 +137,10 @@ func resourceArmSqlActiveDirectoryAdministratorRead(d *schema.ResourceData, meta } func resourceArmSqlActiveDirectoryAdministratorDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + client := meta.(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_sql_administrator_test.go b/azurerm/resource_arm_sql_administrator_test.go index 9ee58735a84f..e76df497b3a3 100644 --- a/azurerm/resource_arm_sql_administrator_test.go +++ b/azurerm/resource_arm_sql_administrator_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -42,7 +43,7 @@ func TestAccAzureRMSqlAdministrator_basic(t *testing.T) { }) } func TestAccAzureRMSqlAdministrator_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -101,7 +102,7 @@ func testCheckAzureRMSqlAdministratorExists(resourceName string) resource.TestCh resourceGroup := rs.Primary.Attributes["resource_group_name"] serverName := rs.Primary.Attributes["server_name"] - client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext _, err := client.Get(ctx, resourceGroup, serverName) @@ -119,7 +120,7 @@ func testCheckAzureRMSqlAdministratorDisappears(resourceName string) resource.Te resourceGroup := rs.Primary.Attributes["resource_group_name"] serverName := rs.Primary.Attributes["server_name"] - client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, resourceGroup, serverName); err != nil { @@ -139,7 +140,7 @@ func testCheckAzureRMSqlAdministratorDestroy(s *terraform.State) error { resourceGroup := rs.Primary.Attributes["resource_group_name"] serverName := rs.Primary.Attributes["server_name"] - client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ServerAzureADAdministratorsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName) diff --git a/azurerm/resource_arm_sql_database.go b/azurerm/resource_arm_sql_database.go index 625857acc9fa..320bdbca1c1c 100644 --- a/azurerm/resource_arm_sql_database.go +++ b/azurerm/resource_arm_sql_database.go @@ -9,6 +9,8 @@ import ( uuid "github.com/satori/go.uuid" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" "github.com/Azure/go-autorest/autorest/date" @@ -141,9 +143,19 @@ func resourceArmSqlDatabase() *schema.Resource { DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(sql.Basic), - string(sql.Standard), - string(sql.Premium), + string(sql.Business), + string(sql.BusinessCritical), string(sql.DataWarehouse), + string(sql.Free), + string(sql.GeneralPurpose), + string(sql.Hyperscale), + string(sql.Premium), + string(sql.PremiumRS), + string(sql.Standard), + string(sql.Stretch), + string(sql.System), + string(sql.System2), + string(sql.Web), }, true), }, @@ -298,11 +310,10 @@ func resourceArmSqlDatabase() *schema.Resource { Default: false, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { - threatDetection, hasThreatDetection := diff.GetOk("threat_detection_policy") if hasThreatDetection { if tl := threatDetection.([]interface{}); len(tl) > 0 { @@ -323,7 +334,7 @@ func resourceArmSqlDatabase() *schema.Resource { } func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlDatabasesClient + client := meta.(*ArmClient).Sql.DatabasesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -331,9 +342,9 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{} resourceGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) createMode := d.Get("create_mode").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -356,7 +367,7 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{} DatabaseProperties: &sql.DatabaseProperties{ CreateMode: sql.CreateMode(createMode), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), } if v, ok := d.GetOk("source_database_id"); ok { @@ -472,7 +483,7 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{} d.SetId(*resp.ID) - threatDetectionClient := meta.(*ArmClient).sqlDatabaseThreatDetectionPoliciesClient + threatDetectionClient := meta.(*ArmClient).Sql.DatabaseThreatDetectionPoliciesClient if _, err = threatDetectionClient.CreateOrUpdate(ctx, resourceGroup, serverName, name, *threatDetection); err != nil { return fmt.Errorf("Error setting database threat detection policy: %+v", err) } @@ -481,10 +492,10 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{} } func resourceArmSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlDatabasesClient + client := meta.(*ArmClient).Sql.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -504,7 +515,7 @@ func resourceArmSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error making Read request on Sql Database %s: %+v", name, err) } - threatDetectionClient := meta.(*ArmClient).sqlDatabaseThreatDetectionPoliciesClient + threatDetectionClient := meta.(*ArmClient).Sql.DatabaseThreatDetectionPoliciesClient threatDetection, err := threatDetectionClient.Get(ctx, resourceGroup, serverName, name) if err == nil { flattenedThreatDetection := flattenArmSqlServerThreatDetectionPolicy(d, threatDetection) @@ -558,16 +569,14 @@ func resourceArmSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSqlDatabaseDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlDatabasesClient + client := meta.(*ArmClient).Sql.DatabasesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -602,7 +611,6 @@ func flattenEncryptionStatus(encryption *[]sql.TransparentDataEncryption) string } func flattenArmSqlServerThreatDetectionPolicy(d *schema.ResourceData, policy sql.DatabaseSecurityAlertPolicy) []interface{} { - // The SQL database threat detection API always returns the default value even if never set. // If the values are on their default one, threat it as not set. properties := policy.DatabaseSecurityAlertPolicyProperties diff --git a/azurerm/resource_arm_sql_database_test.go b/azurerm/resource_arm_sql_database_test.go index 04894179a658..0b9fbb7db184 100644 --- a/azurerm/resource_arm_sql_database_test.go +++ b/azurerm/resource_arm_sql_database_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -36,7 +37,7 @@ func TestAccAzureRMSqlDatabase_basic(t *testing.T) { }) } func TestAccAzureRMSqlDatabase_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -330,7 +331,6 @@ func TestAccAzureRMSqlDatabase_readScale(t *testing.T) { func testCheckAzureRMSqlDatabaseExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) @@ -340,7 +340,7 @@ func testCheckAzureRMSqlDatabaseExists(resourceName string) resource.TestCheckFu serverName := rs.Primary.Attributes["server_name"] databaseName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).Sql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, databaseName, "") @@ -366,7 +366,7 @@ func testCheckAzureRMSqlDatabaseDestroy(s *terraform.State) error { serverName := rs.Primary.Attributes["server_name"] databaseName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).Sql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, databaseName, "") @@ -396,7 +396,7 @@ func testCheckAzureRMSqlDatabaseDisappears(resourceName string) resource.TestChe serverName := rs.Primary.Attributes["server_name"] databaseName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlDatabasesClient + client := testAccProvider.Meta().(*ArmClient).Sql.DatabasesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, resourceGroup, serverName, databaseName); err != nil { diff --git a/azurerm/resource_arm_sql_elasticpool.go b/azurerm/resource_arm_sql_elasticpool.go index c45556180f30..c014f20b5ec7 100644 --- a/azurerm/resource_arm_sql_elasticpool.go +++ b/azurerm/resource_arm_sql_elasticpool.go @@ -7,6 +7,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" "github.com/hashicorp/terraform/helper/schema" @@ -78,13 +80,13 @@ func resourceArmSqlElasticPool() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlElasticPoolsClient + client := meta.(*ArmClient).Sql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for SQL ElasticPool creation.") @@ -93,9 +95,9 @@ func resourceArmSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interfac serverName := d.Get("server_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) resGroup := d.Get("resource_group_name").(string) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -112,7 +114,7 @@ func resourceArmSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interfac Name: &name, Location: &location, ElasticPoolProperties: getArmSqlElasticPoolProperties(d), - Tags: expandTags(tags), + Tags: tags.Expand(t), } future, err := client.CreateOrUpdate(ctx, resGroup, serverName, name, elasticPool) @@ -138,7 +140,7 @@ func resourceArmSqlElasticPoolCreateUpdate(d *schema.ResourceData, meta interfac } func resourceArmSqlElasticPoolRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlElasticPoolsClient + client := meta.(*ArmClient).Sql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext resGroup, serverName, name, err := parseArmSqlElasticPoolId(d.Id()) @@ -174,13 +176,11 @@ func resourceArmSqlElasticPoolRead(d *schema.ResourceData, meta interface{}) err } } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSqlElasticPoolDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlElasticPoolsClient + client := meta.(*ArmClient).Sql.ElasticPoolsClient ctx := meta.(*ArmClient).StopContext resGroup, serverName, name, err := parseArmSqlElasticPoolId(d.Id()) @@ -221,7 +221,7 @@ func getArmSqlElasticPoolProperties(d *schema.ResourceData) *sql.ElasticPoolProp } func parseArmSqlElasticPoolId(sqlElasticPoolId string) (string, string, string, error) { - id, err := parseAzureResourceID(sqlElasticPoolId) + id, err := azure.ParseAzureResourceID(sqlElasticPoolId) if err != nil { return "", "", "", fmt.Errorf("[ERROR] Unable to parse SQL ElasticPool ID %q: %+v", sqlElasticPoolId, err) } diff --git a/azurerm/resource_arm_sql_elasticpool_test.go b/azurerm/resource_arm_sql_elasticpool_test.go index a6d3bfb8a6d3..84532b3e7862 100644 --- a/azurerm/resource_arm_sql_elasticpool_test.go +++ b/azurerm/resource_arm_sql_elasticpool_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMSqlElasticPool_basic(t *testing.T) { @@ -34,7 +35,7 @@ func TestAccAzureRMSqlElasticPool_basic(t *testing.T) { }) } func TestAccAzureRMSqlElasticPool_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -124,7 +125,7 @@ func testCheckAzureRMSqlElasticPoolExists(resourceName string) resource.TestChec serverName := rs.Primary.Attributes["server_name"] poolName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, poolName) @@ -141,7 +142,7 @@ func testCheckAzureRMSqlElasticPoolExists(resourceName string) resource.TestChec } func testCheckAzureRMSqlElasticPoolDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*ArmClient).sqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -179,7 +180,7 @@ func testCheckAzureRMSqlElasticPoolDisappears(resourceName string) resource.Test serverName := rs.Primary.Attributes["server_name"] poolName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlElasticPoolsClient + client := testAccProvider.Meta().(*ArmClient).Sql.ElasticPoolsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext if _, err := client.Delete(ctx, resourceGroup, serverName, poolName); err != nil { diff --git a/azurerm/resource_arm_sql_failover_group.go b/azurerm/resource_arm_sql_failover_group.go new file mode 100644 index 000000000000..2c9b85ce7ec8 --- /dev/null +++ b/azurerm/resource_arm_sql_failover_group.go @@ -0,0 +1,361 @@ +package azurerm + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" + "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/set" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func resourceArmSqlFailoverGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceArmSqlFailoverGroupCreateUpdate, + Read: resourceArmSqlFailoverGroupRead, + Update: resourceArmSqlFailoverGroupCreateUpdate, + Delete: resourceArmSqlFailoverGroupDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateMsSqlFailoverGroupName, + }, + + "location": azure.SchemaLocationForDataSource(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateMsSqlServerName, + }, + + "databases": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, + + "partner_servers": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "location": azure.SchemaLocationForDataSource(), + + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "readonly_endpoint_failover_policy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sql.ReadOnlyEndpointFailoverPolicyDisabled), + string(sql.ReadOnlyEndpointFailoverPolicyEnabled), + }, false), + }, + }, + }, + }, + + "read_write_endpoint_failover_policy": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sql.Automatic), + string(sql.Manual), + }, false), + }, + "grace_minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + + "role": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmSqlFailoverGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Sql.FailoverGroupsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + serverName := d.Get("server_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing SQL Failover Group %q (Resource Group %q, Server %q): %+v", name, resourceGroup, serverName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_sql_failover_group", *existing.ID) + } + } + + t := d.Get("tags").(map[string]interface{}) + + properties := sql.FailoverGroup{ + FailoverGroupProperties: &sql.FailoverGroupProperties{ + ReadOnlyEndpoint: expandSqlFailoverGroupReadOnlyPolicy(d), + ReadWriteEndpoint: expandSqlFailoverGroupReadWritePolicy(d), + PartnerServers: expandSqlFailoverGroupPartnerServers(d), + }, + Tags: tags.Expand(t), + } + + if r, ok := d.Get("databases").(*schema.Set); ok && r.Len() > 0 { + var databases []string + for _, v := range r.List() { + s := v.(string) + databases = append(databases, s) + } + + properties.Databases = &databases + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, serverName, name, properties) + if err != nil { + return fmt.Errorf("Error issuing create/update request for SQL Failover Group %q (Resource Group %q, Server %q): %+v", name, resourceGroup, serverName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting on create/update future for SQL Failover Group %q (Resource Group %q, Server %q): %+v", name, resourceGroup, serverName, err) + } + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error issuing get request for SQL Failover Group %q (Resource Group %q, Server %q): %+v", name, resourceGroup, serverName, err) + } + + d.SetId(*resp.ID) + + return resourceArmSqlFailoverGroupRead(d, meta) +} + +func resourceArmSqlFailoverGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Sql.FailoverGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["failoverGroups"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request for SQL Failover Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if props := resp.FailoverGroupProperties; props != nil { + if err := d.Set("read_write_endpoint_failover_policy", flattenSqlFailoverGroupReadWritePolicy(props.ReadWriteEndpoint)); err != nil { + return fmt.Errorf("Error setting `read_write_endpoint_failover_policy`: %+v", err) + } + + if err := d.Set("readonly_endpoint_failover_policy", flattenSqlFailoverGroupReadOnlyPolicy(props.ReadOnlyEndpoint)); err != nil { + return fmt.Errorf("Error setting `read_only_endpoint_failover_policy`: %+v", err) + } + + if props.Databases != nil { + d.Set("databases", set.FromStringSlice(*props.Databases)) + } + d.Set("role", string(props.ReplicationRole)) + + if err := d.Set("partner_servers", flattenSqlFailoverGroupPartnerServers(props.PartnerServers)); err != nil { + return fmt.Errorf("Error setting `partner_servers`: %+v", err) + } + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmSqlFailoverGroupDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Sql.FailoverGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["failoverGroups"] + + future, err := client.Delete(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error deleting SQL Failover Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error deleting SQL Failover Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + return err +} + +func expandSqlFailoverGroupReadWritePolicy(d *schema.ResourceData) *sql.FailoverGroupReadWriteEndpoint { + vs := d.Get("read_write_endpoint_failover_policy").([]interface{}) + v := vs[0].(map[string]interface{}) + + mode := sql.ReadWriteEndpointFailoverPolicy(v["mode"].(string)) + graceMins := int32(v["grace_minutes"].(int)) + + policy := &sql.FailoverGroupReadWriteEndpoint{ + FailoverPolicy: mode, + } + + if mode != sql.Manual { + policy.FailoverWithDataLossGracePeriodMinutes = utils.Int32(graceMins) + } + + return policy +} + +func flattenSqlFailoverGroupReadWritePolicy(input *sql.FailoverGroupReadWriteEndpoint) []interface{} { + if input == nil { + return []interface{}{} + } + + policy := make(map[string]interface{}) + + policy["mode"] = string(input.FailoverPolicy) + + if input.FailoverWithDataLossGracePeriodMinutes != nil { + policy["grace_minutes"] = *input.FailoverWithDataLossGracePeriodMinutes + } + return []interface{}{policy} +} + +func expandSqlFailoverGroupReadOnlyPolicy(d *schema.ResourceData) *sql.FailoverGroupReadOnlyEndpoint { + vs := d.Get("readonly_endpoint_failover_policy").([]interface{}) + if len(vs) == 0 { + return nil + } + + v := vs[0].(map[string]interface{}) + mode := sql.ReadOnlyEndpointFailoverPolicy(v["mode"].(string)) + + return &sql.FailoverGroupReadOnlyEndpoint{ + FailoverPolicy: mode, + } +} + +func flattenSqlFailoverGroupReadOnlyPolicy(input *sql.FailoverGroupReadOnlyEndpoint) []interface{} { + if input == nil { + return []interface{}{} + } + + policy := make(map[string]interface{}) + policy["mode"] = string(input.FailoverPolicy) + + return []interface{}{policy} +} + +func expandSqlFailoverGroupPartnerServers(d *schema.ResourceData) *[]sql.PartnerInfo { + servers := d.Get("partner_servers").([]interface{}) + partners := make([]sql.PartnerInfo, 0) + + for _, server := range servers { + info := server.(map[string]interface{}) + + id := info["id"].(string) + partners = append(partners, sql.PartnerInfo{ + ID: &id, + }) + } + + return &partners +} + +func flattenSqlFailoverGroupPartnerServers(input *[]sql.PartnerInfo) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + + if input != nil { + for _, server := range *input { + info := make(map[string]interface{}) + + if v := server.ID; v != nil { + info["id"] = *v + } + if v := server.Location; v != nil { + info["location"] = *v + } + info["role"] = string(server.ReplicationRole) + + result = append(result, info) + } + } + return result +} diff --git a/azurerm/resource_arm_sql_failover_group_test.go b/azurerm/resource_arm_sql_failover_group_test.go new file mode 100644 index 000000000000..aa59307e0858 --- /dev/null +++ b/azurerm/resource_arm_sql_failover_group_test.go @@ -0,0 +1,386 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMSqlFailoverGroup_basic(t *testing.T) { + resourceName := "azurerm_sql_failover_group.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlFailoverGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSqlFailoverGroup_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlFailoverGroupExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMSqlFailoverGroup_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skiiping since resources aren't required to be imported") + return + } + resourceName := "azurerm_sql_failover_group.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlFailoverGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSqlFailoverGroup_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlFailoverGroupExists(resourceName), + ), + }, + { + Config: testAccAzureRMSqlFailoverGroup_requiresImport(ri, testLocation(), testAltLocation()), + ExpectError: testRequiresImportError("azurerm_sql_failover_group"), + }, + }, + }) +} + +func TestAccAzureRMSqlFailoverGroup_disappears(t *testing.T) { + resourceName := "azurerm_sql_failover_group.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlFailoverGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSqlFailoverGroup_basic(ri, testLocation(), testAltLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlFailoverGroupExists(resourceName), + testCheckAzureRMSqlFailoverGroupDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMSqlFailoverGroup_withTags(t *testing.T) { + resourceName := "azurerm_sql_failover_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + altLocation := testAltLocation() + preConfig := testAccAzureRMSqlFailoverGroup_withTags(ri, location, altLocation) + postConfig := testAccAzureRMSqlFailoverGroup_withTagsUpdate(ri, location, altLocation) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlFailoverGroupDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlFailoverGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlFailoverGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + ), + }, + }, + }) +} + +func testCheckAzureRMSqlFailoverGroupExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + name := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).Sql.FailoverGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("SQL Failover Group %q (server %q / resource group %q) was not found", name, serverName, resourceGroup) + } + + return err + } + + return nil + } +} + +func testCheckAzureRMSqlFailoverGroupDisappears(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) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + name := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).Sql.FailoverGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + future, err := client.Delete(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Bad: Delete on sqlFailoverGroupsClient: %+v", err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error deleting SQL Failover Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + return nil + } +} + +func testCheckAzureRMSqlFailoverGroupDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_sql_failover_group" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + + client := testAccProvider.Meta().(*ArmClient).Sql.FailoverGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("SQL Failover Group %q (server %q / resource group %q) still exists: %+v", name, serverName, resourceGroup, resp) + } + + return nil +} + +func testAccAzureRMSqlFailoverGroup_basic(rInt int, location, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_sql_server" "test_primary" { + name = "acctestmssql%[1]d-primary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_server" "test_secondary" { + name = "acctestmssql%[1]d-secondary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "%[3]s" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_database" "test" { + name = "acctestdb%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + location = "${azurerm_resource_group.test.location}" + edition = "Standard" + collation = "SQL_Latin1_General_CP1_CI_AS" + max_size_bytes = "1073741824" + requested_service_objective_name = "S0" +} + +resource "azurerm_sql_failover_group" "test" { + name = "acctestsfg%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + databases = ["${azurerm_sql_database.test.id}"] + + partner_servers { + id = "${azurerm_sql_server.test_secondary.id}" + } + + read_write_endpoint_failover_policy { + mode = "Automatic" + grace_minutes = 60 + } +} +`, rInt, location, altlocation) +} + +func testAccAzureRMSqlFailoverGroup_requiresImport(rInt int, location, altlocation string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_sql_failover_group" "import" { + name = "${azurerm_sql_failover_group.test.name}" + resource_group_name = "${azurerm_sql_failover_group.test.resource_group_name}" + server_name = "${azurerm_sql_failover_group.test.server_name}" + databases = "${azurerm_sql_failover_group.test.databases}" + + partner_servers { + id = "${azurerm_sql_failover_group.test.partner_servers.0.id}" + } + + read_write_endpoint_failover_policy { + mode = "${azurerm_sql_failover_group.test.read_write_endpoint_failover_policy.0.mode}" + grace_minutes = "${azurerm_sql_failover_group.test.read_write_endpoint_failover_policy.0.grace_minutes}" + } + } +`, testAccAzureRMSqlFailoverGroup_basic(rInt, location, altlocation)) +} + +func testAccAzureRMSqlFailoverGroup_withTags(rInt int, location, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_sql_server" "test_primary" { + name = "acctestmssql%[1]d-primary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_server" "test_secondary" { + name = "acctestmssql%[1]d-secondary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "%[3]s" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_database" "test" { + name = "acctestdb%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + location = "${azurerm_resource_group.test.location}" + edition = "Standard" + collation = "SQL_Latin1_General_CP1_CI_AS" + max_size_bytes = "1073741824" + requested_service_objective_name = "S0" +} + +resource "azurerm_sql_failover_group" "test" { + name = "acctestsfg%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + databases = ["${azurerm_sql_database.test.id}"] + + partner_servers { + id = "${azurerm_sql_server.test_secondary.id}" + } + read_write_endpoint_failover_policy { + mode = "Automatic" + grace_minutes = 60 + } + tags = { + environment = "staging" + database = "test" + } +} +`, rInt, location, altlocation) +} + +func testAccAzureRMSqlFailoverGroup_withTagsUpdate(rInt int, location, altlocation string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_sql_server" "test_primary" { + name = "acctestmssql%[1]d-primary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_server" "test_secondary" { + name = "acctestmssql%[1]d-secondary" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "%[3]s" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_database" "test" { + name = "acctestdb%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + location = "${azurerm_resource_group.test.location}" + edition = "Standard" + collation = "SQL_Latin1_General_CP1_CI_AS" + max_size_bytes = "1073741824" + requested_service_objective_name = "S0" +} + +resource "azurerm_sql_failover_group" "test" { + name = "acctestsfg%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_sql_server.test_primary.name}" + databases = ["${azurerm_sql_database.test.id}"] + + partner_servers { + id = "${azurerm_sql_server.test_secondary.id}" + } + read_write_endpoint_failover_policy { + mode = "Automatic" + grace_minutes = 60 + } + tags = { + environment = "production" + } +} +`, rInt, location, altlocation) +} diff --git a/azurerm/resource_arm_sql_firewall_rule.go b/azurerm/resource_arm_sql_firewall_rule.go index 51c29700a4bb..048fae1295f1 100644 --- a/azurerm/resource_arm_sql_firewall_rule.go +++ b/azurerm/resource_arm_sql_firewall_rule.go @@ -6,6 +6,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" "github.com/hashicorp/terraform/helper/schema" @@ -55,7 +56,7 @@ func resourceArmSqlFirewallRule() *schema.Resource { } func resourceArmSqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlFirewallRulesClient + client := meta.(*ArmClient).Sql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -64,7 +65,7 @@ func resourceArmSqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta interfa startIPAddress := d.Get("start_ip_address").(string) endIPAddress := d.Get("end_ip_address").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -99,10 +100,10 @@ func resourceArmSqlFirewallRuleCreateUpdate(d *schema.ResourceData, meta interfa } func resourceArmSqlFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlFirewallRulesClient + client := meta.(*ArmClient).Sql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -132,10 +133,10 @@ func resourceArmSqlFirewallRuleRead(d *schema.ResourceData, meta interface{}) er } func resourceArmSqlFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlFirewallRulesClient + client := meta.(*ArmClient).Sql.FirewallRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_sql_firewall_rule_test.go b/azurerm/resource_arm_sql_firewall_rule_test.go index 3e5370be9206..590f11e89161 100644 --- a/azurerm/resource_arm_sql_firewall_rule_test.go +++ b/azurerm/resource_arm_sql_firewall_rule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -44,7 +45,7 @@ func TestAccAzureRMSqlFirewallRule_basic(t *testing.T) { }) } func TestAccAzureRMSqlFirewallRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -104,7 +105,7 @@ func testCheckAzureRMSqlFirewallRuleExists(resourceName string) resource.TestChe serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlFirewallRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.FirewallRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -130,7 +131,7 @@ func testCheckAzureRMSqlFirewallRuleDestroy(s *terraform.State) error { serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlFirewallRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.FirewallRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -160,7 +161,7 @@ func testCheckAzureRMSqlFirewallRuleDisappears(resourceName string) resource.Tes serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlFirewallRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.FirewallRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Delete(ctx, resourceGroup, serverName, ruleName) diff --git a/azurerm/resource_arm_sql_server.go b/azurerm/resource_arm_sql_server.go index fcc75df9f4dc..20a363ca5bb8 100644 --- a/azurerm/resource_arm_sql_server.go +++ b/azurerm/resource_arm_sql_server.go @@ -10,6 +10,8 @@ import ( "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -41,8 +43,8 @@ func resourceArmSqlServer() *schema.Resource { Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string("2.0"), - string("12.0"), + "2.0", + "12.0", }, true), }, @@ -63,13 +65,13 @@ func resourceArmSqlServer() *schema.Resource { Computed: true, }, - "tags": tagsSchema(), + "tags": tags.Schema(), }, } } func resourceArmSqlServerCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServersClient + client := meta.(*ArmClient).Sql.ServersClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -78,10 +80,10 @@ func resourceArmSqlServerCreateUpdate(d *schema.ResourceData, meta interface{}) adminUsername := d.Get("administrator_login").(string) version := d.Get("version").(string) - tags := d.Get("tags").(map[string]interface{}) - metadata := expandTags(tags) + t := d.Get("tags").(map[string]interface{}) + metadata := tags.Expand(t) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -132,10 +134,10 @@ func resourceArmSqlServerCreateUpdate(d *schema.ResourceData, meta interface{}) } func resourceArmSqlServerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServersClient + client := meta.(*ArmClient).Sql.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -166,16 +168,14 @@ func resourceArmSqlServerRead(d *schema.ResourceData, meta interface{}) error { d.Set("fully_qualified_domain_name", serverProperties.FullyQualifiedDomainName) } - flattenAndSetTags(d, resp.Tags) - - return nil + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmSqlServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlServersClient + client := meta.(*ArmClient).Sql.ServersClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_sql_server_test.go b/azurerm/resource_arm_sql_server_test.go index 6de4e4c90113..a12131a7e08a 100644 --- a/azurerm/resource_arm_sql_server_test.go +++ b/azurerm/resource_arm_sql_server_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -35,7 +36,7 @@ func TestAccAzureRMSqlServer_basic(t *testing.T) { }) } func TestAccAzureRMSqlServer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -133,7 +134,7 @@ func testCheckAzureRMSqlServerExists(resourceName string) resource.TestCheckFunc return fmt.Errorf("Bad: no resource group found in state for SQL Server: %s", sqlServerName) } - conn := testAccProvider.Meta().(*ArmClient).sqlServersClient + conn := testAccProvider.Meta().(*ArmClient).Sql.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, sqlServerName) if err != nil { @@ -148,7 +149,7 @@ func testCheckAzureRMSqlServerExists(resourceName string) resource.TestCheckFunc } func testCheckAzureRMSqlServerDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).sqlServersClient + conn := testAccProvider.Meta().(*ArmClient).Sql.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext for _, rs := range s.RootModule().Resources { @@ -170,7 +171,6 @@ func testCheckAzureRMSqlServerDestroy(s *terraform.State) error { } return fmt.Errorf("SQL Server %s still exists", sqlServerName) - } return nil @@ -187,7 +187,7 @@ func testCheckAzureRMSqlServerDisappears(resourceName string) resource.TestCheck resourceGroup := rs.Primary.Attributes["resource_group_name"] serverName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlServersClient + client := testAccProvider.Meta().(*ArmClient).Sql.ServersClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, serverName) diff --git a/azurerm/resource_arm_sql_virtual_network_rule.go b/azurerm/resource_arm_sql_virtual_network_rule.go index 57a41681a844..bf4fb18e6728 100644 --- a/azurerm/resource_arm_sql_virtual_network_rule.go +++ b/azurerm/resource_arm_sql_virtual_network_rule.go @@ -9,6 +9,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" "github.com/hashicorp/terraform/helper/resource" @@ -60,7 +61,7 @@ func resourceArmSqlVirtualNetworkRule() *schema.Resource { } func resourceArmSqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlVirtualNetworkRulesClient + client := meta.(*ArmClient).Sql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext name := d.Get("name").(string) @@ -69,7 +70,7 @@ func resourceArmSqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta i virtualNetworkSubnetId := d.Get("subnet_id").(string) ignoreMissingVnetServiceEndpoint := d.Get("ignore_missing_vnet_service_endpoint").(bool) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, serverName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -119,10 +120,10 @@ func resourceArmSqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta i } func resourceArmSqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlVirtualNetworkRulesClient + client := meta.(*ArmClient).Sql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -155,10 +156,10 @@ func resourceArmSqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface } func resourceArmSqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).sqlVirtualNetworkRulesClient + client := meta.(*ArmClient).Sql.VirtualNetworkRulesClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -241,7 +242,7 @@ func validateSqlVirtualNetworkRuleName(v interface{}, k string) (warnings []stri * Ready * ResponseNotFound (Custom state in case of 404) */ -func sqlVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client sql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { +func sqlVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *sql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.Get(ctx, resourceGroup, serverName, name) diff --git a/azurerm/resource_arm_sql_virtual_network_rule_test.go b/azurerm/resource_arm_sql_virtual_network_rule_test.go index 444604b334e1..0575fef25271 100644 --- a/azurerm/resource_arm_sql_virtual_network_rule_test.go +++ b/azurerm/resource_arm_sql_virtual_network_rule_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -51,7 +52,7 @@ func TestAccAzureRMSqlVirtualNetworkRule_basic(t *testing.T) { } func TestAccAzureRMSqlVirtualNetworkRule_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -358,7 +359,7 @@ func testCheckAzureRMSqlVirtualNetworkRuleExists(resourceName string) resource.T serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -387,7 +388,7 @@ func testCheckAzureRMSqlVirtualNetworkRuleDestroy(s *terraform.State) error { serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) @@ -420,7 +421,7 @@ func testCheckAzureRMSqlVirtualNetworkRuleDisappears(resourceName string) resour serverName := rs.Primary.Attributes["server_name"] ruleName := rs.Primary.Attributes["name"] - client := testAccProvider.Meta().(*ArmClient).sqlVirtualNetworkRulesClient + client := testAccProvider.Meta().(*ArmClient).Sql.VirtualNetworkRulesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext future, err := client.Delete(ctx, resourceGroup, serverName, ruleName) diff --git a/azurerm/resource_arm_storage_account.go b/azurerm/resource_arm_storage_account.go index 6cdc28777c37..64b3adb7a687 100644 --- a/azurerm/resource_arm_storage_account.go +++ b/azurerm/resource_arm_storage_account.go @@ -3,17 +3,26 @@ package azurerm import ( "fmt" "log" + "net/http" "regexp" "strings" + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v1.0/security" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + azautorest "github.com/Azure/go-autorest/autorest" "github.com/hashicorp/go-getter/helper/url" "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/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues" ) const blobStorageAccountDefaultAccessTier = "Hot" @@ -25,11 +34,12 @@ func resourceArmStorageAccount() *schema.Resource { Update: resourceArmStorageAccountUpdate, Delete: resourceArmStorageAccountDelete, + MigrateState: resourceStorageAccountMigrateState, + SchemaVersion: 2, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - MigrateState: resourceStorageAccountMigrateState, - SchemaVersion: 2, Schema: map[string]*schema.Schema{ "name": { @@ -50,6 +60,8 @@ func resourceArmStorageAccount() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(storage.Storage), string(storage.BlobStorage), + string(storage.BlockBlobStorage), + string(storage.FileStorage), string(storage.StorageV2), }, true), Default: string(storage.Storage), @@ -61,7 +73,7 @@ func resourceArmStorageAccount() *schema.Resource { Computed: true, Deprecated: "This field has been split into `account_tier` and `account_replication_type`", ValidateFunc: validateArmStorageAccountType, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "account_tier": { @@ -72,7 +84,7 @@ func resourceArmStorageAccount() *schema.Resource { "Standard", "Premium", }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "account_replication_type": { @@ -84,7 +96,7 @@ func resourceArmStorageAccount() *schema.Resource { "GRS", "RAGRS", }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, // Only valid for BlobStorage & StorageV2 accounts, defaults to "Hot" in create function @@ -106,7 +118,7 @@ func resourceArmStorageAccount() *schema.Resource { string(storage.MicrosoftKeyvault), string(storage.MicrosoftStorage), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "custom_domain": { @@ -153,10 +165,17 @@ func resourceArmStorageAccount() *schema.Resource { ForceNew: true, }, + "enable_advanced_threat_protection": { + Type: schema.TypeBool, + Optional: true, + Default: false, // TODO remove this in 2.0 so we can use GetOkExists to guard the ATP client use + }, + "network_rules": { Type: schema.TypeList, - MaxItems: 1, Optional: true, + Computed: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "bypass": { @@ -174,18 +193,224 @@ func resourceArmStorageAccount() *schema.Resource { }, Set: schema.HashString, }, + "ip_rules": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, + "virtual_network_subnet_ids": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, + + "default_action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(storage.DefaultActionAllow), + string(storage.DefaultActionDeny), + }, false), + }, + }, + }, + }, + + "identity": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.StringInSlice([]string{ + "SystemAssigned", + }, true), + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "tags": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ValidateFunc: validateAzureRMStorageAccountTags, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "queue_properties": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cors_rule": { + Type: schema.TypeList, + Optional: true, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_origins": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "exposed_headers": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "allowed_headers": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + "allowed_methods": { + Type: schema.TypeList, + Required: true, + MaxItems: 64, + Elem: &schema.Schema{ + Type: schema.TypeString, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "DELETE", + "GET", + "HEAD", + "MERGE", + "POST", + "OPTIONS", + "PUT"}, false), + }, + }, + }, + "max_age_in_seconds": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 2000000000), + }, + }, + }, + }, + "logging": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "delete": { + Type: schema.TypeBool, + Required: true, + }, + "read": { + Type: schema.TypeBool, + Required: true, + }, + "write": { + Type: schema.TypeBool, + Required: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, + "hour_metrics": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "include_apis": { + Type: schema.TypeBool, + Optional: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, + "minute_metrics": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "include_apis": { + Type: schema.TypeBool, + Optional: true, + }, + "retention_policy_days": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 365), + }, + }, + }, + }, }, }, }, @@ -355,40 +580,6 @@ func resourceArmStorageAccount() *schema.Resource { Computed: true, Sensitive: true, }, - - "identity": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, - ValidateFunc: validation.StringInSlice([]string{ - "SystemAssigned", - }, true), - }, - "principal_id": { - Type: schema.TypeString, - Computed: true, - }, - "tenant_id": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - - "tags": { - Type: schema.TypeMap, - Optional: true, - Computed: true, - ValidateFunc: validateAzureRMStorageAccountTags, - }, }, } } @@ -397,7 +588,7 @@ func validateAzureRMStorageAccountTags(v interface{}, _ string) (warnings []stri tagsMap := v.(map[string]interface{}) if len(tagsMap) > 15 { - errors = append(errors, fmt.Errorf("a maximum of 15 tags can be applied to each ARM resource")) + errors = append(errors, fmt.Errorf("a maximum of 15 tags can be applied to storage account ARM resource")) } for k, v := range tagsMap { @@ -405,7 +596,7 @@ func validateAzureRMStorageAccountTags(v interface{}, _ string) (warnings []stri errors = append(errors, fmt.Errorf("the maximum length for a tag key is 128 characters: %q is %d characters", k, len(k))) } - value, err := tagValueToString(v) + value, err := tags.TagValueToString(v) if err != nil { errors = append(errors, err) } else if len(value) > 256 { @@ -418,12 +609,13 @@ func validateAzureRMStorageAccountTags(v interface{}, _ string) (warnings []stri func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + client := meta.(*ArmClient).Storage.AccountsClient + advancedThreatProtectionClient := meta.(*ArmClient).SecurityCenter.AdvancedThreatProtectionClient storageAccountName := d.Get("name").(string) resourceGroupName := d.Get("resource_group_name").(string) - if requireResourcesToBeImported { + if features.ShouldResourcesBeImported() { existing, err := client.GetProperties(ctx, resourceGroupName, storageAccountName, "") if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -438,7 +630,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e accountKind := d.Get("account_kind").(string) location := azure.NormalizeLocation(d.Get("location").(string)) - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) enableBlobEncryption := d.Get("enable_blob_encryption").(bool) enableFileEncryption := d.Get("enable_file_encryption").(bool) enableHTTPSTrafficOnly := d.Get("enable_https_traffic_only").(bool) @@ -449,14 +641,12 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e storageType := fmt.Sprintf("%s_%s", accountTier, replicationType) storageAccountEncryptionSource := d.Get("account_encryption_source").(string) - networkRules := expandStorageAccountNetworkRules(d) - parameters := storage.AccountCreateParameters{ Location: &location, Sku: &storage.Sku{ Name: storage.SkuName(storageType), }, - Tags: expandTags(tags), + Tags: tags.Expand(t), Kind: storage.Kind(accountKind), AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{ Encryption: &storage.Encryption{ @@ -470,7 +660,7 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e KeySource: storage.KeySource(storageAccountEncryptionSource), }, EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly, - NetworkRuleSet: networkRules, + NetworkRuleSet: expandStorageAccountNetworkRules(d), IsHnsEnabled: &isHnsEnabled, }, } @@ -491,8 +681,8 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e } } - // AccessTier is only valid for BlobStorage and StorageV2 accounts - if accountKind == string(storage.BlobStorage) || accountKind == string(storage.StorageV2) { + // AccessTier is only valid for BlobStorage, StorageV2, and FileStorage accounts + if accountKind == string(storage.BlobStorage) || accountKind == string(storage.StorageV2) || accountKind == string(storage.FileStorage) { accessTier, ok := d.GetOk("access_tier") if !ok { // default to "Hot" @@ -506,6 +696,13 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e } } + // AccountTier must be Premium for FileStorage + if accountKind == string(storage.FileStorage) { + if string(parameters.Sku.Tier) == string(storage.StandardLRS) { + return fmt.Errorf("A `account_tier` of `Standard` is not supported for FileStorage accounts.") + } + } + // Create future, err := client.Create(ctx, resourceGroupName, storageAccountName, parameters) if err != nil { @@ -528,6 +725,37 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e log.Printf("[INFO] storage account %q ID: %q", storageAccountName, *account.ID) d.SetId(*account.ID) + // as this is not available in all regions, and presumably off by default + // lets only try to set this value when true + // TODO in 2.0 switch to guarding this with d.GetOkExists() ? + if v := d.Get("enable_advanced_threat_protection").(bool); v { + advancedThreatProtectionSetting := security.AdvancedThreatProtectionSetting{ + AdvancedThreatProtectionProperties: &security.AdvancedThreatProtectionProperties{ + IsEnabled: utils.Bool(v), + }, + } + + if _, err = advancedThreatProtectionClient.Create(ctx, d.Id(), advancedThreatProtectionSetting); err != nil { + return fmt.Errorf("Error updating Azure Storage Account enable_advanced_threat_protection %q: %+v", storageAccountName, err) + } + } + + if val, ok := d.GetOk("queue_properties"); ok { + queueClient, err := meta.(*ArmClient).Storage.QueuesClient(ctx, resourceGroupName, storageAccountName) + if err != nil { + return fmt.Errorf("Error building Queues Client: %s", err) + } + + queueProperties, err := expandQueueProperties(val.([]interface{})) + if err != nil { + return fmt.Errorf("Error expanding `queue_properties` for Azure Storage Account %q: %+v", storageAccountName, err) + } + + if _, err = queueClient.SetServiceProperties(ctx, storageAccountName, queueProperties); err != nil { + return fmt.Errorf("Error updating Azure Storage Account `queue_properties` %q: %+v", storageAccountName, err) + } + } + return resourceArmStorageAccountRead(d, meta) } @@ -536,8 +764,10 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e // available requires a call to Update per parameter... func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient - id, err := parseAzureResourceID(d.Id()) + client := meta.(*ArmClient).Storage.AccountsClient + advancedThreatProtectionClient := meta.(*ArmClient).SecurityCenter.AdvancedThreatProtectionClient + + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -590,10 +820,10 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } if d.HasChange("tags") { - tags := d.Get("tags").(map[string]interface{}) + t := d.Get("tags").(map[string]interface{}) opts := storage.AccountUpdateParameters{ - Tags: expandTags(tags), + Tags: tags.Expand(t), } if _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts); err != nil { @@ -632,17 +862,15 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("enable_file_encryption") } - _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts) - if err != nil { + if _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts); err != nil { return fmt.Errorf("Error updating Azure Storage Account Encryption %q: %+v", storageAccountName, err) } } if d.HasChange("custom_domain") { - customDomain := expandStorageAccountCustomDomain(d) opts := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ - CustomDomain: customDomain, + CustomDomain: expandStorageAccountCustomDomain(d), }, } @@ -668,10 +896,8 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } if d.HasChange("identity") { - storageAccountIdentity := expandAzureRmStorageAccountIdentity(d) - opts := storage.AccountUpdateParameters{ - Identity: storageAccountIdentity, + Identity: expandAzureRmStorageAccountIdentity(d), } if _, err := client.Update(ctx, resourceGroupName, storageAccountName, opts); err != nil { @@ -680,11 +906,9 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e } if d.HasChange("network_rules") { - networkRules := expandStorageAccountNetworkRules(d) - opts := storage.AccountUpdateParameters{ AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ - NetworkRuleSet: networkRules, + NetworkRuleSet: expandStorageAccountNetworkRules(d), }, } @@ -695,16 +919,49 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("network_rules") } + if d.HasChange("enable_advanced_threat_protection") { + opts := security.AdvancedThreatProtectionSetting{ + AdvancedThreatProtectionProperties: &security.AdvancedThreatProtectionProperties{ + IsEnabled: utils.Bool(d.Get("enable_advanced_threat_protection").(bool)), + }, + } + + if _, err := advancedThreatProtectionClient.Create(ctx, d.Id(), opts); err != nil { + return fmt.Errorf("Error updating Azure Storage Account enable_advanced_threat_protection %q: %+v", storageAccountName, err) + } + + d.SetPartial("enable_advanced_threat_protection") + } + + if d.HasChange("queue_properties") { + queueClient, err := meta.(*ArmClient).Storage.QueuesClient(ctx, resourceGroupName, storageAccountName) + if err != nil { + return fmt.Errorf("Error building Queues Client: %s", err) + } + + queueProperties, err := expandQueueProperties(d.Get("queue_properties").([]interface{})) + if err != nil { + return fmt.Errorf("Error expanding `queue_properties` for Azure Storage Account %q: %+v", storageAccountName, err) + } + + if _, err = queueClient.SetServiceProperties(ctx, storageAccountName, queueProperties); err != nil { + return fmt.Errorf("Error updating Azure Storage Account `queue_properties` %q: %+v", storageAccountName, err) + } + + d.SetPartial("queue_properties") + } + d.Partial(false) return resourceArmStorageAccountRead(d, meta) } func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + client := meta.(*ArmClient).Storage.AccountsClient + advancedThreatProtectionClient := meta.(*ArmClient).SecurityCenter.AdvancedThreatProtectionClient endpointSuffix := meta.(*ArmClient).environment.StorageEndpointSuffix - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -720,12 +977,31 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %+v", name, err) } + // handle the user not having permissions to list the keys + d.Set("primary_connection_string", "") + d.Set("secondary_connection_string", "") + d.Set("primary_blob_connection_string", "") + d.Set("secondary_blob_connection_string", "") + d.Set("primary_access_key", "") + d.Set("secondary_access_key", "") + keys, err := client.ListKeys(ctx, resGroup, name) if err != nil { - return err + // the API returns a 200 with an inner error of a 409.. + var hasWriteLock bool + var doesntHavePermissions bool + if e, ok := err.(azautorest.DetailedError); ok { + if status, ok := e.StatusCode.(int); ok { + hasWriteLock = status == http.StatusConflict + doesntHavePermissions = status == http.StatusUnauthorized + } + } + + if !hasWriteLock && !doesntHavePermissions { + return fmt.Errorf("Error listing Keys for Storage Account %q (Resource Group %q): %s", name, resGroup, err) + } } - accessKeys := *keys.Keys d.Set("name", resp.Name) d.Set("resource_group_name", resGroup) if location := resp.Location; location != nil { @@ -766,62 +1042,103 @@ func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) err d.Set("primary_location", props.PrimaryLocation) d.Set("secondary_location", props.SecondaryLocation) - if len(accessKeys) > 0 { - pcs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *accessKeys[0].Value, endpointSuffix) - d.Set("primary_connection_string", pcs) - } + if accessKeys := keys.Keys; accessKeys != nil { + storageAccountKeys := *accessKeys + if len(storageAccountKeys) > 0 { + pcs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *storageAccountKeys[0].Value, endpointSuffix) + d.Set("primary_connection_string", pcs) + } - if len(accessKeys) > 1 { - scs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *accessKeys[1].Value, endpointSuffix) - d.Set("secondary_connection_string", scs) + if len(storageAccountKeys) > 1 { + scs := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=%s", *resp.Name, *storageAccountKeys[1].Value, endpointSuffix) + d.Set("secondary_connection_string", scs) + } } if err := flattenAndSetAzureRmStorageAccountPrimaryEndpoints(d, props.PrimaryEndpoints); err != nil { return fmt.Errorf("error setting primary endpoints and hosts for blob, queue, table and file: %+v", err) } - var primaryBlobConnectStr string - if v := props.PrimaryEndpoints; v != nil { - primaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, accessKeys[0].Value) + if accessKeys := keys.Keys; accessKeys != nil { + storageAccountKeys := *accessKeys + var primaryBlobConnectStr string + if v := props.PrimaryEndpoints; v != nil { + primaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, storageAccountKeys[0].Value) + } + d.Set("primary_blob_connection_string", primaryBlobConnectStr) } - d.Set("primary_blob_connection_string", primaryBlobConnectStr) if err := flattenAndSetAzureRmStorageAccountSecondaryEndpoints(d, props.SecondaryEndpoints); err != nil { return fmt.Errorf("error setting secondary endpoints and hosts for blob, queue, table: %+v", err) } - var secondaryBlobConnectStr string - if v := props.SecondaryEndpoints; v != nil { - secondaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, accessKeys[1].Value) + if accessKeys := keys.Keys; accessKeys != nil { + storageAccountKeys := *accessKeys + var secondaryBlobConnectStr string + if v := props.SecondaryEndpoints; v != nil { + secondaryBlobConnectStr = getBlobConnectionString(v.Blob, resp.Name, storageAccountKeys[1].Value) + } + d.Set("secondary_blob_connection_string", secondaryBlobConnectStr) } - d.Set("secondary_blob_connection_string", secondaryBlobConnectStr) - networkRules := props.NetworkRuleSet - if networkRules != nil { + if networkRules := props.NetworkRuleSet; networkRules != nil { if err := d.Set("network_rules", flattenStorageAccountNetworkRules(networkRules)); err != nil { return fmt.Errorf("Error setting `network_rules`: %+v", err) } } } - d.Set("primary_access_key", accessKeys[0].Value) - d.Set("secondary_access_key", accessKeys[1].Value) + if accessKeys := keys.Keys; accessKeys != nil { + storageAccountKeys := *accessKeys + d.Set("primary_access_key", storageAccountKeys[0].Value) + d.Set("secondary_access_key", storageAccountKeys[1].Value) + } identity := flattenAzureRmStorageAccountIdentity(resp.Identity) if err := d.Set("identity", identity); err != nil { return err } - flattenAndSetTags(d, resp.Tags) + // TODO in 2.0 switch to guarding this with d.GetOkExists() + atp, err := advancedThreatProtectionClient.Get(ctx, d.Id()) + if err != nil { + msg := err.Error() + if !strings.Contains(msg, "No registered resource provider found for location '") { + if !strings.Contains(msg, "' and API version '2017-08-01-preview' for type ") { + return fmt.Errorf("Error reading the advanced threat protection settings of AzureRM Storage Account %q: %+v", name, err) + } + } + } else { + if atpp := atp.AdvancedThreatProtectionProperties; atpp != nil { + d.Set("enable_advanced_threat_protection", atpp.IsEnabled) + } + } - return nil + queueClient, err := meta.(*ArmClient).Storage.QueuesClient(ctx, resGroup, name) + if err != nil { + return fmt.Errorf("Error building Queues Client: %s", err) + } + + queueProps, err := queueClient.GetServiceProperties(ctx, name) + if err != nil { + if queueProps.Response.Response != nil && !utils.ResponseWasNotFound(queueProps.Response) { + return fmt.Errorf("Error reading queue properties for AzureRM Storage Account %q: %+v", name, err) + } + } + + if err := d.Set("queue_properties", flattenQueueProperties(queueProps)); err != nil { + return fmt.Errorf("Error setting `queue_properties `for AzureRM Storage Account %q: %+v", name, err) + } + + return tags.FlattenAndSet(d, resp.Tags) } func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext - client := meta.(*ArmClient).storageServiceClient + storageClient := meta.(*ArmClient).Storage + client := storageClient.AccountsClient - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -847,7 +1164,7 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e continue } - id, err2 := parseAzureResourceID(*v.VirtualNetworkResourceID) + id, err2 := azure.ParseAzureResourceID(*v.VirtualNetworkResourceID) if err2 != nil { return err2 } @@ -859,8 +1176,8 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e } } - azureRMLockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) - defer azureRMUnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + locks.MultipleByName(&virtualNetworkNames, virtualNetworkResourceName) + defer locks.UnlockMultipleByName(&virtualNetworkNames, virtualNetworkResourceName) resp, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -869,6 +1186,9 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e } } + // remove this from the cache + storageClient.ClearFromCache(resourceGroup, name) + return nil } @@ -908,13 +1228,15 @@ func expandStorageAccountNetworkRules(d *schema.ResourceData) *storage.NetworkRu } networkRule := networkRules[0].(map[string]interface{}) - networkRuleSet := &storage.NetworkRuleSet{} + networkRuleSet := &storage.NetworkRuleSet{ + IPRules: expandStorageAccountIPRules(networkRule), + VirtualNetworkRules: expandStorageAccountVirtualNetworks(networkRule), + Bypass: expandStorageAccountBypass(networkRule), + } - networkRuleSet.IPRules = expandStorageAccountIPRules(networkRule) - networkRuleSet.VirtualNetworkRules = expandStorageAccountVirtualNetworks(networkRule) - networkRuleSet.Bypass = expandStorageAccountBypass(networkRule) - // Default Access is disabled when network rules are set. - networkRuleSet.DefaultAction = storage.DefaultActionDeny + if v := networkRule["default_action"]; v != nil { + networkRuleSet.DefaultAction = storage.DefaultAction(v.(string)) + } return networkRuleSet } @@ -962,6 +1284,112 @@ func expandStorageAccountBypass(networkRule map[string]interface{}) storage.Bypa return storage.Bypass(strings.Join(bypassValues, ", ")) } +func expandQueueProperties(input []interface{}) (queues.StorageServiceProperties, error) { + var err error + properties := queues.StorageServiceProperties{} + if len(input) == 0 { + return properties, nil + } + + attrs := input[0].(map[string]interface{}) + + properties.Cors = expandQueuePropertiesCors(attrs["cors_rule"].([]interface{})) + properties.Logging = expandQueuePropertiesLogging(attrs["logging"].([]interface{})) + properties.MinuteMetrics, err = expandQueuePropertiesMetrics(attrs["minute_metrics"].([]interface{})) + if err != nil { + return properties, fmt.Errorf("Error expanding `minute_metrics`: %+v", err) + } + properties.HourMetrics, err = expandQueuePropertiesMetrics(attrs["hour_metrics"].([]interface{})) + if err != nil { + return properties, fmt.Errorf("Error expanding `hour_metrics`: %+v", err) + } + + return properties, nil +} + +func expandQueuePropertiesMetrics(input []interface{}) (*queues.MetricsConfig, error) { + if len(input) == 0 { + return &queues.MetricsConfig{}, nil + } + + metricsAttr := input[0].(map[string]interface{}) + + metrics := &queues.MetricsConfig{ + Version: metricsAttr["version"].(string), + Enabled: metricsAttr["enabled"].(bool), + } + + if v, ok := metricsAttr["retention_policy_days"]; ok { + if days := v.(int); days > 0 { + metrics.RetentionPolicy = queues.RetentionPolicy{ + Days: days, + Enabled: true, + } + } + } + + if v, ok := metricsAttr["include_apis"]; ok { + includeAPIs := v.(bool) + if metrics.Enabled { + metrics.IncludeAPIs = &includeAPIs + } else if includeAPIs { + return nil, fmt.Errorf("`include_apis` may only be set when `enabled` is true") + } + } + + return metrics, nil +} + +func expandQueuePropertiesLogging(input []interface{}) *queues.LoggingConfig { + if len(input) == 0 { + return &queues.LoggingConfig{} + } + + loggingAttr := input[0].(map[string]interface{}) + logging := &queues.LoggingConfig{ + Version: loggingAttr["version"].(string), + Delete: loggingAttr["delete"].(bool), + Read: loggingAttr["read"].(bool), + Write: loggingAttr["write"].(bool), + } + + if v, ok := loggingAttr["retention_policy_days"]; ok { + if days := v.(int); days > 0 { + logging.RetentionPolicy = queues.RetentionPolicy{ + Days: days, + Enabled: true, + } + } + } + + return logging +} + +func expandQueuePropertiesCors(input []interface{}) *queues.Cors { + if len(input) == 0 { + return &queues.Cors{} + } + + corsRules := make([]queues.CorsRule, 0) + for _, attr := range input { + corsRuleAttr := attr.(map[string]interface{}) + corsRule := queues.CorsRule{} + + corsRule.AllowedOrigins = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_origins"].([]interface{})), ",") + corsRule.ExposedHeaders = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["exposed_headers"].([]interface{})), ",") + corsRule.AllowedHeaders = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_headers"].([]interface{})), ",") + corsRule.AllowedMethods = strings.Join(*utils.ExpandStringSlice(corsRuleAttr["allowed_methods"].([]interface{})), ",") + corsRule.MaxAgeInSeconds = corsRuleAttr["max_age_in_seconds"].(int) + + corsRules = append(corsRules, corsRule) + } + + cors := &queues.Cors{ + CorsRule: corsRules, + } + return cors +} + func flattenStorageAccountNetworkRules(input *storage.NetworkRuleSet) []interface{} { if len(*input.IPRules) == 0 && len(*input.VirtualNetworkRules) == 0 { return []interface{}{} @@ -971,6 +1399,7 @@ func flattenStorageAccountNetworkRules(input *storage.NetworkRuleSet) []interfac networkRules["ip_rules"] = schema.NewSet(schema.HashString, flattenStorageAccountIPRules(input.IPRules)) networkRules["virtual_network_subnet_ids"] = schema.NewSet(schema.HashString, flattenStorageAccountVirtualNetworks(input.VirtualNetworkRules)) networkRules["bypass"] = schema.NewSet(schema.HashString, flattenStorageAccountBypass(input.Bypass)) + networkRules["default_action"] = string(input.DefaultAction) return []interface{}{networkRules} } @@ -998,6 +1427,106 @@ func flattenStorageAccountVirtualNetworks(input *[]storage.VirtualNetworkRule) [ return virtualNetworks } +func flattenQueueProperties(input queues.StorageServicePropertiesResponse) []interface{} { + if input.Response.Response == nil { + return []interface{}{} + } + + queueProperties := make(map[string]interface{}) + + if cors := input.Cors; cors != nil { + if len(cors.CorsRule) > 0 { + if cors.CorsRule[0].AllowedOrigins != "" { + queueProperties["cors_rule"] = flattenQueuePropertiesCorsRule(input.Cors.CorsRule) + } + } + } + + if logging := input.Logging; logging != nil { + if logging.Version != "" { + queueProperties["logging"] = flattenQueuePropertiesLogging(*logging) + } + } + + if hourMetrics := input.HourMetrics; hourMetrics != nil { + if hourMetrics.Version != "" { + queueProperties["hour_metrics"] = flattenQueuePropertiesMetrics(*hourMetrics) + } + } + + if minuteMetrics := input.MinuteMetrics; minuteMetrics != nil { + if minuteMetrics.Version != "" { + queueProperties["minute_metrics"] = flattenQueuePropertiesMetrics(*minuteMetrics) + } + } + + if len(queueProperties) == 0 { + return []interface{}{} + } + return []interface{}{queueProperties} +} + +func flattenQueuePropertiesMetrics(input queues.MetricsConfig) []interface{} { + metrics := make(map[string]interface{}) + + metrics["version"] = input.Version + metrics["enabled"] = input.Enabled + + if input.IncludeAPIs != nil { + metrics["include_apis"] = *input.IncludeAPIs + } + + if input.RetentionPolicy.Enabled { + metrics["retention_policy_days"] = input.RetentionPolicy.Days + } + + return []interface{}{metrics} +} + +func flattenQueuePropertiesCorsRule(input []queues.CorsRule) []interface{} { + corsRules := make([]interface{}, 0) + + for _, corsRule := range input { + attr := make(map[string]interface{}) + + attr["allowed_origins"] = flattenCorsProperty(corsRule.AllowedOrigins) + attr["allowed_methods"] = flattenCorsProperty(corsRule.AllowedMethods) + attr["allowed_headers"] = flattenCorsProperty(corsRule.AllowedHeaders) + attr["exposed_headers"] = flattenCorsProperty(corsRule.ExposedHeaders) + attr["max_age_in_seconds"] = corsRule.MaxAgeInSeconds + + corsRules = append(corsRules, attr) + } + + return corsRules +} + +func flattenQueuePropertiesLogging(input queues.LoggingConfig) []interface{} { + logging := make(map[string]interface{}) + + logging["version"] = input.Version + logging["delete"] = input.Delete + logging["read"] = input.Read + logging["write"] = input.Write + + if input.RetentionPolicy.Enabled { + logging["retention_policy_days"] = input.RetentionPolicy.Days + } + + return []interface{}{logging} +} + +func flattenCorsProperty(input string) []interface{} { + results := make([]interface{}, 0, len(input)) + + origins := strings.Split(input, ",") + for _, origin := range origins { + results = append(results, origin) + } + + return results +} + func flattenStorageAccountBypass(input storage.Bypass) []interface{} { bypassValues := strings.Split(string(input), ", ") bypass := make([]interface{}, len(bypassValues)) @@ -1147,7 +1676,9 @@ func setEndpointAndHost(d *schema.ResourceData, ordinalString string, endpointTy host = u.Host } + // lintignore: R001 d.Set(fmt.Sprintf("%s_%s_endpoint", ordinalString, typeString), endpoint) + // lintignore: R001 d.Set(fmt.Sprintf("%s_%s_host", ordinalString, typeString), host) return nil } diff --git a/azurerm/resource_arm_storage_account_test.go b/azurerm/resource_arm_storage_account_test.go index ef83d4a1630a..d9387b264064 100644 --- a/azurerm/resource_arm_storage_account_test.go +++ b/azurerm/resource_arm_storage_account_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" @@ -98,7 +99,7 @@ func TestAccAzureRMStorageAccount_basic(t *testing.T) { } func TestAccAzureRMStorageAccount_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -127,6 +128,46 @@ func TestAccAzureRMStorageAccount_requiresImport(t *testing.T) { }) } +func TestAccAzureRMStorageAccount_writeLock(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccount_writeLock(ri, rs, location), + }, + { + // works around a bug in the test suite where the Storage Account won't be re-read after the Lock's provisioned + Config: testAccAzureRMStorageAccount_writeLock(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "account_tier", "Standard"), + resource.TestCheckResourceAttr(resourceName, "account_replication_type", "LRS"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "production"), + resource.TestCheckResourceAttr(resourceName, "primary_connection_string", ""), + resource.TestCheckResourceAttr(resourceName, "secondary_connection_string", ""), + resource.TestCheckResourceAttr(resourceName, "primary_blob_connection_string", ""), + resource.TestCheckResourceAttr(resourceName, "secondary_blob_connection_string", ""), + resource.TestCheckResourceAttr(resourceName, "primary_access_key", ""), + resource.TestCheckResourceAttr(resourceName, "secondary_access_key", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMStorageAccount_premium(t *testing.T) { resourceName := "azurerm_storage_account.testsa" ri := tf.AccRandTimeInt() @@ -397,6 +438,72 @@ func TestAccAzureRMStorageAccount_blobStorageWithUpdate(t *testing.T) { }) } +func TestAccAzureRMStorageAccount_blockBlobStorage(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccount_blockBlobStorage(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "account_kind", "BlockBlobStorage"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "access_tier", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_fileStorageWithUpdate(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_fileStorage(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_fileStorageUpdate(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "account_kind", "FileStorage"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "account_tier", "Premium"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "access_tier", "Hot"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "account_tier", "Premium"), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "access_tier", "Cool"), + ), + }, + }, + }) +} func TestAccAzureRMStorageAccount_storageV2WithUpdate(t *testing.T) { resourceName := "azurerm_storage_account.testsa" ri := tf.AccRandTimeInt() @@ -543,6 +650,7 @@ func TestAccAzureRMStorageAccount_networkRules(t *testing.T) { Config: preConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_rules.0.default_action", "Deny"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "1"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "1"), ), @@ -556,8 +664,9 @@ func TestAccAzureRMStorageAccount_networkRules(t *testing.T) { Config: postConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_rules.0.default_action", "Deny"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "2"), - resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.bypass.#", "2"), ), }, @@ -571,7 +680,7 @@ func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) { rs := acctest.RandString(4) location := testLocation() preConfig := testAccAzureRMStorageAccount_networkRules(ri, rs, location) - postConfig := testAccAzureRMStorageAccount_basic(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_networkRulesReverted(ri, rs, location) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -582,6 +691,7 @@ func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) { Config: preConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "network_rules.0.default_action", "Deny"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.ip_rules.#", "1"), resource.TestCheckResourceAttr(resourceName, "network_rules.0.virtual_network_subnet_ids.#", "1"), ), @@ -590,13 +700,93 @@ func TestAccAzureRMStorageAccount_networkRulesDeleted(t *testing.T) { Config: postConfig, Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageAccountExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "network_rules.#", "0"), + resource.TestCheckResourceAttr(resourceName, "network_rules.0.default_action", "Allow"), ), }, }, }) } +func TestAccAzureRMStorageAccount_enableAdvancedThreatProtection(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtection(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_enableAdvancedThreatProtectionDisabled(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + resource.TestCheckResourceAttr("azurerm_storage_account.testsa", "enable_advanced_threat_protection", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccount_queueProperties(t *testing.T) { + resourceName := "azurerm_storage_account.testsa" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + preConfig := testAccAzureRMStorageAccount_queueProperties(ri, rs, location) + postConfig := testAccAzureRMStorageAccount_queuePropertiesUpdated(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMStorageAccountExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -610,7 +800,7 @@ func testCheckAzureRMStorageAccountExists(resourceName string) resource.TestChec // Ensure resource group exists in API ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).storageServiceClient + conn := testAccProvider.Meta().(*ArmClient).Storage.AccountsClient resp, err := conn.GetProperties(ctx, resourceGroup, storageAccount, "") if err != nil { @@ -638,7 +828,7 @@ func testCheckAzureRMStorageAccountDisappears(resourceName string) resource.Test // Ensure resource group exists in API ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).storageServiceClient + conn := testAccProvider.Meta().(*ArmClient).Storage.AccountsClient if _, err := conn.Delete(ctx, resourceGroup, storageAccount); err != nil { return fmt.Errorf("Bad: Delete on storageServiceClient: %+v", err) @@ -650,7 +840,7 @@ func testCheckAzureRMStorageAccountDisappears(resourceName string) resource.Test func testCheckAzureRMStorageAccountDestroy(s *terraform.State) error { ctx := testAccProvider.Meta().(*ArmClient).StopContext - conn := testAccProvider.Meta().(*ArmClient).storageServiceClient + conn := testAccProvider.Meta().(*ArmClient).Storage.AccountsClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_storage_account" { @@ -710,6 +900,19 @@ resource "azurerm_storage_account" "import" { `, template) } +func testAccAzureRMStorageAccount_writeLock(rInt int, rString, location string) string { + template := testAccAzureRMStorageAccount_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_management_lock" "test" { + name = "acctestlock-%d" + scope = "${azurerm_storage_account.testsa.id}" + lock_level = "ReadOnly" +} +`, template, rInt) +} + func testAccAzureRMStorageAccount_premium(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "testrg" { @@ -903,11 +1106,11 @@ resource "azurerm_storage_account" "testsa" { name = "unlikely23exst2acct%s" resource_group_name = "${azurerm_resource_group.testrg.name}" - location = "${azurerm_resource_group.testrg.location}" - account_kind = "StorageV2" - account_tier = "Standard" - account_replication_type = "LRS" - is_hns_enabled = true + location = "${azurerm_resource_group.testrg.location}" + account_kind = "StorageV2" + account_tier = "Standard" + account_replication_type = "LRS" + is_hns_enabled = true } `, rInt, location, rString) } @@ -923,11 +1126,11 @@ resource "azurerm_storage_account" "testsa" { name = "unlikely23exst2acct%s" resource_group_name = "${azurerm_resource_group.testrg.name}" - location = "${azurerm_resource_group.testrg.location}" - account_kind = "StorageV2" - account_tier = "Standard" - account_replication_type = "LRS" - is_hns_enabled = false + location = "${azurerm_resource_group.testrg.location}" + account_kind = "StorageV2" + account_tier = "Standard" + account_replication_type = "LRS" + is_hns_enabled = false } `, rInt, location, rString) } @@ -979,6 +1182,77 @@ resource "azurerm_storage_account" "testsa" { `, rInt, location, rString) } +func testAccAzureRMStorageAccount_blockBlobStorage(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_kind = "BlockBlobStorage" + account_tier = "Premium" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_fileStorage(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_kind = "FileStorage" + account_tier = "Premium" + account_replication_type = "LRS" + access_tier = "Hot" + + tags = { + environment = "production" + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_fileStorageUpdate(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_kind = "FileStorage" + account_tier = "Premium" + account_replication_type = "LRS" + access_tier = "Cool" + + tags = { + environment = "production" + } +} +`, rInt, location, rString) +} + func testAccAzureRMStorageAccount_storageV2(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "testrg" { @@ -1103,6 +1377,7 @@ resource "azurerm_storage_account" "testsa" { account_replication_type = "LRS" network_rules { + default_action = "Deny" ip_rules = ["127.0.0.1"] virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] } @@ -1144,8 +1419,51 @@ resource "azurerm_storage_account" "testsa" { account_replication_type = "LRS" network_rules { - ip_rules = ["127.0.0.1", "127.0.0.2"] - bypass = ["Logging", "Metrics"] + default_action = "Deny" + ip_rules = ["127.0.0.1", "127.0.0.2"] + bypass = ["Logging", "Metrics"] + } + + tags = { + environment = "production" + } +} +`, rInt, location, rInt, rInt, rString) +} + +func testAccAzureRMStorageAccount_networkRulesReverted(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.testrg.location}" + resource_group_name = "${azurerm_resource_group.testrg.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.testrg.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + network_rules { + default_action = "Allow" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] } tags = { @@ -1154,3 +1472,143 @@ resource "azurerm_storage_account" "testsa" { } `, rInt, location, rInt, rInt, rString) } + +func testAccAzureRMStorageAccount_enableAdvancedThreatProtection(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + enable_advanced_threat_protection = true +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_enableAdvancedThreatProtectionDisabled(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + enable_advanced_threat_protection = false +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_queueProperties(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + queue_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["x-tempo-*"] + allowed_methods = ["GET", "PUT"] + max_age_in_seconds = "500" + } + + logging { + version = "1.0" + delete = true + read = true + write = true + retention_policy_days = 7 + } + + hour_metrics { + version = "1.0" + enabled = false + retention_policy_days = 7 + } + + minute_metrics { + version = "1.0" + enabled = false + retention_policy_days = 7 + } + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageAccount_queuePropertiesUpdated(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + queue_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*", "x-method-*"] + allowed_headers = ["*"] + allowed_methods = ["GET"] + max_age_in_seconds = "2000000000" + } + cors_rule { + allowed_origins = ["http://www.test.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["*"] + allowed_methods = ["PUT"] + max_age_in_seconds = "1000" + } + logging { + version = "1.0" + delete = true + read = true + write = true + retention_policy_days = 7 + } + + hour_metrics { + version = "1.0" + enabled = true + retention_policy_days = 7 + include_apis = true + } + + minute_metrics { + version = "1.0" + enabled = true + include_apis = false + retention_policy_days = 7 + } + } +} +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_blob.go b/azurerm/resource_arm_storage_blob.go index 2716d9dc3c1b..896253a45a9c 100644 --- a/azurerm/resource_arm_storage_blob.go +++ b/azurerm/resource_arm_storage_blob.go @@ -1,29 +1,20 @@ package azurerm import ( - "bytes" - "crypto/rand" - "encoding/base64" "fmt" - "io" "log" - "net/url" - "os" - "runtime" "strings" - "sync" + "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/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" - - "github.com/hashicorp/terraform/helper/validation" - + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" - - "github.com/Azure/azure-sdk-for-go/storage" - azauto "github.com/Azure/go-autorest/autorest/azure" - "github.com/hashicorp/terraform/helper/schema" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" ) func resourceArmStorageBlob() *schema.Resource { @@ -43,27 +34,33 @@ func resourceArmStorageBlob() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + // TODO: add validation }, - "resource_group_name": azure.SchemaResourceGroupName(), - "storage_account_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, }, "storage_container_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageContainerName, }, "type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"block", "page"}, true), + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: suppress.CaseDifference, // TODO: remove in 2.0 + ValidateFunc: validation.StringInSlice([]string{ + "Append", + "Block", + "Page", + }, true), }, "size": { @@ -74,25 +71,42 @@ func resourceArmStorageBlob() *schema.Resource { ValidateFunc: validate.IntDivisibleBy(512), }, + "access_tier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + string(blobs.Archive), + string(blobs.Cool), + string(blobs.Hot), + }, false), + }, + "content_type": { + Type: schema.TypeString, + Optional: true, + Default: "application/octet-stream", + }, + + "source": { Type: schema.TypeString, Optional: true, - Default: "application/octet-stream", - ConflictsWith: []string{"source_uri"}, + ForceNew: true, + ConflictsWith: []string{"source_uri", "source_content"}, }, - "source": { + "source_content": { Type: schema.TypeString, Optional: true, ForceNew: true, - ConflictsWith: []string{"source_uri"}, + ConflictsWith: []string{"source", "source_uri"}, }, "source_uri": { Type: schema.TypeString, Optional: true, ForceNew: true, - ConflictsWith: []string{"source"}, + ConflictsWith: []string{"source", "source_content"}, }, "url": { @@ -101,6 +115,7 @@ func resourceArmStorageBlob() *schema.Resource { }, "parallelism": { + // TODO: @tombuildsstuff - a note this only works for Page blobs Type: schema.TypeInt, Optional: true, Default: 8, @@ -108,678 +123,233 @@ func resourceArmStorageBlob() *schema.Resource { ValidateFunc: validation.IntAtLeast(1), }, + "metadata": storage.MetaDataSchema(), + + // Deprecated fields "attempts": { Type: schema.TypeInt, Optional: true, Default: 1, ForceNew: true, + Deprecated: "Retries are now handled by the Azure SDK as such this field is no longer necessary and will be removed in v2.0 of the Azure Provider", ValidateFunc: validation.IntAtLeast(1), }, - "metadata": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validate.NoEmptyStrings, - }, - }, + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), }, } } func resourceArmStorageBlobCreate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext - env := armClient.environment + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - resourceGroupName := d.Get("resource_group_name").(string) - storageAccountName := d.Get("storage_account_name").(string) + accountName := d.Get("storage_account_name").(string) + containerName := d.Get("storage_container_name").(string) + name := d.Get("name").(string) - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", accountName, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Blob %q (Container %q / Account %q)", name, containerName, accountName) } - name := d.Get("name").(string) - blobType := d.Get("type").(string) - containerName := d.Get("storage_container_name").(string) - sourceUri := d.Get("source_uri").(string) - contentType := d.Get("content_type").(string) - - log.Printf("[INFO] Creating blob %q in container %q within storage account %q", name, containerName, storageAccountName) - container := blobClient.GetContainerReference(containerName) - blob := container.GetBlobReference(name) + blobsClient, err := storageClient.BlobsClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Blobs Client: %s", err) + } - // gives us https://example.blob.core.windows.net/container/file.vhd - id := fmt.Sprintf("https://%s.blob.%s/%s/%s", storageAccountName, env.StorageEndpointSuffix, containerName, name) - if requireResourcesToBeImported && d.IsNewResource() { - exists, err := blob.Exists() + id := blobsClient.GetResourceID(accountName, containerName, name) + if features.ShouldResourcesBeImported() && d.IsNewResource() { + input := blobs.GetPropertiesInput{} + props, err := blobsClient.GetProperties(ctx, accountName, containerName, name, input) if err != nil { - return fmt.Errorf("Error checking if Blob %q exists (Container %q / Account %q / Resource Group %q): %s", name, containerName, storageAccountName, resourceGroupName, err) + if !utils.ResponseWasNotFound(props.Response) { + return fmt.Errorf("Error checking if Blob %q exists (Container %q / Account %q / Resource Group %q): %s", name, containerName, accountName, *resourceGroup, err) + } } - - if exists { + if !utils.ResponseWasNotFound(props.Response) { return tf.ImportAsExistsError("azurerm_storage_blob", id) } } - if sourceUri != "" { - options := &storage.CopyOptions{} - if err := blob.Copy(sourceUri, options); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - } else { - switch strings.ToLower(blobType) { - case "block": - options := &storage.PutBlobOptions{} - if err := blob.CreateBlockBlob(options); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - - source := d.Get("source").(string) - if source != "" { - parallelism := d.Get("parallelism").(int) - attempts := d.Get("attempts").(int) + log.Printf("[DEBUG] Creating Blob %q in Container %q within Storage Account %q..", name, containerName, accountName) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + blobInput := storage.BlobUpload{ + AccountName: accountName, + ContainerName: containerName, + BlobName: name, + Client: blobsClient, - if err := resourceArmStorageBlobBlockUploadFromSource(containerName, name, source, contentType, blobClient, parallelism, attempts); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - } - case "page": - source := d.Get("source").(string) - if source != "" { - parallelism := d.Get("parallelism").(int) - attempts := d.Get("attempts").(int) - - if err := resourceArmStorageBlobPageUploadFromSource(containerName, name, source, contentType, blobClient, parallelism, attempts); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - } else { - size := int64(d.Get("size").(int)) - options := &storage.PutBlobOptions{} - - blob.Properties.ContentLength = size - blob.Properties.ContentType = contentType - if err := blob.PutPageBlob(options); err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - } - } + BlobType: d.Get("type").(string), + ContentType: d.Get("content_type").(string), + MetaData: storage.ExpandMetaData(metaDataRaw), + Parallelism: d.Get("parallelism").(int), + Size: d.Get("size").(int), + Source: d.Get("source").(string), + SourceContent: d.Get("source_content").(string), + SourceUri: d.Get("source_uri").(string), } - - blob.Metadata = expandStorageAccountBlobMetadata(d) - - opts := &storage.SetBlobMetadataOptions{} - if err := blob.SetMetadata(opts); err != nil { - return fmt.Errorf("Error setting metadata for storage blob on Azure: %s", err) + if err := blobInput.Create(ctx); err != nil { + return fmt.Errorf("Error creating Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) } + log.Printf("[DEBUG] Created Blob %q in Container %q within Storage Account %q.", name, containerName, accountName) d.SetId(id) - return resourceArmStorageBlobRead(d, meta) -} -type resourceArmStorageBlobPage struct { - offset int64 - section *io.SectionReader + return resourceArmStorageBlobUpdate(d, meta) } -func resourceArmStorageBlobPageUploadFromSource(container, name, source, contentType string, client *storage.BlobStorageClient, parallelism, attempts int) error { - workerCount := parallelism * runtime.NumCPU() - - file, err := os.Open(source) - if err != nil { - return fmt.Errorf("Error opening source file for upload %q: %s", source, err) - } - defer utils.IoCloseAndLogError(file, fmt.Sprintf("Error closing Storage Blob `%s` file `%s` after upload", name, source)) - - blobSize, pageList, err := resourceArmStorageBlobPageSplit(file) - if err != nil { - return fmt.Errorf("Error splitting source file %q into pages: %s", source, err) - } - - options := &storage.PutBlobOptions{} - containerRef := client.GetContainerReference(container) - blob := containerRef.GetBlobReference(name) - blob.Properties.ContentLength = blobSize - blob.Properties.ContentType = contentType - err = blob.PutPageBlob(options) - if err != nil { - return fmt.Errorf("Error creating storage blob on Azure: %s", err) - } - - pages := make(chan resourceArmStorageBlobPage, len(pageList)) - errors := make(chan error, len(pageList)) - wg := &sync.WaitGroup{} - wg.Add(len(pageList)) - - total := int64(0) - for _, page := range pageList { - total += page.section.Size() - pages <- page - } - close(pages) - - for i := 0; i < workerCount; i++ { - go resourceArmStorageBlobPageUploadWorker(resourceArmStorageBlobPageUploadContext{ - container: container, - name: name, - source: source, - blobSize: blobSize, - client: client, - pages: pages, - errors: errors, - wg: wg, - attempts: attempts, - }) - } - - wg.Wait() - - if len(errors) > 0 { - return fmt.Errorf("Error while uploading source file %q: %s", source, <-errors) - } - - return nil -} - -func resourceArmStorageBlobPageSplit(file *os.File) (int64, []resourceArmStorageBlobPage, error) { - const ( - minPageSize int64 = 4 * 1024 - maxPageSize int64 = 4 * 1024 * 1024 - ) - - info, err := file.Stat() - if err != nil { - return int64(0), nil, fmt.Errorf("Could not stat file %q: %s", file.Name(), err) - } - - blobSize := info.Size() - if info.Size()%minPageSize != 0 { - blobSize = info.Size() + (minPageSize - (info.Size() % minPageSize)) - } - - emptyPage := make([]byte, minPageSize) - - type byteRange struct { - offset int64 - length int64 - } - - var nonEmptyRanges []byteRange - var currentRange byteRange - for i := int64(0); i < blobSize; i += minPageSize { - pageBuf := make([]byte, minPageSize) - _, err = file.ReadAt(pageBuf, i) - if err != nil && err != io.EOF { - return int64(0), nil, fmt.Errorf("Could not read chunk at %d: %s", i, err) - } - - if bytes.Equal(pageBuf, emptyPage) { - if currentRange.length != 0 { - nonEmptyRanges = append(nonEmptyRanges, currentRange) - } - currentRange = byteRange{ - offset: i + minPageSize, - } - } else { - currentRange.length += minPageSize - if currentRange.length == maxPageSize || (currentRange.offset+currentRange.length == blobSize) { - nonEmptyRanges = append(nonEmptyRanges, currentRange) - currentRange = byteRange{ - offset: i + minPageSize, - } - } - } - } - - var pages []resourceArmStorageBlobPage - for _, nonEmptyRange := range nonEmptyRanges { - pages = append(pages, resourceArmStorageBlobPage{ - offset: nonEmptyRange.offset, - section: io.NewSectionReader(file, nonEmptyRange.offset, nonEmptyRange.length), - }) - } - - return info.Size(), pages, nil -} - -type resourceArmStorageBlobPageUploadContext struct { - container string - name string - source string - blobSize int64 - client *storage.BlobStorageClient - pages chan resourceArmStorageBlobPage - errors chan error - wg *sync.WaitGroup - attempts int -} - -func resourceArmStorageBlobPageUploadWorker(ctx resourceArmStorageBlobPageUploadContext) { - for page := range ctx.pages { - start := page.offset - end := page.offset + page.section.Size() - 1 - if end > ctx.blobSize-1 { - end = ctx.blobSize - 1 - } - size := end - start + 1 - - chunk := make([]byte, size) - _, err := page.section.Read(chunk) - if err != nil && err != io.EOF { - ctx.errors <- fmt.Errorf("Error reading source file %q at offset %d: %s", ctx.source, page.offset, err) - ctx.wg.Done() - continue - } - - for x := 0; x < ctx.attempts; x++ { - container := ctx.client.GetContainerReference(ctx.container) - blob := container.GetBlobReference(ctx.name) - blobRange := storage.BlobRange{ - Start: uint64(start), - End: uint64(end), - } - options := &storage.PutPageOptions{} - reader := bytes.NewReader(chunk) - err = blob.WriteRange(blobRange, reader, options) - if err == nil { - break - } - } - if err != nil { - ctx.errors <- fmt.Errorf("Error writing page at offset %d for file %q: %s", page.offset, ctx.source, err) - ctx.wg.Done() - continue - } - - ctx.wg.Done() - } -} - -type resourceArmStorageBlobBlock struct { - section *io.SectionReader - id string -} - -func resourceArmStorageBlobBlockUploadFromSource(container, name, source, contentType string, client *storage.BlobStorageClient, parallelism, attempts int) error { - workerCount := parallelism * runtime.NumCPU() +func resourceArmStorageBlobUpdate(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - file, err := os.Open(source) + id, err := blobs.ParseResourceID(d.Id()) if err != nil { - return fmt.Errorf("Error opening source file for upload %q: %s", source, err) + return fmt.Errorf("Error parsing %q: %s", d.Id(), err) } - defer utils.IoCloseAndLogError(file, fmt.Sprintf("Error closing Storage Blob `%s` file `%s` after upload", name, source)) - blockList, parts, err := resourceArmStorageBlobBlockSplit(file) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return fmt.Errorf("Error reading and splitting source file for upload %q: %s", source, err) - } - - wg := &sync.WaitGroup{} - blocks := make(chan resourceArmStorageBlobBlock, len(parts)) - errors := make(chan error, len(parts)) - - wg.Add(len(parts)) - for _, p := range parts { - blocks <- p - } - close(blocks) - - for i := 0; i < workerCount; i++ { - go resourceArmStorageBlobBlockUploadWorker(resourceArmStorageBlobBlockUploadContext{ - client: client, - source: source, - container: container, - name: name, - blocks: blocks, - errors: errors, - wg: wg, - attempts: attempts, - }) - } - - wg.Wait() - - if len(errors) > 0 { - return fmt.Errorf("Error while uploading source file %q: %s", source, <-errors) + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) } - - containerReference := client.GetContainerReference(container) - blobReference := containerReference.GetBlobReference(name) - blobReference.Properties.ContentType = contentType - options := &storage.PutBlockListOptions{} - err = blobReference.PutBlockList(blockList, options) - if err != nil { - return fmt.Errorf("Error updating block list for source file %q: %s", source, err) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Blob %q (Container %q / Account %q)", id.BlobName, id.ContainerName, id.AccountName) } - return nil -} - -func resourceArmStorageBlobBlockSplit(file *os.File) ([]storage.Block, []resourceArmStorageBlobBlock, error) { - const ( - idSize = 64 - blockSize int64 = 4 * 1024 * 1024 - ) - var parts []resourceArmStorageBlobBlock - var blockList []storage.Block - - info, err := file.Stat() + blobsClient, err := storageClient.BlobsClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return nil, nil, fmt.Errorf("Error stating source file %q: %s", file.Name(), err) + return fmt.Errorf("Error building Blobs Client: %s", err) } - for i := int64(0); i < info.Size(); i = i + blockSize { - entropy := make([]byte, idSize) - _, err = rand.Read(entropy) - if err != nil { - return nil, nil, fmt.Errorf("Error generating a random block ID for source file %q: %s", file.Name(), err) - } + if d.HasChange("access_tier") { + // this is only applicable for Gen2/BlobStorage accounts + log.Printf("[DEBUG] Updating Access Tier for Blob %q (Container %q / Account %q)...", id.BlobName, id.ContainerName, id.AccountName) + accessTier := blobs.AccessTier(d.Get("access_tier").(string)) - sectionSize := blockSize - remainder := info.Size() - i - if remainder < blockSize { - sectionSize = remainder + if _, err := blobsClient.SetTier(ctx, id.AccountName, id.ContainerName, id.BlobName, accessTier); err != nil { + return fmt.Errorf("Error updating Access Tier for Blob %q (Container %q / Account %q): %s", id.BlobName, id.ContainerName, id.AccountName, err) } - block := storage.Block{ - ID: base64.StdEncoding.EncodeToString(entropy), - Status: storage.BlockStatusUncommitted, - } - - blockList = append(blockList, block) - - parts = append(parts, resourceArmStorageBlobBlock{ - id: block.ID, - section: io.NewSectionReader(file, i, sectionSize), - }) + log.Printf("[DEBUG] Updated Access Tier for Blob %q (Container %q / Account %q).", id.BlobName, id.ContainerName, id.AccountName) } - return blockList, parts, nil -} - -type resourceArmStorageBlobBlockUploadContext struct { - client *storage.BlobStorageClient - container string - name string - source string - attempts int - blocks chan resourceArmStorageBlobBlock - errors chan error - wg *sync.WaitGroup -} - -func resourceArmStorageBlobBlockUploadWorker(ctx resourceArmStorageBlobBlockUploadContext) { - for block := range ctx.blocks { - buffer := make([]byte, block.section.Size()) - - _, err := block.section.Read(buffer) - if err != nil { - ctx.errors <- fmt.Errorf("Error reading source file %q: %s", ctx.source, err) - ctx.wg.Done() - continue - } - - for i := 0; i < ctx.attempts; i++ { - container := ctx.client.GetContainerReference(ctx.container) - blob := container.GetBlobReference(ctx.name) - options := &storage.PutBlockOptions{} - if err = blob.PutBlock(block.id, buffer, options); err == nil { - break - } + if d.HasChange("content_type") { + log.Printf("[DEBUG] Updating Properties for Blob %q (Container %q / Account %q)...", id.BlobName, id.ContainerName, id.AccountName) + input := blobs.SetPropertiesInput{ + ContentType: utils.String(d.Get("content_type").(string)), } - if err != nil { - ctx.errors <- fmt.Errorf("Error uploading block %q for source file %q: %s", block.id, ctx.source, err) - ctx.wg.Done() - continue + if _, err := blobsClient.SetProperties(ctx, id.AccountName, id.ContainerName, id.BlobName, input); err != nil { + return fmt.Errorf("Error updating Properties for Blob %q (Container %q / Account %q): %s", id.BlobName, id.ContainerName, id.AccountName, err) } - - ctx.wg.Done() - } -} - -func resourceArmStorageBlobUpdate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext - - id, err := parseStorageBlobID(d.Id(), armClient.environment) - if err != nil { - return err - } - - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) - if err != nil { - return err - } - - if resourceGroup == nil { - return fmt.Errorf("Unable to determine Resource Group for Storage Account %q", id.storageAccountName) - } - - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) - if err != nil { - return fmt.Errorf("Error getting storage account %s: %+v", id.storageAccountName, err) - } - if !accountExists { - return fmt.Errorf("Storage account %s not found in resource group %s", id.storageAccountName, *resourceGroup) - } - - container := blobClient.GetContainerReference(id.containerName) - blob := container.GetBlobReference(id.blobName) - - if d.HasChange("content_type") { - blob.Properties.ContentType = d.Get("content_type").(string) - } - - options := &storage.SetBlobPropertiesOptions{} - err = blob.SetProperties(options) - if err != nil { - return fmt.Errorf("Error setting properties of blob %s (container %s, storage account %s): %+v", id.blobName, id.containerName, id.storageAccountName, err) + log.Printf("[DEBUG] Updated Properties for Blob %q (Container %q / Account %q).", id.BlobName, id.ContainerName, id.AccountName) } if d.HasChange("metadata") { - blob.Metadata = expandStorageAccountBlobMetadata(d) - - opts := &storage.SetBlobMetadataOptions{} - if err := blob.SetMetadata(opts); err != nil { - return fmt.Errorf("Error setting metadata for storage blob on Azure: %s", err) + log.Printf("[DEBUG] Updating MetaData for Blob %q (Container %q / Account %q)...", id.BlobName, id.ContainerName, id.AccountName) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + input := blobs.SetMetaDataInput{ + MetaData: storage.ExpandMetaData(metaDataRaw), + } + if _, err := blobsClient.SetMetaData(ctx, id.AccountName, id.ContainerName, id.BlobName, input); err != nil { + return fmt.Errorf("Error updating MetaData for Blob %q (Container %q / Account %q): %s", id.BlobName, id.ContainerName, id.AccountName, err) } + log.Printf("[DEBUG] Updated MetaData for Blob %q (Container %q / Account %q).", id.BlobName, id.ContainerName, id.AccountName) } - return nil + return resourceArmStorageBlobRead(d, meta) } func resourceArmStorageBlobRead(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageBlobID(d.Id(), armClient.environment) + id, err := blobs.ParseResourceID(d.Id()) if err != nil { - return err + return fmt.Errorf("Error parsing %q: %s", d.Id(), err) } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) } - if resourceGroup == nil { - return fmt.Errorf("Unable to determine Resource Group for Storage Account %q", id.storageAccountName) - } - - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) - if err != nil { - return err - } - if !accountExists { - log.Printf("[DEBUG] Storage account %q not found, removing blob %q from state", id.storageAccountName, d.Id()) + log.Printf("[DEBUG] Unable to locate Resource Group for Blob %q (Container %q / Account %q) - assuming removed & removing from state!", id.BlobName, id.ContainerName, id.AccountName) d.SetId("") return nil } - log.Printf("[INFO] Checking for existence of storage blob %q in container %q.", id.blobName, id.containerName) - container := blobClient.GetContainerReference(id.containerName) - blob := container.GetBlobReference(id.blobName) - exists, err := blob.Exists() + blobsClient, err := storageClient.BlobsClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return fmt.Errorf("error checking for existence of storage blob %q: %s", id.blobName, err) + return fmt.Errorf("Error building Blobs Client: %s", err) } - if !exists { - log.Printf("[INFO] Storage blob %q no longer exists, removing from state...", id.blobName) - d.SetId("") - return nil - } - - options := &storage.GetBlobPropertiesOptions{} - err = blob.GetProperties(options) + log.Printf("[INFO] Retrieving Storage Blob %q (Container %q / Account %q).", id.BlobName, id.ContainerName, id.AccountName) + input := blobs.GetPropertiesInput{} + props, err := blobsClient.GetProperties(ctx, id.AccountName, id.ContainerName, id.BlobName, input) if err != nil { - return fmt.Errorf("Error getting properties of blob %s (container %s, storage account %s): %+v", id.blobName, id.containerName, id.storageAccountName, err) - } + if utils.ResponseWasNotFound(props.Response) { + log.Printf("[INFO] Blob %q was not found in Container %q / Account %q - assuming removed & removing from state...", id.BlobName, id.ContainerName, id.AccountName) + d.SetId("") + return nil + } - metadataOptions := &storage.GetBlobMetadataOptions{} - err = blob.GetMetadata(metadataOptions) - if err != nil { - return fmt.Errorf("Error getting metadata of blob %s (container %s, storage account %s): %+v", id.blobName, id.containerName, id.storageAccountName, err) + return fmt.Errorf("Error retrieving properties for Blob %q (Container %q / Account %q): %s", id.BlobName, id.ContainerName, id.AccountName, err) } - d.Set("name", id.blobName) - d.Set("storage_container_name", id.containerName) - d.Set("storage_account_name", id.storageAccountName) + d.Set("name", id.BlobName) + d.Set("storage_container_name", id.ContainerName) + d.Set("storage_account_name", id.AccountName) d.Set("resource_group_name", resourceGroup) - d.Set("content_type", blob.Properties.ContentType) - - d.Set("source_uri", blob.Properties.CopySource) - - blobType := strings.ToLower(strings.Replace(string(blob.Properties.BlobType), "Blob", "", 1)) - d.Set("type", blobType) + d.Set("access_tier", string(props.AccessTier)) + d.Set("content_type", props.ContentType) + d.Set("type", strings.TrimSuffix(string(props.BlobType), "Blob")) + d.Set("url", d.Id()) - u := blob.GetURL() - if u == "" { - log.Printf("[INFO] URL for %q is empty", id.blobName) + if err := d.Set("metadata", storage.FlattenMetaData(props.MetaData)); err != nil { + return fmt.Errorf("Error setting `metadata`: %+v", err) + } + // The CopySource is only returned if the blob hasn't been modified (e.g. metadata configured etc) + // as such, we need to conditionally set this to ensure it's trackable if possible + if props.CopySource != "" { + d.Set("source_uri", props.CopySource) } - d.Set("url", u) - d.Set("metadata", flattenStorageAccountBlobMetadata(blob.Metadata)) return nil } func resourceArmStorageBlobDelete(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageBlobID(d.Id(), armClient.environment) + id, err := blobs.ParseResourceID(d.Id()) if err != nil { - return err + return fmt.Errorf("Error parsing %q: %s", d.Id(), err) } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return fmt.Errorf("Unable to determine Resource Group for Storage Account %q: %+v", id.storageAccountName, err) + return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) } if resourceGroup == nil { - log.Printf("[INFO] Resource Group doesn't exist so the blob won't exist") - return nil + return fmt.Errorf("Unable to locate Resource Group for Storage Account %q!", id.AccountName) } - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + blobsClient, err := storageClient.BlobsClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return err - } - if !accountExists { - log.Printf("[INFO] Storage Account %q doesn't exist so the blob won't exist", id.storageAccountName) - return nil + return fmt.Errorf("Error building Blobs Client: %s", err) } - log.Printf("[INFO] Deleting storage blob %q", id.blobName) - options := &storage.DeleteBlobOptions{} - container := blobClient.GetContainerReference(id.containerName) - blob := container.GetBlobReference(id.blobName) - _, err = blob.DeleteIfExists(options) - if err != nil { - return fmt.Errorf("Error deleting storage blob %q: %s", id.blobName, err) + log.Printf("[INFO] Deleting Blob %q from Container %q / Storage Account %q", id.BlobName, id.ContainerName, id.AccountName) + input := blobs.DeleteInput{ + DeleteSnapshots: true, } - - return nil -} - -type storageBlobId struct { - storageAccountName string - containerName string - blobName string -} - -func parseStorageBlobID(input string, environment azauto.Environment) (*storageBlobId, error) { - uri, err := url.Parse(input) - if err != nil { - return nil, fmt.Errorf("Error parsing %q as URI: %+v", input, err) - } - - // trim the leading `/` - segments := strings.Split(strings.TrimPrefix(uri.Path, "/"), "/") - if len(segments) < 2 { - return nil, fmt.Errorf("Expected number of segments in the path to be < 2 but got %d", len(segments)) - } - - storageAccountName := strings.Replace(uri.Host, fmt.Sprintf(".blob.%s", environment.StorageEndpointSuffix), "", 1) - containerName := segments[0] - blobName := strings.TrimPrefix(uri.Path, fmt.Sprintf("/%s/", containerName)) - - id := storageBlobId{ - storageAccountName: storageAccountName, - containerName: containerName, - blobName: blobName, + if _, err := blobsClient.Delete(ctx, id.AccountName, id.ContainerName, id.BlobName, input); err != nil { + return fmt.Errorf("Error deleting Blob %q (Container %q / Account %q): %s", id.BlobName, id.ContainerName, id.AccountName, err) } - return &id, nil -} - -func determineResourceGroupForStorageAccount(accountName string, client *ArmClient) (*string, error) { - storageClient := client.storageServiceClient - ctx := client.StopContext - // first locate which resource group the storage account is in - groupsResp, err := storageClient.List(ctx) - if err != nil { - return nil, fmt.Errorf("Error loading the Resource Groups for Storage Account %q: %+v", accountName, err) - } - - if groups := groupsResp.Value; groups != nil { - for _, group := range *groups { - if group.Name != nil && *group.Name == accountName { - groupId, err := parseAzureResourceID(*group.ID) - if err != nil { - return nil, err - } - - return &groupId.ResourceGroup, nil - } - } - } - - return nil, nil -} - -func expandStorageAccountBlobMetadata(d *schema.ResourceData) storage.BlobMetadata { - blobMetadata := make(map[string]string) - - blobMetadataRaw := d.Get("metadata").(map[string]interface{}) - for key, value := range blobMetadataRaw { - blobMetadata[key] = value.(string) - } - return storage.BlobMetadata(blobMetadata) -} - -func flattenStorageAccountBlobMetadata(in storage.BlobMetadata) map[string]interface{} { - blobMetadata := make(map[string]interface{}) - - for key, value := range in { - blobMetadata[key] = value - } - - return blobMetadata + return nil } diff --git a/azurerm/resource_arm_storage_blob_migration_test.go b/azurerm/resource_arm_storage_blob_migration_test.go index bb21ddc25578..273adb66339e 100644 --- a/azurerm/resource_arm_storage_blob_migration_test.go +++ b/azurerm/resource_arm_storage_blob_migration_test.go @@ -16,7 +16,7 @@ func TestAccAzureRMStorageBlobMigrateState(t *testing.T) { return } - client, err := getArmClient(config, false, "") + client, err := getArmClient(config, false, "0.0.0", "", true) if err != nil { t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) return diff --git a/azurerm/resource_arm_storage_blob_test.go b/azurerm/resource_arm_storage_blob_test.go index cf2746c3c239..9fc83584287d 100644 --- a/azurerm/resource_arm_storage_blob_test.go +++ b/azurerm/resource_arm_storage_blob_test.go @@ -3,20 +3,44 @@ package azurerm import ( "crypto/rand" "fmt" - "io" "io/ioutil" - "testing" - + "os" "strings" + "testing" - "github.com/Azure/azure-sdk-for-go/storage" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" ) -func TestAccAzureRMStorageBlob_basic(t *testing.T) { +func TestAccAzureRMStorageBlob_disappears(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockEmpty(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + testCheckAzureRMStorageBlobDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_appendEmpty(t *testing.T) { resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) @@ -28,12 +52,9 @@ func TestAccAzureRMStorageBlob_basic(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMStorageBlob_basic(ri, rs, location), + Config: testAccAzureRMStorageBlob_appendEmpty(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageBlobExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "metadata.%", "2"), - resource.TestCheckResourceAttr(resourceName, "metadata.test", "value1"), - resource.TestCheckResourceAttr(resourceName, "metadata.test2", "value2"), ), }, { @@ -45,12 +66,8 @@ func TestAccAzureRMStorageBlob_basic(t *testing.T) { }, }) } -func TestAccAzureRMStorageBlob_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { - t.Skip("Skipping since resources aren't required to be imported") - return - } +func TestAccAzureRMStorageBlob_appendEmptyMetaData(t *testing.T) { resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) @@ -62,24 +79,26 @@ func TestAccAzureRMStorageBlob_requiresImport(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMStorageBlob_basic(ri, rs, location), + Config: testAccAzureRMStorageBlob_appendEmptyMetaData(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageBlobExists(resourceName), ), }, { - Config: testAccAzureRMStorageBlob_requiresImport(ri, rs, location), - ExpectError: testRequiresImportError("azurerm_storage_blob"), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, }, }, }) } -func TestAccAzureRMStorageBlob_disappears(t *testing.T) { +func TestAccAzureRMStorageBlob_blockEmpty(t *testing.T) { resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageBlob_basic(ri, rs, testLocation()) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -87,36 +106,26 @@ func TestAccAzureRMStorageBlob_disappears(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageBlob_blockEmpty(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageBlobExists(resourceName), - testCheckAzureRMStorageBlobDisappears(resourceName), ), - ExpectNonEmptyPlan: true, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, }, }, }) } -func TestAccAzureRMStorageBlobBlock_source(t *testing.T) { +func TestAccAzureRMStorageBlob_blockEmptyMetaData(t *testing.T) { + resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() - rs1 := strings.ToLower(acctest.RandString(11)) - sourceBlob, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("Failed to create local source blob file") - } - - _, err = io.CopyN(sourceBlob, rand.Reader, 25*1024*1024) - if err != nil { - t.Fatalf("Failed to write random test to source blob") - } - - err = sourceBlob.Close() - if err != nil { - t.Fatalf("Failed to close source blob") - } - - config := testAccAzureRMStorageBlobBlock_source(ri, rs1, sourceBlob.Name(), testLocation()) + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -124,59 +133,95 @@ func TestAccAzureRMStorageBlobBlock_source(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageBlob_blockEmptyMetaData(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageBlobMatchesFile("azurerm_storage_blob.source", storage.BlobTypeBlock, sourceBlob.Name()), + testCheckAzureRMStorageBlobExists(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, }, }) } -func TestAccAzureRMStorageBlobPage_source(t *testing.T) { - resourceName := "azurerm_storage_blob.source" +func TestAccAzureRMStorageBlob_blockEmptyAccessTier(t *testing.T) { + resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - sourceBlob, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("Failed to create local source blob file") - } - - err = sourceBlob.Truncate(25*1024*1024 + 512) - if err != nil { - t.Fatalf("Failed to truncate file to 25M") - } - - for i := int64(0); i < 20; i = i + 2 { - randomBytes := make([]byte, 1*1024*1024) - _, err = rand.Read(randomBytes) - if err != nil { - t.Fatalf("Failed to read random bytes") - } - - _, err = sourceBlob.WriteAt(randomBytes, i*1024*1024) - if err != nil { - t.Fatalf("Failed to write random bytes to file") - } - } + location := testLocation() - randomBytes := make([]byte, 5*1024*1024) - _, err = rand.Read(randomBytes) - if err != nil { - t.Fatalf("Failed to read random bytes") - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockEmptyAccessTier(ri, rs, location, blobs.Cool), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "access_tier", "Cool"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + { + Config: testAccAzureRMStorageBlob_blockEmptyAccessTier(ri, rs, location, blobs.Hot), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "access_tier", "Hot"), + ), + }, + { + Config: testAccAzureRMStorageBlob_blockEmptyAccessTier(ri, rs, location, blobs.Cool), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "access_tier", "Cool"), + ), + }, + }, + }) +} - _, err = sourceBlob.WriteAt(randomBytes, 20*1024*1024) - if err != nil { - t.Fatalf("Failed to write random bytes to file") - } +func TestAccAzureRMStorageBlob_blockFromInlineContent(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() - err = sourceBlob.Close() - if err != nil { - t.Fatalf("Failed to close source blob") - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockFromInlineContent(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_content", "type"}, + }, + }, + }) +} - config := testAccAzureRMStorageBlobPage_source(ri, rs, sourceBlob.Name(), testLocation()) +func TestAccAzureRMStorageBlob_blockFromPublicBlob(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -184,35 +229,53 @@ func TestAccAzureRMStorageBlobPage_source(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageBlob_blockFromPublicBlob(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageBlobMatchesFile(resourceName, storage.BlobTypePage, sourceBlob.Name()), + testCheckAzureRMStorageBlobExists(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_uri", "type"}, + }, }, }) } -func TestAccAzureRMStorageBlob_source_uri(t *testing.T) { - resourceName := "azurerm_storage_blob.destination" +func TestAccAzureRMStorageBlob_blockFromPublicFile(t *testing.T) { + resourceName := "azurerm_storage_blob.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - sourceBlob, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("Failed to create local source blob file") - } - - _, err = io.CopyN(sourceBlob, rand.Reader, 25*1024*1024) - if err != nil { - t.Fatalf("Failed to write random test to source blob") - } + location := testLocation() - err = sourceBlob.Close() - if err != nil { - t.Fatalf("Failed to close source blob") - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockFromPublicFile(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_uri", "type"}, + }, + }, + }) +} - config := testAccAzureRMStorageBlob_source_uri(ri, rs, sourceBlob.Name(), testLocation()) +func TestAccAzureRMStorageBlob_blockFromExistingBlob(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -220,42 +283,63 @@ func TestAccAzureRMStorageBlob_source_uri(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageBlob_blockFromExistingBlob(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageBlobMatchesFile(resourceName, storage.BlobTypeBlock, sourceBlob.Name()), + testCheckAzureRMStorageBlobExists(resourceName), ), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_uri", "type"}, }, }, }) } -func TestAccAzureRMStorageBlobBlock_blockContentType(t *testing.T) { - resourceName := "azurerm_storage_blob.source" - ri := tf.AccRandTimeInt() - rs1 := strings.ToLower(acctest.RandString(11)) +func TestAccAzureRMStorageBlob_blockFromLocalFile(t *testing.T) { sourceBlob, err := ioutil.TempFile("", "") if err != nil { t.Fatalf("Failed to create local source blob file") } - _, err = io.CopyN(sourceBlob, rand.Reader, 25*1024*1024) - if err != nil { - t.Fatalf("Failed to write random test to source blob") + if err := testAccAzureRMStorageBlob_populateTempFile(sourceBlob); err != nil { + t.Fatalf("Error populating temp file: %s", err) } - err = sourceBlob.Close() - if err != nil { - t.Fatalf("Failed to close source blob") - } + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockFromLocalBlob(ri, rs, location, sourceBlob.Name()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + testCheckAzureRMStorageBlobMatchesFile(resourceName, blobs.BlockBlob, sourceBlob.Name()), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source", "type"}, + }, + }, + }) +} - config := testAccAzureRMStorageBlobPage_blockContentType(ri, rs1, testLocation(), sourceBlob.Name(), "text/plain") - updateConfig := testAccAzureRMStorageBlobPage_blockContentType(ri, rs1, testLocation(), sourceBlob.Name(), "text/vnd.terraform.acctest.tmpfile") +func TestAccAzureRMStorageBlob_contentType(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -263,248 +347,832 @@ func TestAccAzureRMStorageBlobBlock_blockContentType(t *testing.T) { CheckDestroy: testCheckAzureRMStorageBlobDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageBlob_contentType(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageBlobExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "content_type", "text/plain"), ), }, { - Config: updateConfig, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + { + Config: testAccAzureRMStorageBlob_contentTypeUpdated(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageBlobExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "content_type", "text/vnd.terraform.acctest.tmpfile"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, }, }) } -func testCheckAzureRMStorageBlobExists(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } - - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - storageContainerName := rs.Primary.Attributes["storage_container_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage blob: %s", name) - } - - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) - if err != nil { - return err - } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) - } - - container := blobClient.GetContainerReference(storageContainerName) - blob := container.GetBlobReference(name) - exists, err := blob.Exists() - if err != nil { - return err - } - - if !exists { - return fmt.Errorf("Bad: Storage Blob %q (storage container: %q) does not exist", name, storageContainerName) - } +func TestAccAzureRMStorageBlob_contentTypePremium(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() - return nil + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_contentTypePremium(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_pageEmpty(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_pageEmpty(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_pageEmptyPremium(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_pageEmptyPremium(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_pageEmptyMetaData(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_pageEmptyMetaData(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_pageFromExistingBlob(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_pageFromExistingBlob(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source_uri", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_pageFromLocalFile(t *testing.T) { + sourceBlob, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("Failed to create local source blob file") + } + + if err := testAccAzureRMStorageBlob_populateTempFile(sourceBlob); err != nil { + t.Fatalf("Error populating temp file: %s", err) + } + + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_pageFromLocalBlob(ri, rs, location, sourceBlob.Name()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + testCheckAzureRMStorageBlobMatchesFile(resourceName, blobs.PageBlob, sourceBlob.Name()), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "source", "type"}, + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_blockFromPublicBlob(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + Config: testAccAzureRMStorageBlob_requiresImport(ri, rs, location), + ExpectError: testRequiresImportError("azurerm_storage_blob"), + }, + }, + }) +} + +func TestAccAzureRMStorageBlob_update(t *testing.T) { + resourceName := "azurerm_storage_blob.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageBlobDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageBlob_update(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + { + Config: testAccAzureRMStorageBlob_updateUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageBlobExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"attempts", "parallelism", "size", "type"}, + }, + }, + }) +} + +func testCheckAzureRMStorageBlobExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + name := rs.Primary.Attributes["name"] + containerName := rs.Primary.Attributes["storage_container_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Blob %q (Container %q / Account %q) - assuming removed & removing from state", name, containerName, accountName) + } + + client, err := storageClient.BlobsClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Blobs Client: %s", err) + } + + input := blobs.GetPropertiesInput{} + resp, err := client.GetProperties(ctx, accountName, containerName, name, input) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Blob %q (Container %q / Account %q / Resource Group %q) does not exist", name, containerName, accountName, *resourceGroup) + } + + return fmt.Errorf("Bad: Get on BlobsClient: %+v", err) + } + + return nil } } func testCheckAzureRMStorageBlobDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + name := rs.Primary.Attributes["name"] + containerName := rs.Primary.Attributes["storage_container_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Blob %q (Container %q / Account %q) - assuming removed & removing from state", name, containerName, accountName) + } + + client, err := storageClient.BlobsClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Blobs Client: %s", err) + } + input := blobs.DeleteInput{ + DeleteSnapshots: false, + } + if _, err := client.Delete(ctx, accountName, containerName, name, input); err != nil { + return fmt.Errorf("Error deleting Blob %q (Container %q / Account %q / Resource Group %q): %s", name, containerName, accountName, *resourceGroup, err) + } + + return nil + } +} + +func testCheckAzureRMStorageBlobMatchesFile(resourceName string, kind blobs.BlobType, filePath string) resource.TestCheckFunc { + return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - storageContainerName := rs.Primary.Attributes["storage_container_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage blob: %s", name) + containerName := rs.Primary.Attributes["storage_container_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Blob %q (Container %q / Account %q) - assuming removed & removing from state", name, containerName, accountName) } - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) + client, err := storageClient.BlobsClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Blobs Client: %s", err) + } + + // first check the type + getPropsInput := blobs.GetPropertiesInput{} + props, err := client.GetProperties(ctx, accountName, containerName, name, getPropsInput) + if err != nil { + return fmt.Errorf("Error retrieving Properties for Blob %q (Container %q): %s", name, containerName, err) + } + + if props.BlobType != kind { + return fmt.Errorf("Bad: blob type %q does not match expected type %q", props.BlobType, kind) + } + + // then compare the content itself + getInput := blobs.GetInput{} + actualProps, err := client.Get(ctx, accountName, containerName, name, getInput) + if err != nil { + return fmt.Errorf("Error retrieving Blob %q (Container %q): %s", name, containerName, err) + } + + actualContents := actualProps.Contents + + // local file for comparison + expectedContents, err := ioutil.ReadFile(filePath) if err != nil { return err } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) + + if string(actualContents) != string(expectedContents) { + return fmt.Errorf("Bad: Storage Blob %q (storage container: %q) does not match contents", name, containerName) + } + + return nil + } +} + +func testCheckAzureRMStorageBlobDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_blob" { + continue + } + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + name := rs.Primary.Attributes["name"] + containerName := rs.Primary.Attributes["storage_container_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) + } + if resourceGroup == nil { + return nil + } + + client, err := storageClient.BlobsClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Blobs Client: %s", err) + } + + input := blobs.GetPropertiesInput{} + props, err := client.GetProperties(ctx, accountName, containerName, name, input) + if err != nil { + if !utils.ResponseWasNotFound(props.Response) { + return fmt.Errorf("Error retrieving Blob %q (Container %q / Account %q): %s", name, containerName, accountName, err) + } + } + + if utils.ResponseWasNotFound(props.Response) { + return nil } - container := blobClient.GetContainerReference(storageContainerName) - blob := container.GetBlobReference(name) - options := &storage.DeleteBlobOptions{} - _, err = blob.DeleteIfExists(options) - return err + return fmt.Errorf("Bad: Storage Blob %q (Storage Container: %q) still exists", name, containerName) } + + return nil +} + +func testAccAzureRMStorageBlob_appendEmpty(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "append" +} +`, template) +} + +func testAccAzureRMStorageBlob_appendEmptyMetaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "append" + + metadata = { + hello = "world" + } +} +`, template) +} + +func testAccAzureRMStorageBlob_blockEmpty(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" +} +`, template) +} + +func testAccAzureRMStorageBlob_blockEmptyMetaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + + metadata = { + hello = "world" + } +} +`, template) +} + +func testAccAzureRMStorageBlob_blockEmptyAccessTier(rInt int, rString string, location string, accessTier blobs.AccessTier) string { + template := testAccAzureRMStorageBlob_templateBlockBlobStorage(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + access_tier = "%s" +} +`, template, string(accessTier)) +} + +func testAccAzureRMStorageBlob_blockFromInlineContent(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "blob") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "rick.morty" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source_content = "Wubba Lubba Dub Dub" +} +`, template) +} + +func testAccAzureRMStorageBlob_blockFromPublicBlob(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "blob") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "source" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source_uri = "http://releases.ubuntu.com/18.04.3/ubuntu-18.04.3-desktop-amd64.iso" + content_type = "application/x-iso9660-image" +} + +resource "azurerm_storage_container" "second" { + name = "second" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_storage_blob" "test" { + name = "copied.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.second.name}" + type = "block" + source_uri = "${azurerm_storage_blob.source.id}" + content_type = "${azurerm_storage_blob.source.content_type}" +} +`, template) } -func testCheckAzureRMStorageBlobMatchesFile(resourceName string, kind storage.BlobType, filePath string) resource.TestCheckFunc { - return func(s *terraform.State) error { - - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } +func testAccAzureRMStorageBlob_blockFromPublicFile(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - storageContainerName := rs.Primary.Attributes["storage_container_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage blob: %s", name) - } +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source_uri = "http://releases.ubuntu.com/18.04.3/ubuntu-18.04.3-desktop-amd64.iso" + content_type = "application/x-iso9660-image" +} +`, template) +} - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) - if err != nil { - return err - } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) - } +func testAccAzureRMStorageBlob_blockFromExistingBlob(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - containerReference := blobClient.GetContainerReference(storageContainerName) - blobReference := containerReference.GetBlobReference(name) - propertyOptions := &storage.GetBlobPropertiesOptions{} - err = blobReference.GetProperties(propertyOptions) - if err != nil { - return err - } +resource "azurerm_storage_blob" "source" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source_uri = "http://releases.ubuntu.com/18.04.3/ubuntu-18.04.3-desktop-amd64.iso" + content_type = "application/x-iso9660-image" +} - properties := blobReference.Properties +resource "azurerm_storage_blob" "test" { + name = "copied.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source_uri = "${azurerm_storage_blob.source.id}" + content_type = "${azurerm_storage_blob.source.content_type}" +} +`, template) +} - if properties.BlobType != kind { - return fmt.Errorf("Bad: blob type %q does not match expected type %q", properties.BlobType, kind) - } +func testAccAzureRMStorageBlob_blockFromLocalBlob(rInt int, rString, location, fileName string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - getOptions := &storage.GetBlobOptions{} - blob, err := blobReference.Get(getOptions) - if err != nil { - return err - } +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + source = "%s" +} +`, template, fileName) +} - contents, err := ioutil.ReadAll(blob) - if err != nil { - return err - } - defer blob.Close() +func testAccAzureRMStorageBlob_contentType(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - expectedContents, err := ioutil.ReadFile(filePath) - if err != nil { - return err - } +resource "azurerm_storage_blob" "test" { + name = "example.ext" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 + content_type = "image/png" +} +`, template) +} - if string(contents) != string(expectedContents) { - return fmt.Errorf("Bad: Storage Blob %q (storage container: %q) does not match contents", name, storageContainerName) - } +func testAccAzureRMStorageBlob_contentTypePremium(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_templatePremium(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - return nil - } +resource "azurerm_storage_blob" "test" { + name = "example.ext" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 + content_type = "image/png" +} +`, template) } -func testCheckAzureRMStorageBlobDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_storage_blob" { - continue - } +func testAccAzureRMStorageBlob_contentTypeUpdated(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - storageContainerName := rs.Primary.Attributes["storage_container_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage blob: %s", name) - } +resource "azurerm_storage_blob" "test" { + name = "example.ext" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 + content_type = "image/gif" +} +`, template) +} - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) - if err != nil { - return nil - } - if !accountExists { - return nil - } +func testAccAzureRMStorageBlob_pageEmpty(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - container := blobClient.GetContainerReference(storageContainerName) - blob := container.GetBlobReference(name) - exists, err := blob.Exists() - if err != nil { - return nil - } +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 +} +`, template) +} - if exists { - return fmt.Errorf("Bad: Storage Blob %q (storage container: %q) still exists", name, storageContainerName) - } - } +func testAccAzureRMStorageBlob_pageEmptyPremium(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_templatePremium(rInt, rString, location, "private") + return fmt.Sprintf(` +%s - return nil +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 +} +`, template) } -func testAccAzureRMStorageBlob_basic(rInt int, rString string, location string) string { +func testAccAzureRMStorageBlob_pageEmptyMetaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} +%s -resource "azurerm_storage_account" "test" { - name = "acctestacc%s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 - tags = { - environment = "staging" + metadata = { + hello = "world" } } +`, template) +} -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" +func testAccAzureRMStorageBlob_pageFromExistingBlob(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "source" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + size = 5120 + content_type = "application/x-iso9660-image" } resource "azurerm_storage_blob" "test" { - name = "herpderp1.vhd" - + name = "copied.vhd" resource_group_name = "${azurerm_resource_group.test.name}" storage_account_name = "${azurerm_storage_account.test.name}" storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + source_uri = "${azurerm_storage_blob.source.id}" + content_type = "${azurerm_storage_blob.source.content_type}" +} +`, template) +} - type = "page" - size = 5120 - - metadata = { - test = "value1" - test2 = "value2" - } +func testAccAzureRMStorageBlob_pageFromLocalBlob(rInt int, rString, location, fileName string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s + +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "page" + source = "%s" } -`, rInt, location, rString) +`, template, fileName) +} + +func testAccAzureRMStorageBlob_populateTempFile(input *os.File) error { + if err := input.Truncate(25*1024*1024 + 512); err != nil { + return fmt.Errorf("Failed to truncate file to 25M") + } + + for i := int64(0); i < 20; i = i + 2 { + randomBytes := make([]byte, 1*1024*1024) + if _, err := rand.Read(randomBytes); err != nil { + return fmt.Errorf("Failed to read random bytes") + } + + if _, err := input.WriteAt(randomBytes, i*1024*1024); err != nil { + return fmt.Errorf("Failed to write random bytes to file") + } + } + + randomBytes := make([]byte, 5*1024*1024) + if _, err := rand.Read(randomBytes); err != nil { + return fmt.Errorf("Failed to read random bytes") + } + + if _, err := input.WriteAt(randomBytes, 20*1024*1024); err != nil { + return fmt.Errorf("Failed to write random bytes to file") + } + + if err := input.Close(); err != nil { + return fmt.Errorf("Failed to close source blob") + } + + return nil } -func testAccAzureRMStorageBlob_requiresImport(rInt int, rString string, location string) string { - template := testAccAzureRMStorageBlob_basic(rInt, rString, location) +func testAccAzureRMStorageBlob_requiresImport(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_blockFromPublicBlob(rInt, rString, location) return fmt.Sprintf(` %s @@ -519,176 +1187,116 @@ resource "azurerm_storage_blob" "import" { `, template) } -func testAccAzureRMStorageBlobBlock_source(rInt int, rString string, sourceBlobName string, location string) string { +func testAccAzureRMStorageBlob_update(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "source" { - name = "acctestacc%s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" +%s - tags = { - environment = "staging" +resource "azurerm_storage_blob" "test" { + name = "example.vhd" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + size = 5120 + content_type = "vnd/panda+pops" + metadata = { + hello = "world" } } - -resource "azurerm_storage_container" "source" { - name = "source" - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - container_access_type = "blob" +`, template) } -resource "azurerm_storage_blob" "source" { - name = "source.vhd" +func testAccAzureRMStorageBlob_updateUpdated(rInt int, rString, location string) string { + template := testAccAzureRMStorageBlob_template(rInt, rString, location, "private") + return fmt.Sprintf(` +%s +resource "azurerm_storage_blob" "test" { + name = "example.vhd" resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - storage_container_name = "${azurerm_storage_container.source.name}" - - type = "block" - source = "%s" - parallelism = 4 - attempts = 2 + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + type = "block" + size = 5120 + content_type = "vnd/mountain-mover-3000" + metadata = { + hello = "world" + panda = "pops" + } } -`, rInt, location, rString, sourceBlobName) +`, template) } -func testAccAzureRMStorageBlobPage_source(rInt int, rString string, sourceBlobName string, location string) string { +func testAccAzureRMStorageBlob_template(rInt int, rString, location, accessLevel string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" location = "%s" } -resource "azurerm_storage_account" "source" { +resource "azurerm_storage_account" "test" { name = "acctestacc%s" 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" "source" { - name = "source" +resource "azurerm_storage_container" "test" { + name = "test" resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - container_access_type = "blob" -} - -resource "azurerm_storage_blob" "source" { - name = "source.vhd" - - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - storage_container_name = "${azurerm_storage_container.source.name}" - - type = "page" - source = "%s" - parallelism = 3 - attempts = 3 + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "%s" } -`, rInt, location, rString, sourceBlobName) +`, rInt, location, rString, accessLevel) } -func testAccAzureRMStorageBlob_source_uri(rInt int, rString string, sourceBlobName string, location string) string { +func testAccAzureRMStorageBlob_templateBlockBlobStorage(rInt int, rString, location, accessLevel string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" location = "%s" } -resource "azurerm_storage_account" "source" { +resource "azurerm_storage_account" "test" { name = "acctestacc%s" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" + account_kind = "StorageV2" account_tier = "Standard" account_replication_type = "LRS" - - tags = { - environment = "staging" - } } -resource "azurerm_storage_container" "source" { - name = "source" +resource "azurerm_storage_container" "test" { + name = "test" resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - container_access_type = "blob" -} - -resource "azurerm_storage_blob" "source" { - name = "source.vhd" - - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - storage_container_name = "${azurerm_storage_container.source.name}" - - type = "block" - source = "%s" - parallelism = 4 - attempts = 2 -} - -resource "azurerm_storage_blob" "destination" { - name = "destination.vhd" - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - storage_container_name = "${azurerm_storage_container.source.name}" - source_uri = "${azurerm_storage_blob.source.url}" - type = "block" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "%s" } -`, rInt, location, rString, sourceBlobName) +`, rInt, location, rString, accessLevel) } -func testAccAzureRMStorageBlobPage_blockContentType(rInt int, rString, location string, sourceBlobName, contentType string) string { +func testAccAzureRMStorageBlob_templatePremium(rInt int, rString, location, accessLevel string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" location = "%s" } -resource "azurerm_storage_account" "source" { +resource "azurerm_storage_account" "test" { name = "acctestacc%s" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" + account_tier = "Premium" account_replication_type = "LRS" - - tags = { - environment = "staging" - } } -resource "azurerm_storage_container" "source" { - name = "source" +resource "azurerm_storage_container" "test" { + name = "test" resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - container_access_type = "blob" -} - -resource "azurerm_storage_blob" "source" { - name = "source.vhd" - - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.source.name}" - storage_container_name = "${azurerm_storage_container.source.name}" - - type = "page" - source = "%s" - content_type = "%s" - parallelism = 3 - attempts = 3 + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "%s" } -`, rInt, location, rString, sourceBlobName, contentType) +`, rInt, location, rString, accessLevel) } diff --git a/azurerm/resource_arm_storage_container.go b/azurerm/resource_arm_storage_container.go index 1413c3a62f96..5f779da19213 100644 --- a/azurerm/resource_arm_storage_container.go +++ b/azurerm/resource_arm_storage_container.go @@ -3,25 +3,24 @@ package azurerm import ( "fmt" "log" - "net/url" "regexp" - "strings" - "time" - "github.com/Azure/azure-sdk-for-go/storage" - azauto "github.com/Azure/go-autorest/autorest/azure" - "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" ) func resourceArmStorageContainer() *schema.Resource { return &schema.Resource{ - Create: resourceArmStorageContainerCreateUpdate, + Create: resourceArmStorageContainerCreate, Read: resourceArmStorageContainerRead, Delete: resourceArmStorageContainerDelete, - Update: resourceArmStorageContainerCreateUpdate, + Update: resourceArmStorageContainerUpdate, MigrateState: resourceStorageContainerMigrateState, SchemaVersion: 1, @@ -36,277 +35,289 @@ func resourceArmStorageContainer() *schema.Resource { ForceNew: true, ValidateFunc: validateArmStorageContainerName, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "storage_account_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, }, + "container_access_type": { - Type: schema.TypeString, - Optional: true, - Default: "private", - ValidateFunc: validateArmStorageContainerAccessType, + Type: schema.TypeString, + Optional: true, + Default: "private", + ValidateFunc: validation.StringInSlice([]string{ + string(containers.Blob), + string(containers.Container), + "private", + }, false), }, - "properties": { - Type: schema.TypeMap, + "metadata": storage.MetaDataComputedSchema(), + + // TODO: support for ACL's, Legal Holds and Immutability Policies + "has_immutability_policy": { + Type: schema.TypeBool, Computed: true, }, - }, - } -} -//Following the naming convention as laid out in the docs -func validateArmStorageContainerName(v interface{}, k string) (warnings []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^\$root$|^[0-9a-z-]+$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "only lowercase alphanumeric characters and hyphens allowed in %q: %q", - k, value)) - } - if len(value) < 3 || len(value) > 63 { - errors = append(errors, fmt.Errorf( - "%q must be between 3 and 63 characters: %q", k, value)) - } - if regexp.MustCompile(`^-`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q cannot begin with a hyphen: %q", k, value)) - } - return warnings, errors -} + "has_legal_hold": { + Type: schema.TypeBool, + Computed: true, + }, -func validateArmStorageContainerAccessType(v interface{}, _ string) (warnings []string, errors []error) { - value := strings.ToLower(v.(string)) - validTypes := map[string]struct{}{ - "private": {}, - "blob": {}, - "container": {}, - } + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), - if _, ok := validTypes[value]; !ok { - errors = append(errors, fmt.Errorf("Storage container access type %q is invalid, must be %q, %q or %q", value, "private", "blob", "page")) + "properties": { + Type: schema.TypeMap, + Computed: true, + Deprecated: "This field will be removed in version 2.0 of the Azure Provider", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, } - return warnings, errors } -func resourceArmStorageContainerCreateUpdate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext +func resourceArmStorageContainerCreate(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext + + containerName := d.Get("name").(string) + accountName := d.Get("storage_account_name").(string) + accessLevelRaw := d.Get("container_access_type").(string) + accessLevel := expandStorageContainerAccessLevel(accessLevelRaw) - name := d.Get("name").(string) - resourceGroupName := d.Get("resource_group_name").(string) - storageAccountName := d.Get("storage_account_name").(string) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", containerName, accountName, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Container %q (Account %s)", containerName, accountName) } - var accessType storage.ContainerAccessType - if d.Get("container_access_type").(string) == "private" { - accessType = storage.ContainerAccessType("") - } else { - accessType = storage.ContainerAccessType(d.Get("container_access_type").(string)) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Containers Client: %s", err) } - reference := blobClient.GetContainerReference(name) - id := fmt.Sprintf("https://%s.blob.%s/%s", storageAccountName, armClient.environment.StorageEndpointSuffix, name) - if requireResourcesToBeImported && d.IsNewResource() { - exists, e := reference.Exists() - if e != nil { - return fmt.Errorf("Error checking if Storage Container %q exists (Account %q / Resource Group %q): %s", name, storageAccountName, resourceGroupName, e) + id := client.GetResourceID(accountName, containerName) + if features.ShouldResourcesBeImported() { + existing, err := client.GetProperties(ctx, accountName, containerName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for existence of existing Container %q (Account %q / Resource Group %q): %+v", containerName, accountName, *resourceGroup, err) + } } - if exists { + if !utils.ResponseWasNotFound(existing.Response) { return tf.ImportAsExistsError("azurerm_storage_container", id) } } - log.Printf("[INFO] Creating container %q in storage account %q.", name, storageAccountName) - err = resource.Retry(120*time.Second, checkContainerIsCreated(reference)) - if err != nil { - return fmt.Errorf("Error creating container %q in storage account %q: %s", name, storageAccountName, err) - } - - permissions := storage.ContainerPermissions{ - AccessType: accessType, + log.Printf("[INFO] Creating Container %q in Storage Account %q", containerName, accountName) + input := containers.CreateInput{ + AccessLevel: accessLevel, + MetaData: metaData, } - permissionOptions := &storage.SetContainerPermissionOptions{} - err = reference.SetPermissions(permissions, permissionOptions) - if err != nil { - return fmt.Errorf("Error setting permissions for container %s in storage account %s: %+v", name, storageAccountName, err) + if _, err := client.Create(ctx, accountName, containerName, input); err != nil { + return fmt.Errorf("Error creating Container %q (Account %q / Resource Group %q): %s", containerName, accountName, *resourceGroup, err) } d.SetId(id) return resourceArmStorageContainerRead(d, meta) } -// resourceAzureStorageContainerRead does all the necessary API calls to -// read the status of the storage container off Azure. -func resourceArmStorageContainerRead(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext +func resourceArmStorageContainerUpdate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - id, err := parseStorageContainerID(d.Id(), armClient.environment) + id, err := containers.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", id.ContainerName, id.AccountName, err) } if resourceGroup == nil { - log.Printf("Cannot locate Resource Group for Storage Account %q (presuming it's gone) - removing from state", id.storageAccountName) + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Container %q (Account %s) - assuming removed & removing from state", id.ContainerName, id.AccountName) d.SetId("") return nil } - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return err - } - if !accountExists { - log.Printf("[DEBUG] Storage account %q not found, removing container %q from state", id.storageAccountName, d.Id()) - d.SetId("") - return nil + return fmt.Errorf("Error building Containers Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) } - var container *storage.Container - listParams := storage.ListContainersParameters{ - Prefix: id.containerName, - Timeout: 90, - } + if d.HasChange("container_access_type") { + log.Printf("[DEBUG] Updating the Access Control for Container %q (Storage Account %q / Resource Group %q)..", id.ContainerName, id.AccountName, *resourceGroup) + accessLevelRaw := d.Get("container_access_type").(string) + accessLevel := expandStorageContainerAccessLevel(accessLevelRaw) - for { - resp, err := blobClient.ListContainers(listParams) - if err != nil { - return fmt.Errorf("Failed to retrieve storage resp in account %q: %s", id.containerName, err) + if _, err := client.SetAccessControl(ctx, id.AccountName, id.ContainerName, accessLevel); err != nil { + return fmt.Errorf("Error updating the Access Control for Container %q (Storage Account %q / Resource Group %q): %s", id.ContainerName, id.AccountName, *resourceGroup, err) } + log.Printf("[DEBUG] Updated the Access Control for Container %q (Storage Account %q / Resource Group %q)", id.ContainerName, id.AccountName, *resourceGroup) + } - for _, c := range resp.Containers { - if c.Name == id.containerName { - container = &c - break - } - } + if d.HasChange("metadata") { + log.Printf("[DEBUG] Updating the MetaData for Container %q (Storage Account %q / Resource Group %q)..", id.ContainerName, id.AccountName, *resourceGroup) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) - if resp.NextMarker == "" { - break + if _, err := client.SetMetaData(ctx, id.AccountName, id.ContainerName, metaData); err != nil { + return fmt.Errorf("Error updating the MetaData for Container %q (Storage Account %q / Resource Group %q): %s", id.ContainerName, id.AccountName, *resourceGroup, err) } + log.Printf("[DEBUG] Updated the MetaData for Container %q (Storage Account %q / Resource Group %q)", id.ContainerName, id.AccountName, *resourceGroup) + } + + return resourceArmStorageContainerRead(d, meta) +} + +func resourceArmStorageContainerRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - listParams.Marker = resp.NextMarker + id, err := containers.ParseResourceID(d.Id()) + if err != nil { + return err } - if container == nil { - log.Printf("[INFO] Storage container %q does not exist in account %q, removing from state...", id.containerName, id.storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", id.ContainerName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Container %q (Account %s) - assuming removed & removing from state", id.ContainerName, id.AccountName) d.SetId("") return nil } - d.Set("name", id.containerName) - d.Set("storage_account_name", id.storageAccountName) - d.Set("resource_group_name", resourceGroup) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Containers Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } - // for historical reasons, "private" above is an empty string in the API - if container.Properties.PublicAccess == storage.ContainerAccessTypePrivate { - d.Set("container_access_type", "private") - } else { - d.Set("container_access_type", string(container.Properties.PublicAccess)) + props, err := client.GetProperties(ctx, id.AccountName, id.ContainerName) + if err != nil { + if utils.ResponseWasNotFound(props.Response) { + log.Printf("[DEBUG] Container %q was not found in Account %q / Resource Group %q - assuming removed & removing from state", id.ContainerName, id.AccountName, *resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Container %q (Account %q / Resource Group %q): %s", id.ContainerName, id.AccountName, *resourceGroup, err) } - output := make(map[string]interface{}) + d.Set("name", id.ContainerName) + d.Set("storage_account_name", id.AccountName) + d.Set("resource_group_name", resourceGroup) + + d.Set("container_access_type", flattenStorageContainerAccessLevel(props.AccessLevel)) - output["last_modified"] = container.Properties.LastModified - output["lease_status"] = container.Properties.LeaseStatus - output["lease_state"] = container.Properties.LeaseState - output["lease_duration"] = container.Properties.LeaseDuration + if err := d.Set("metadata", storage.FlattenMetaData(props.MetaData)); err != nil { + return fmt.Errorf("Error setting `metadata`: %+v", err) + } - if err := d.Set("properties", output); err != nil { + if err := d.Set("properties", flattenStorageContainerProperties(props)); err != nil { return fmt.Errorf("Error setting `properties`: %+v", err) } + d.Set("has_immutability_policy", props.HasImmutabilityPolicy) + d.Set("has_legal_hold", props.HasLegalHold) + return nil } -// resourceAzureStorageContainerDelete does all the necessary API calls to -// delete a storage container off Azure. func resourceArmStorageContainerDelete(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - id, err := parseStorageContainerID(d.Id(), armClient.environment) + id, err := containers.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", id.ContainerName, id.AccountName, err) } if resourceGroup == nil { - log.Printf("Cannot locate Resource Group for Storage Account %q (presuming it's gone) - removing from state", id.storageAccountName) + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Container %q (Account %s) - assuming removed & removing from state", id.ContainerName, id.AccountName) + d.SetId("") return nil } - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return err - } - if !accountExists { - log.Printf("[INFO] Storage Account %q doesn't exist so the container won't exist", id.storageAccountName) - return nil + return fmt.Errorf("Error building Containers Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) } - log.Printf("[INFO] Deleting storage container %q in account %q", id.containerName, id.storageAccountName) - reference := blobClient.GetContainerReference(id.containerName) - deleteOptions := &storage.DeleteContainerOptions{} - if _, err := reference.DeleteIfExists(deleteOptions); err != nil { - return fmt.Errorf("Error deleting storage container %q from storage account %q: %s", id.containerName, id.storageAccountName, err) + if _, err := client.Delete(ctx, id.AccountName, id.ContainerName); err != nil { + return fmt.Errorf("Error deleting Container %q (Storage Account %q / Resource Group %q): %s", id.ContainerName, id.AccountName, *resourceGroup, err) } return nil } -func checkContainerIsCreated(reference *storage.Container) func() *resource.RetryError { - return func() *resource.RetryError { - createOptions := &storage.CreateContainerOptions{} - - if _, err := reference.CreateIfNotExists(createOptions); err != nil { - return resource.RetryableError(err) - } +func flattenStorageContainerProperties(input containers.ContainerProperties) map[string]interface{} { + output := map[string]interface{}{ + "last_modified": input.Header.Get("Last-Modified"), + "lease_duration": "", + "lease_state": string(input.LeaseState), + "lease_status": string(input.LeaseStatus), + } - return nil + if input.LeaseDuration != nil { + output["lease_duration"] = string(*input.LeaseDuration) } -} -type storageContainerId struct { - storageAccountName string - containerName string + return output } -func parseStorageContainerID(input string, environment azauto.Environment) (*storageContainerId, error) { - uri, err := url.Parse(input) - if err != nil { - return nil, fmt.Errorf("Error parsing %q as URI: %+v", input, err) +func expandStorageContainerAccessLevel(input string) containers.AccessLevel { + // for historical reasons, "private" above is an empty string in the API + // so the enum doesn't 1:1 match. You could argue the SDK should handle this + // but this is suitable for now + if input == "private" { + return containers.Private } - // remove the leading `/` - segments := strings.Split(strings.TrimPrefix(uri.Path, "/"), "/") - if len(segments) < 1 { - return nil, fmt.Errorf("Expected number of segments in the path to be < 1 but got %d", len(segments)) + return containers.AccessLevel(input) +} + +func flattenStorageContainerAccessLevel(input containers.AccessLevel) string { + // for historical reasons, "private" above is an empty string in the API + if input == containers.Private { + return "private" } - storageAccountName := strings.Replace(uri.Host, fmt.Sprintf(".blob.%s", environment.StorageEndpointSuffix), "", 1) - containerName := segments[0] + return string(input) +} - id := storageContainerId{ - storageAccountName: storageAccountName, - containerName: containerName, +func validateArmStorageContainerName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^\$root$|^\$web$|^[0-9a-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters and hyphens allowed in %q: %q", + k, value)) } - return &id, nil + if len(value) < 3 || len(value) > 63 { + errors = append(errors, fmt.Errorf( + "%q must be between 3 and 63 characters: %q", k, value)) + } + if regexp.MustCompile(`^-`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot begin with a hyphen: %q", k, value)) + } + return warnings, errors } diff --git a/azurerm/resource_arm_storage_container_migration_test.go b/azurerm/resource_arm_storage_container_migration_test.go index a638f4e6c907..ddf1e2ee9ea9 100644 --- a/azurerm/resource_arm_storage_container_migration_test.go +++ b/azurerm/resource_arm_storage_container_migration_test.go @@ -16,7 +16,7 @@ func TestAccAzureRMStorageContainerMigrateState(t *testing.T) { return } - client, err := getArmClient(config, false, "") + client, err := getArmClient(config, false, "0.0.0", "", true) if err != nil { t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) return diff --git a/azurerm/resource_arm_storage_container_test.go b/azurerm/resource_arm_storage_container_test.go index 3de0224b4e8a..74261557679b 100644 --- a/azurerm/resource_arm_storage_container_test.go +++ b/azurerm/resource_arm_storage_container_test.go @@ -2,24 +2,23 @@ package azurerm import ( "fmt" - "log" "strings" "testing" - "github.com/Azure/azure-sdk-for-go/storage" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestAccAzureRMStorageContainer_basic(t *testing.T) { resourceName := "azurerm_storage_container.test" - var c storage.Container ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageContainer_basic(ri, rs, testLocation()) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -27,9 +26,9 @@ func TestAccAzureRMStorageContainer_basic(t *testing.T) { CheckDestroy: testCheckAzureRMStorageContainerDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageContainer_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists(resourceName, &c), + testCheckAzureRMStorageContainerExists(resourceName), ), }, { @@ -42,13 +41,12 @@ func TestAccAzureRMStorageContainer_basic(t *testing.T) { } func TestAccAzureRMStorageContainer_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } resourceName := "azurerm_storage_container.test" - var c storage.Container ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) @@ -62,7 +60,7 @@ func TestAccAzureRMStorageContainer_requiresImport(t *testing.T) { { Config: testAccAzureRMStorageContainer_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists(resourceName, &c), + testCheckAzureRMStorageContainerExists(resourceName), ), }, { @@ -75,14 +73,10 @@ func TestAccAzureRMStorageContainer_requiresImport(t *testing.T) { func TestAccAzureRMStorageContainer_update(t *testing.T) { resourceName := "azurerm_storage_container.test" - var c storage.Container ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - at1 := "private" - at2 := "container" - initconfig := testAccAzureRMStorageContainer_update(ri, rs, testLocation(), at1) - updateconfig := testAccAzureRMStorageContainer_update(ri, rs, testLocation(), at2) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -90,17 +84,17 @@ func TestAccAzureRMStorageContainer_update(t *testing.T) { CheckDestroy: testCheckAzureRMStorageContainerDestroy, Steps: []resource.TestStep{ { - Config: initconfig, + Config: testAccAzureRMStorageContainer_update(ri, rs, location, "private"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists(resourceName, &c), - resource.TestCheckResourceAttr(resourceName, "container_access_type", at1), + testCheckAzureRMStorageContainerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "container_access_type", "private"), ), }, { - Config: updateconfig, + Config: testAccAzureRMStorageContainer_update(ri, rs, location, "container"), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists(resourceName, &c), - resource.TestCheckResourceAttr(resourceName, "container_access_type", at2), + testCheckAzureRMStorageContainerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "container_access_type", "container"), ), }, { @@ -112,12 +106,12 @@ func TestAccAzureRMStorageContainer_update(t *testing.T) { }) } -func TestAccAzureRMStorageContainer_disappears(t *testing.T) { - var c storage.Container +func TestAccAzureRMStorageContainer_metaData(t *testing.T) { + resourceName := "azurerm_storage_container.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageContainer_basic(ri, rs, testLocation()) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -125,10 +119,58 @@ func TestAccAzureRMStorageContainer_disappears(t *testing.T) { CheckDestroy: testCheckAzureRMStorageContainerDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageContainer_metaData(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists("azurerm_storage_container.test", &c), - testAccARMStorageContainerDisappears("azurerm_storage_container.test", &c), + testCheckAzureRMStorageContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageContainer_metaDataUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageContainer_metaDataEmpty(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageContainerExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageContainer_disappears(t *testing.T) { + resourceName := "azurerm_storage_container.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageContainerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageContainer_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageContainerExists(resourceName), + testAccARMStorageContainerDisappears(resourceName), ), ExpectNonEmptyPlan: true, }, @@ -138,11 +180,10 @@ func TestAccAzureRMStorageContainer_disappears(t *testing.T) { func TestAccAzureRMStorageContainer_root(t *testing.T) { resourceName := "azurerm_storage_container.test" - var c storage.Container ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageContainer_root(ri, rs, testLocation()) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -150,9 +191,9 @@ func TestAccAzureRMStorageContainer_root(t *testing.T) { CheckDestroy: testCheckAzureRMStorageContainerDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageContainer_root(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageContainerExists(resourceName, &c), + testCheckAzureRMStorageContainerExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", "$root"), ), }, @@ -165,88 +206,104 @@ func TestAccAzureRMStorageContainer_root(t *testing.T) { }) } -func testCheckAzureRMStorageContainerExists(resourceName string, c *storage.Container) resource.TestCheckFunc { - return func(s *terraform.State) error { +func TestAccAzureRMStorageContainer_web(t *testing.T) { + resourceName := "azurerm_storage_container.test" + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageContainerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageContainer_web(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageContainerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "$web"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMStorageContainerExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage container: %s", name) - } + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + containerName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", containerName, accountName, err) } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Container %q (Account %s) - assuming removed & removing from state", containerName, accountName) } - containers, err := blobClient.ListContainers(storage.ListContainersParameters{ - Prefix: name, - Timeout: 90, - }) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, accountName) if err != nil { - return fmt.Errorf("Error listing Storage Container %q containers (storage account: %q) : %+v", name, storageAccountName, err) + return fmt.Errorf("Error building Containers Client: %s", err) } - if len(containers.Containers) == 0 { - return fmt.Errorf("Bad: Storage Container %q (storage account: %q) does not exist", name, storageAccountName) - } - - var found bool - for _, container := range containers.Containers { - if container.Name == name { - found = true - *c = container + resp, err := client.GetProperties(ctx, accountName, containerName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Container %q (Account %q / Resource Group %q) does not exist", containerName, accountName, *resourceGroup) } - } - if !found { - return fmt.Errorf("Bad: Storage Container %q (storage account: %q) does not exist", name, storageAccountName) + return fmt.Errorf("Bad: Get on ContainersClient: %+v", err) } return nil } } -func testAccARMStorageContainerDisappears(resourceName string, c *storage.Container) resource.TestCheckFunc { +func testAccARMStorageContainerDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage container: %s", c.Name) + containerName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", containerName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Container %q (Account %s) - assuming removed & removing from state", containerName, accountName) } - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) + client, err := storageClient.ContainersClient(ctx, *resourceGroup, accountName) if err != nil { - return err + return fmt.Errorf("Error building Containers Client: %s", err) } - if !accountExists { - log.Printf("[INFO]Storage Account %q doesn't exist so the container won't exist", storageAccountName) - return nil + + if _, err := client.Delete(ctx, accountName, containerName); err != nil { + return fmt.Errorf("Error deleting Container %q (Account %q): %s", containerName, accountName, err) } - reference := blobClient.GetContainerReference(c.Name) - options := &storage.DeleteContainerOptions{} - _, err = reference.DeleteIfExists(options) - return err + return nil } } @@ -256,96 +313,41 @@ func testCheckAzureRMStorageContainerDestroy(s *terraform.State) error { continue } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage container: %s", name) - } + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + containerName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - blobClient, accountExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - //If we can't get keys then the blob can't exist - return nil + return fmt.Errorf("Error locating Resource Group for Storage Container %q (Account %s): %s", containerName, accountName, err) } - if !accountExists { + + if resourceGroup == nil { return nil } - containers, err := blobClient.ListContainers(storage.ListContainersParameters{ - Prefix: name, - Timeout: 90, - }) - + client, err := storageClient.ContainersClient(ctx, *resourceGroup, accountName) if err != nil { - return nil + return fmt.Errorf("Error building Containers Client: %s", err) } - var found bool - for _, container := range containers.Containers { - if container.Name == name { - found = true - } + props, err := client.GetProperties(ctx, accountName, containerName) + if err != nil { + return nil } - if found { - return fmt.Errorf("Bad: Storage Container %q (storage account: %q) still exist", name, storageAccountName) - } + return fmt.Errorf("Container still exists: %+v", props) } return nil } -func TestValidateArmStorageContainerName(t *testing.T) { - validNames := []string{ - "valid-name", - "valid02-name", - "$root", - } - for _, v := range validNames { - _, errors := validateArmStorageContainerName(v, "name") - if len(errors) != 0 { - t.Fatalf("%q should be a valid Storage Container Name: %q", v, errors) - } - } - - invalidNames := []string{ - "InvalidName1", - "-invalidname1", - "invalid_name", - "invalid!", - "ww", - "$notroot", - strings.Repeat("w", 65), - } - for _, v := range invalidNames { - _, errors := validateArmStorageContainerName(v, "name") - if len(errors) == 0 { - t.Fatalf("%q should be an invalid Storage Container Name", v) - } - } -} - func testAccAzureRMStorageContainer_basic(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "acctestacc%s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags = { - environment = "staging" - } -} +%s resource "azurerm_storage_container" "test" { name = "vhds" @@ -353,7 +355,7 @@ resource "azurerm_storage_container" "test" { storage_account_name = "${azurerm_storage_account.test.name}" container_access_type = "private" } -`, rInt, location, rString) +`, template) } func testAccAzureRMStorageContainer_requiresImport(rInt int, rString string, location string) string { @@ -371,34 +373,102 @@ resource "azurerm_storage_container" "import" { } func testAccAzureRMStorageContainer_update(rInt int, rString string, location string, accessType string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" +%s + +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 = "%s" +} +`, template, accessType) } -resource "azurerm_storage_account" "test" { - name = "acctestacc%s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" +func testAccAzureRMStorageContainer_metaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) + return fmt.Sprintf(` +%s - 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" + + metadata = { + hello = "world" + } +} +`, template) +} + +func testAccAzureRMStorageContainer_metaDataUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +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" + + metadata = { + hello = "world" + panda = "pops" } } +`, template) +} + +func testAccAzureRMStorageContainer_metaDataEmpty(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) + return fmt.Sprintf(` +%s 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 = "%s" + container_access_type = "private" + + metadata = { + } } -`, rInt, location, rString, accessType) +`, template) } func testAccAzureRMStorageContainer_root(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_container" "test" { + name = "$root" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} +`, template) +} + +func testAccAzureRMStorageContainer_web(rInt int, rString string, location string) string { + template := testAccAzureRMStorageContainer_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_container" "test" { + name = "$web" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} +`, template) +} + +func testAccAzureRMStorageContainer_template(rInt int, rString, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestRG-%d" @@ -416,12 +486,37 @@ resource "azurerm_storage_account" "test" { environment = "staging" } } - -resource "azurerm_storage_container" "test" { - name = "$root" - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.test.name}" - container_access_type = "private" -} `, rInt, location, rString) } + +func TestValidateArmStorageContainerName(t *testing.T) { + validNames := []string{ + "valid-name", + "valid02-name", + "$root", + "$web", + } + for _, v := range validNames { + _, errors := validateArmStorageContainerName(v, "name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid Storage Container Name: %q", v, errors) + } + } + + invalidNames := []string{ + "InvalidName1", + "-invalidname1", + "invalid_name", + "invalid!", + "ww", + "$notroot", + "$notweb", + strings.Repeat("w", 65), + } + for _, v := range invalidNames { + _, errors := validateArmStorageContainerName(v, "name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid Storage Container Name", v) + } + } +} diff --git a/azurerm/resource_arm_storage_data_lake_gen2_filesystem.go b/azurerm/resource_arm_storage_data_lake_gen2_filesystem.go new file mode 100644 index 000000000000..3e80fed523da --- /dev/null +++ b/azurerm/resource_arm_storage_data_lake_gen2_filesystem.go @@ -0,0 +1,225 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/datalakestore/filesystems" +) + +func resourceArmStorageDataLakeGen2FileSystem() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageDataLakeGen2FileSystemCreate, + Read: resourceArmStorageDataLakeGen2FileSystemRead, + Update: resourceArmStorageDataLakeGen2FileSystemUpdate, + Delete: resourceArmStorageDataLakeGen2FileSystemDelete, + + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + storageClients := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext + + id, err := filesystems.ParseResourceID(d.Id()) + if err != nil { + return []*schema.ResourceData{d}, fmt.Errorf("Error parsing ID %q for import of Data Lake Gen2 File System: %v", d.Id(), err) + } + + // we then need to look up the Storage Account ID - so first find the resource group + resourceGroup, err := storageClients.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return []*schema.ResourceData{d}, fmt.Errorf("Error locating Resource Group for Storage Account %q to import Data Lake Gen2 File System %q: %v", id.AccountName, d.Id(), err) + } + + if resourceGroup == nil { + return []*schema.ResourceData{d}, fmt.Errorf("Unable to locate Resource Group for Storage Account %q to import Data Lake Gen2 File System %q", id.AccountName, d.Id()) + } + + // then pull the storage account itself + account, err := storageClients.AccountsClient.GetProperties(ctx, *resourceGroup, id.AccountName, "") + if err != nil { + return []*schema.ResourceData{d}, fmt.Errorf("Error retrieving Storage Account %q to import Data Lake Gen2 File System %q: %+v", id.AccountName, d.Id(), err) + } + + d.Set("storage_account_id", account.ID) + + return []*schema.ResourceData{d}, nil + }, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageDataLakeGen2FileSystemName, + }, + + "storage_account_id": storage.AccountIDSchema(), + + "properties": storage.MetaDataSchema(), + }, + } +} + +func resourceArmStorageDataLakeGen2FileSystemCreate(d *schema.ResourceData, meta interface{}) error { + accountsClient := meta.(*ArmClient).Storage.AccountsClient + client := meta.(*ArmClient).Storage.FileSystemsClient + ctx := meta.(*ArmClient).StopContext + + storageID, err := storage.ParseAccountID(d.Get("storage_account_id").(string)) + if err != nil { + return err + } + + // confirm the storage account exists, otherwise Data Plane API requests will fail + storageAccount, err := accountsClient.GetProperties(ctx, storageID.ResourceGroup, storageID.Name, "") + if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + return fmt.Errorf("Storage Account %q was not found in Resource Group %q!", storageID.Name, storageID.ResourceGroup) + } + + return fmt.Errorf("Error checking for existence of Storage Account %q (Resource Group %q): %+v", storageID.Name, storageID.ResourceGroup, err) + } + + fileSystemName := d.Get("name").(string) + propertiesRaw := d.Get("properties").(map[string]interface{}) + properties := storage.ExpandMetaData(propertiesRaw) + + id := client.GetResourceID(storageID.Name, fileSystemName) + + if features.ShouldResourcesBeImported() { + resp, err := client.GetProperties(ctx, storageID.Name, fileSystemName) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error checking for existence of existing File System %q (Account %q): %+v", fileSystemName, storageID.Name, err) + } + } + + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_storage_data_lake_gen2_filesystem", id) + } + } + + log.Printf("[INFO] Creating File System %q in Storage Account %q.", fileSystemName, storageID.Name) + input := filesystems.CreateInput{ + Properties: properties, + } + if _, err := client.Create(ctx, storageID.Name, fileSystemName, input); err != nil { + return fmt.Errorf("Error creating File System %q in Storage Account %q: %s", fileSystemName, storageID.Name, err) + } + + d.SetId(id) + return resourceArmStorageDataLakeGen2FileSystemRead(d, meta) +} + +func resourceArmStorageDataLakeGen2FileSystemUpdate(d *schema.ResourceData, meta interface{}) error { + accountsClient := meta.(*ArmClient).Storage.AccountsClient + client := meta.(*ArmClient).Storage.FileSystemsClient + ctx := meta.(*ArmClient).StopContext + + id, err := filesystems.ParseResourceID(d.Id()) + if err != nil { + return err + } + + storageID, err := storage.ParseAccountID(d.Get("storage_account_id").(string)) + if err != nil { + return err + } + + // confirm the storage account exists, otherwise Data Plane API requests will fail + storageAccount, err := accountsClient.GetProperties(ctx, storageID.ResourceGroup, storageID.Name, "") + if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + return fmt.Errorf("Storage Account %q was not found in Resource Group %q!", storageID.Name, storageID.ResourceGroup) + } + + return fmt.Errorf("Error checking for existence of Storage Account %q (Resource Group %q): %+v", storageID.Name, storageID.ResourceGroup, err) + } + + propertiesRaw := d.Get("properties").(map[string]interface{}) + properties := storage.ExpandMetaData(propertiesRaw) + + log.Printf("[INFO] Updating Properties for File System %q in Storage Account %q.", id.DirectoryName, id.AccountName) + input := filesystems.SetPropertiesInput{ + Properties: properties, + } + if _, err = client.SetProperties(ctx, id.AccountName, id.DirectoryName, input); err != nil { + return fmt.Errorf("Error updating Properties for File System %q in Storage Account %q: %s", id.DirectoryName, id.AccountName, err) + } + + return resourceArmStorageDataLakeGen2FileSystemRead(d, meta) +} + +func resourceArmStorageDataLakeGen2FileSystemRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.FileSystemsClient + ctx := meta.(*ArmClient).StopContext + + id, err := filesystems.ParseResourceID(d.Id()) + if err != nil { + return err + } + + // TODO: what about when this has been removed? + resp, err := client.GetProperties(ctx, id.AccountName, id.DirectoryName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] File System %q does not exist in Storage Account %q - removing from state...", id.DirectoryName, id.AccountName) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving File System %q in Storage Account %q: %+v", id.DirectoryName, id.AccountName, err) + } + + d.Set("name", id.DirectoryName) + + if err := d.Set("properties", resp.Properties); err != nil { + return fmt.Errorf("Error setting `properties`: %+v", err) + } + + return nil +} + +func resourceArmStorageDataLakeGen2FileSystemDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.FileSystemsClient + ctx := meta.(*ArmClient).StopContext + + id, err := filesystems.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Delete(ctx, id.AccountName, id.DirectoryName) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error deleting File System %q in Storage Account %q: %+v", id.DirectoryName, id.AccountName, err) + } + } + + return nil +} + +func validateArmStorageDataLakeGen2FileSystemName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^\$root$|^[0-9a-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters and hyphens allowed in %q: %q", + k, value)) + } + if len(value) < 3 || len(value) > 63 { + errors = append(errors, fmt.Errorf( + "%q must be between 3 and 63 characters: %q", k, value)) + } + if regexp.MustCompile(`^-`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot begin with a hyphen: %q", k, value)) + } + return warnings, errors +} diff --git a/azurerm/resource_arm_storage_data_lake_gen2_filesystem_test.go b/azurerm/resource_arm_storage_data_lake_gen2_filesystem_test.go new file mode 100644 index 000000000000..40a8255ebc43 --- /dev/null +++ b/azurerm/resource_arm_storage_data_lake_gen2_filesystem_test.go @@ -0,0 +1,224 @@ +package azurerm + +import ( + "fmt" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMStorageDataLakeGen2FileSystem_basic(t *testing.T) { + resourceName := "azurerm_storage_data_lake_gen2_filesystem.test" + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageDataLakeGen2FileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageDataLakeGen2FileSystem_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageDataLakeGen2FileSystemExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageDataLakeGen2FileSystem_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_storage_data_lake_gen2_filesystem.test" + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageDataLakeGen2FileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageDataLakeGen2FileSystem_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageDataLakeGen2FileSystemExists(resourceName), + ), + }, + { + Config: testAccAzureRMStorageDataLakeGen2FileSystem_requiresImport(ri, rs, location), + ExpectError: testRequiresImportError("azurerm_storage_data_lake_gen2_filesystem"), + }, + }, + }) +} + +func TestAccAzureRMStorageDataLakeGen2FileSystem_properties(t *testing.T) { + resourceName := "azurerm_storage_data_lake_gen2_filesystem.test" + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageDataLakeGen2FileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageDataLakeGen2FileSystem_properties(ri, rs, location, "aGVsbG8="), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageDataLakeGen2FileSystemExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageDataLakeGen2FileSystem_properties(ri, rs, location, "ZXll"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageDataLakeGen2FileSystemExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMStorageDataLakeGen2FileSystemExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + client := testAccProvider.Meta().(*ArmClient).Storage.FileSystemsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + fileSystemName := rs.Primary.Attributes["name"] + storageID, err := storage.ParseAccountID(rs.Primary.Attributes["storage_account_id"]) + if err != nil { + return err + } + + resp, err := client.GetProperties(ctx, storageID.Name, fileSystemName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: File System %q (Account %q) does not exist", fileSystemName, storageID.Name) + } + + return fmt.Errorf("Bad: Get on FileSystemsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMStorageDataLakeGen2FileSystemDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_data_lake_gen2_filesystem" { + continue + } + + client := testAccProvider.Meta().(*ArmClient).Storage.FileSystemsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + fileSystemName := rs.Primary.Attributes["name"] + storageID, err := storage.ParseAccountID(rs.Primary.Attributes["storage_account_id"]) + if err != nil { + return err + } + + props, err := client.GetProperties(ctx, storageID.Name, fileSystemName) + if err != nil { + return nil + } + + return fmt.Errorf("File System still exists: %+v", props) + } + + return nil +} + +func testAccAzureRMStorageDataLakeGen2FileSystem_basic(rInt int, rString string, location string) string { + template := testAccAzureRMStorageDataLakeGen2FileSystem_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_data_lake_gen2_filesystem" "test" { + name = "acctest-%d" + storage_account_id = azurerm_storage_account.test.id +} +`, template, rInt) +} + +func testAccAzureRMStorageDataLakeGen2FileSystem_requiresImport(rInt int, rString string, location string) string { + template := testAccAzureRMStorageDataLakeGen2FileSystem_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_data_lake_gen2_filesystem" "import" { + name = azurerm_storage_data_lake_gen2_filesystem.test.name + storage_account_id = azurerm_storage_data_lake_gen2_filesystem.storage_account_id +} +`, template) +} + +func testAccAzureRMStorageDataLakeGen2FileSystem_properties(rInt int, rString, location, value string) string { + template := testAccAzureRMStorageDataLakeGen2FileSystem_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_data_lake_gen2_filesystem" "test" { + name = "acctest-%d" + storage_account_id = azurerm_storage_account.test.id + + properties = { + key = "%s" + } +} +`, template, rInt, value) +} + +func testAccAzureRMStorageDataLakeGen2FileSystem_template(rInt int, rString, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_kind = "BlobStorage" + account_tier = "Standard" + account_replication_type = "LRS" +} +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_management_policy.go b/azurerm/resource_arm_storage_management_policy.go new file mode 100644 index 000000000000..9c4c175c9d64 --- /dev/null +++ b/azurerm/resource_arm_storage_management_policy.go @@ -0,0 +1,397 @@ +package azurerm + +import ( + "fmt" + "regexp" + + "github.com/hashicorp/terraform/helper/validation" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceArmStorageManagementPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageManagementPolicyCreateOrUpdate, + Read: resourceArmStorageManagementPolicyRead, + Update: resourceArmStorageManagementPolicyCreateOrUpdate, + Delete: resourceArmStorageManagementPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + "rule": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[a-zA-Z0-9]*$`), + "A rule name can contain any combination of alpha numeric characters.", + ), + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "filters": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prefix_match": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "blob_types": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"blockBlob"}, false), + }, + Set: schema.HashString, + }, + }, + }, + }, + "actions": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base_blob": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tier_to_cool_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "tier_to_archive_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "delete_after_days_since_modification_greater_than": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + "snapshot": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delete_after_days_since_creation_greater_than": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceArmStorageManagementPolicyCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.ManagementPoliciesClient + ctx := meta.(*ArmClient).StopContext + + storageAccountId := d.Get("storage_account_id").(string) + + rid, err := parseAzureResourceID(storageAccountId) + if err != nil { + return err + } + resourceGroupName := rid.ResourceGroup + storageAccountName := rid.Path["storageAccounts"] + + name := "default" // The name of the Storage Account Management Policy. It should always be 'default' (from https://docs.microsoft.com/en-us/rest/api/storagerp/managementpolicies/createorupdate) + + parameters := storage.ManagementPolicy{ + Name: &name, + } + + rules := d.Get("rule").([]interface{}) + armRules, err := expandStorageManagementPolicyRules(rules) + if err != nil { + return fmt.Errorf("Error expanding Azure Storage Management Policy Rules %q: %+v", storageAccountId, err) + } + + parameters.ManagementPolicyProperties = &storage.ManagementPolicyProperties{ + Policy: &storage.ManagementPolicySchema{ + Rules: armRules, + }, + } + + result, err := client.CreateOrUpdate(ctx, resourceGroupName, storageAccountName, parameters) + if err != nil { + return fmt.Errorf("Error creating Azure Storage Management Policy %q: %+v", storageAccountId, err) + } + + result, err = client.Get(ctx, resourceGroupName, storageAccountName) + if err != nil { + return fmt.Errorf("Error getting created Azure Storage Management Policy %q: %+v", storageAccountId, err) + } + + d.SetId(*result.ID) + + return resourceArmStorageManagementPolicyRead(d, meta) +} + +func resourceArmStorageManagementPolicyRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.ManagementPoliciesClient + ctx := meta.(*ArmClient).StopContext + + id := d.Id() + + rid, err := parseAzureResourceID(id) + if err != nil { + return err + } + resourceGroupName := rid.ResourceGroup + storageAccountName := rid.Path["storageAccounts"] + + result, err := client.Get(ctx, resourceGroupName, storageAccountName) + if err != nil { + return err + } + + storageAccountID := "/subscriptions/" + rid.SubscriptionID + "/resourceGroups/" + rid.ResourceGroup + "/providers/" + rid.Provider + "/storageAccounts/" + storageAccountName + d.Set("storage_account_id", storageAccountID) + + if policy := result.Policy; policy != nil { + policy := result.Policy + if rules := policy.Rules; rules != nil { + if err := d.Set("rule", flattenStorageManagementPolicyRules(policy.Rules)); err != nil { + return fmt.Errorf("Error flattening `rule`: %+v", err) + } + } + } + + return nil +} + +func resourceArmStorageManagementPolicyDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.ManagementPoliciesClient + ctx := meta.(*ArmClient).StopContext + + id := d.Id() + + rid, err := parseAzureResourceID(id) + if err != nil { + return err + } + resourceGroupName := rid.ResourceGroup + storageAccountName := rid.Path["storageAccounts"] + + _, err = client.Delete(ctx, resourceGroupName, storageAccountName) + if err != nil { + return err + } + return nil +} + +func expandStorageManagementPolicyRules(list []interface{}) (*[]storage.ManagementPolicyRule, error) { + result := []storage.ManagementPolicyRule{} + + for _, tempItem := range list { + if tempItem != nil { + item := tempItem.(map[string]interface{}) + policyRule, err := expandStorageManagementPolicyRule(item) + if err != nil { + return nil, err + } + result = append(result, policyRule) + } + } + return &result, nil +} + +func expandStorageManagementPolicyRule(ref map[string]interface{}) (storage.ManagementPolicyRule, error) { + name := ref["name"].(string) + enabled := ref["enabled"].(bool) + typeVal := "Lifecycle" + + definition := storage.ManagementPolicyDefinition{ + Filters: &storage.ManagementPolicyFilter{}, + Actions: &storage.ManagementPolicyAction{}, + } + filtersRef := ref["filters"].([]interface{}) + if len(filtersRef) == 1 { + if filtersRef[0] != nil { + filterRef := filtersRef[0].(map[string]interface{}) + + prefixMatches := []string{} + prefixMatchesRef := filterRef["prefix_match"].(*schema.Set) + if prefixMatchesRef != nil { + for _, prefixMatchRef := range prefixMatchesRef.List() { + prefixMatches = append(prefixMatches, prefixMatchRef.(string)) + } + } + definition.Filters.PrefixMatch = &prefixMatches + + blobTypes := []string{} + blobTypesRef := filterRef["blob_types"].(*schema.Set) + if blobTypesRef != nil { + for _, blobTypeRef := range blobTypesRef.List() { + blobTypes = append(blobTypes, blobTypeRef.(string)) + } + } + definition.Filters.BlobTypes = &blobTypes + } + } + actionsRef := ref["actions"].([]interface{}) + if len(actionsRef) == 1 { + actionRef := actionsRef[0].(map[string]interface{}) + + baseBlobsRef := actionRef["base_blob"].([]interface{}) + if len(baseBlobsRef) == 1 { + baseBlob := &storage.ManagementPolicyBaseBlob{} + baseBlobRef := baseBlobsRef[0].(map[string]interface{}) + if v, ok := baseBlobRef["tier_to_cool_after_days_since_modification_greater_than"]; ok { + v2 := float64(v.(int)) + baseBlob.TierToCool = &storage.DateAfterModification{DaysAfterModificationGreaterThan: &v2} + } + if v, ok := baseBlobRef["tier_to_archive_after_days_since_modification_greater_than"]; ok { + v2 := float64(v.(int)) + baseBlob.TierToArchive = &storage.DateAfterModification{DaysAfterModificationGreaterThan: &v2} + } + if v, ok := baseBlobRef["delete_after_days_since_modification_greater_than"]; ok { + v2 := float64(v.(int)) + baseBlob.Delete = &storage.DateAfterModification{DaysAfterModificationGreaterThan: &v2} + } + definition.Actions.BaseBlob = baseBlob + } + + snapshotRef := actionRef["snapshot"].([]interface{}) + if len(snapshotRef) == 1 { + snapshot := &storage.ManagementPolicySnapShot{} + snapshotRef := snapshotRef[0].(map[string]interface{}) + if v, ok := snapshotRef["delete_after_days_since_creation_greater_than"]; ok { + v2 := float64(v.(int)) + snapshot.Delete = &storage.DateAfterCreation{DaysAfterCreationGreaterThan: &v2} + } + definition.Actions.Snapshot = snapshot + } + } + + rule := storage.ManagementPolicyRule{ + Name: &name, + Enabled: &enabled, + Type: &typeVal, + Definition: &definition, + } + return rule, nil +} + +func flattenStorageManagementPolicyRules(armRules *[]storage.ManagementPolicyRule) []interface{} { + rules := make([]interface{}, 0) + if armRules == nil { + return rules + } + for _, armRule := range *armRules { + rule := make(map[string]interface{}) + + if armRule.Name != nil { + rule["name"] = *armRule.Name + } + if armRule.Enabled != nil { + rule["enabled"] = *armRule.Enabled + } + + armDefinition := armRule.Definition + if armDefinition != nil { + armFilter := armDefinition.Filters + if armFilter != nil { + filter := make(map[string]interface{}) + if armFilter.PrefixMatch != nil { + prefixMatches := make([]interface{}, 0) + for _, armPrefixMatch := range *armFilter.PrefixMatch { + prefixMatches = append(prefixMatches, armPrefixMatch) + } + filter["prefix_match"] = prefixMatches + } + if armFilter.BlobTypes != nil { + blobTypes := make([]interface{}, 0) + for _, armBlobType := range *armFilter.BlobTypes { + blobTypes = append(blobTypes, armBlobType) + } + filter["blob_types"] = blobTypes + } + rule["filters"] = [1]interface{}{filter} + } + + armAction := armDefinition.Actions + if armAction != nil { + action := make(map[string]interface{}) + armActionBaseBlob := armAction.BaseBlob + if armActionBaseBlob != nil { + baseBlob := make(map[string]interface{}) + if armActionBaseBlob.TierToCool != nil && armActionBaseBlob.TierToCool.DaysAfterModificationGreaterThan != nil { + intTemp := int(*armActionBaseBlob.TierToCool.DaysAfterModificationGreaterThan) + baseBlob["tier_to_cool_after_days_since_modification_greater_than"] = intTemp + } + if armActionBaseBlob.TierToArchive != nil && armActionBaseBlob.TierToArchive.DaysAfterModificationGreaterThan != nil { + intTemp := int(*armActionBaseBlob.TierToArchive.DaysAfterModificationGreaterThan) + baseBlob["tier_to_archive_after_days_since_modification_greater_than"] = intTemp + } + if armActionBaseBlob.Delete != nil && armActionBaseBlob.Delete.DaysAfterModificationGreaterThan != nil { + intTemp := int(*armActionBaseBlob.Delete.DaysAfterModificationGreaterThan) + baseBlob["delete_after_days_since_modification_greater_than"] = intTemp + } + action["base_blob"] = [1]interface{}{baseBlob} + } + + armActionSnaphost := armAction.Snapshot + if armActionSnaphost != nil { + snapshot := make(map[string]interface{}) + if armActionSnaphost.Delete != nil && armActionSnaphost.Delete.DaysAfterCreationGreaterThan != nil { + intTemp := int(*armActionSnaphost.Delete.DaysAfterCreationGreaterThan) + snapshot["delete_after_days_since_creation_greater_than"] = intTemp + } + action["snapshot"] = [1]interface{}{snapshot} + } + + rule["actions"] = [1]interface{}{action} + } + } + + rules = append(rules, rule) + } + + return rules +} diff --git a/azurerm/resource_arm_storage_management_policy_test.go b/azurerm/resource_arm_storage_management_policy_test.go new file mode 100644 index 000000000000..9c59745f6889 --- /dev/null +++ b/azurerm/resource_arm_storage_management_policy_test.go @@ -0,0 +1,448 @@ +package azurerm + +import ( + "fmt" + "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/tf" +) + +func TestAccAzureRMStorageManagementPolicy_basic(t *testing.T) { + resourceName := "azurerm_storage_management_policy.testpolicy" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + config := testAccAzureRMStorageManagementPolicy_basic(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountManagementPolicyDestroy(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountManagementPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.name", "rule1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.3439697764", "container1/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "10"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "100"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "30"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageManagementPolicy_multipleRule(t *testing.T) { + resourceName := "azurerm_storage_management_policy.testpolicy" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + config := testAccAzureRMStorageManagementPolicy_multipleRule(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountManagementPolicyDestroy(), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountManagementPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + + // Rule1 + resource.TestCheckResourceAttr(resourceName, "rule.0.name", "rule1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.3439697764", "container1/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "10"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "100"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "30"), + + // Rule2 + resource.TestCheckResourceAttr(resourceName, "rule.1.name", "rule2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.4102595489", "container2/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.1837232667", "container2/prefix2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "11"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "51"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "101"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "31"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageManagementPolicy_updateMultipleRule(t *testing.T) { + resourceName := "azurerm_storage_management_policy.testpolicy" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + config1 := testAccAzureRMStorageManagementPolicy_multipleRule(ri, rs, location) + config2 := testAccAzureRMStorageManagementPolicy_multipleRuleUpdated(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountManagementPolicyDestroy(), + Steps: []resource.TestStep{ + { + Config: config1, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountManagementPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + + // Rule1 + resource.TestCheckResourceAttr(resourceName, "rule.0.name", "rule1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.3439697764", "container1/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "10"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "100"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "30"), + + // Rule2 + resource.TestCheckResourceAttr(resourceName, "rule.1.name", "rule2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.4102595489", "container2/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.1837232667", "container2/prefix2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "11"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "51"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "101"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "31"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: config2, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountManagementPolicyExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + + // Rule1 + resource.TestCheckResourceAttr(resourceName, "rule.0.name", "rule1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.prefix_match.3439697764", "container1/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "10"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "50"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "100"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.0.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "30"), + + // Rule2 + resource.TestCheckResourceAttr(resourceName, "rule.1.name", "rule2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.enabled", "true"), // check updated + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.#", "2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.4102595489", "container2/prefix1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.prefix_match.1837232667", "container2/prefix2"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.filters.0.blob_types.1068358194", "blockBlob"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_cool_after_days_since_modification_greater_than", "12"), // check updated + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.tier_to_archive_after_days_since_modification_greater_than", "52"), // check updated + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.base_blob.0.delete_after_days_since_modification_greater_than", "102"), // check updated + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1.actions.0.snapshot.0.delete_after_days_since_creation_greater_than", "32"), // check updated + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMStorageAccountManagementPolicyDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_management_policy" { + continue + } + storageAccountID := rs.Primary.Attributes["storage_account_id"] + + exists, err := testCheckAzureRMStorageAccountManagementPolicyExistsInternal(storageAccountID) + if err != nil { + return fmt.Errorf("Error checking if item has been destroyed: %s", err) + } + if exists { + return fmt.Errorf("Bad: Storage Account Management Policy '%q' still exists", storageAccountID) + } + } + + return nil + } +} + +func testCheckAzureRMStorageAccountManagementPolicyExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + storageAccountID := rs.Primary.Attributes["storage_account_id"] + + exists, err := testCheckAzureRMStorageAccountManagementPolicyExistsInternal(storageAccountID) + if err != nil { + return fmt.Errorf("Error checking if item exists: %s", err) + } + if !exists { + return fmt.Errorf("Bad: Storage Account Management Policy '%q' does not exist", storageAccountID) + } + + return nil + } +} + +func testCheckAzureRMStorageAccountManagementPolicyExistsInternal(storageAccountID string) (bool, error) { + rid, err := parseAzureResourceID(storageAccountID) + if err != nil { + return false, fmt.Errorf("Bad: Failed to parse ID (id: %s): %+v", storageAccountID, err) + } + + resourceGroupName := rid.ResourceGroup + storageAccountName := rid.Path["storageAccounts"] + + conn := testAccProvider.Meta().(*ArmClient).Storage.ManagementPoliciesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + response, err := conn.Get(ctx, resourceGroupName, storageAccountName) + if err != nil { + if response.Response.IsHTTPStatus(404) { + return false, nil + } + return false, fmt.Errorf("Bad: Get on storageAccount ManagementPolicy client (id: %s): %+v", storageAccountID, err) + } + + return true, nil +} + +func testAccAzureRMStorageManagementPolicy_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "BlobStorage" +} + +resource "azurerm_storage_management_policy" "testpolicy" { + storage_account_id = "${azurerm_storage_account.testsa.id}" + + rule { + name = "rule1" + enabled = true + filters { + prefix_match = [ "container1/prefix1" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 10 + tier_to_archive_after_days_since_modification_greater_than = 50 + delete_after_days_since_modification_greater_than = 100 + } + snapshot { + delete_after_days_since_creation_greater_than = 30 + } + } + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageManagementPolicy_multipleRule(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "BlobStorage" +} + +resource "azurerm_storage_management_policy" "testpolicy" { + storage_account_id = "${azurerm_storage_account.testsa.id}" + + rule { + name = "rule1" + enabled = true + filters { + prefix_match = [ "container1/prefix1" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 10 + tier_to_archive_after_days_since_modification_greater_than = 50 + delete_after_days_since_modification_greater_than = 100 + } + snapshot { + delete_after_days_since_creation_greater_than = 30 + } + } + } + rule { + name = "rule2" + enabled = false + filters { + prefix_match = [ "container2/prefix1", "container2/prefix2" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 11 + tier_to_archive_after_days_since_modification_greater_than = 51 + delete_after_days_since_modification_greater_than = 101 + } + snapshot { + delete_after_days_since_creation_greater_than = 31 + } + } + } +} +`, rInt, location, rString) +} + +func testAccAzureRMStorageManagementPolicy_multipleRuleUpdated(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "BlobStorage" +} + +resource "azurerm_storage_management_policy" "testpolicy" { + storage_account_id = "${azurerm_storage_account.testsa.id}" + + rule { + name = "rule1" + enabled = true + filters { + prefix_match = [ "container1/prefix1" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 10 + tier_to_archive_after_days_since_modification_greater_than = 50 + delete_after_days_since_modification_greater_than = 100 + } + snapshot { + delete_after_days_since_creation_greater_than = 30 + } + } + } + rule { + name = "rule2" + enabled = true + filters { + prefix_match = [ "container2/prefix1", "container2/prefix2" ] + blob_types = [ "blockBlob" ] + } + actions { + base_blob { + tier_to_cool_after_days_since_modification_greater_than = 12 + tier_to_archive_after_days_since_modification_greater_than = 52 + delete_after_days_since_modification_greater_than = 102 + } + snapshot { + delete_after_days_since_creation_greater_than = 32 + } + } + } +} +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_queue.go b/azurerm/resource_arm_storage_queue.go index f87fe98f583e..010cd2ab704a 100644 --- a/azurerm/resource_arm_storage_queue.go +++ b/azurerm/resource_arm_storage_queue.go @@ -3,20 +3,22 @@ package azurerm import ( "fmt" "log" - "net/url" "regexp" - "strings" - "github.com/Azure/azure-sdk-for-go/storage" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues" ) func resourceArmStorageQueue() *schema.Resource { return &schema.Resource{ Create: resourceArmStorageQueueCreate, Read: resourceArmStorageQueueRead, + Update: resourceArmStorageQueueUpdate, Delete: resourceArmStorageQueueDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -31,12 +33,17 @@ func resourceArmStorageQueue() *schema.Resource { ForceNew: true, ValidateFunc: validateArmStorageQueueName, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "storage_account_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, }, + + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), + + "metadata": storage.MetaDataSchema(), }, } } @@ -71,157 +78,156 @@ func validateArmStorageQueueName(v interface{}, k string) (warnings []string, er } func resourceArmStorageQueueCreate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext - environment := armClient.environment + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext + + queueName := d.Get("name").(string) + accountName := d.Get("storage_account_name").(string) - name := d.Get("name").(string) - resourceGroupName := d.Get("resource_group_name").(string) - storageAccountName := d.Get("storage_account_name").(string) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) - queueClient, accountExists, err := armClient.getQueueServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Queue %q (Account %s): %s", queueName, accountName, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Queue %q (Account %s)", queueName, accountName) } - queueReference := queueClient.GetQueueReference(name) - id := fmt.Sprintf("https://%s.queue.%s/%s", storageAccountName, environment.StorageEndpointSuffix, name) - if requireResourcesToBeImported { - exists, e := queueReference.Exists() - if e != nil { - return fmt.Errorf("Error checking if Queue %q exists (Account %q / Resource Group %q): %s", name, storageAccountName, resourceGroupName, e) + queueClient, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Queues Client: %s", err) + } + + resourceID := queueClient.GetResourceID(accountName, queueName) + if features.ShouldResourcesBeImported() { + existing, err := queueClient.GetMetaData(ctx, accountName, queueName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Queue %q (Storage Account %q): %s", queueName, accountName, err) + } } - if exists { - return tf.ImportAsExistsError("azurerm_storage_queue", id) + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_storage_queue", resourceID) } } - log.Printf("[INFO] Creating queue %q in storage account %q", name, storageAccountName) - options := &storage.QueueServiceOptions{} - err = queueReference.Create(options) - if err != nil { - return fmt.Errorf("Error creating storage queue on Azure: %s", err) + if _, err := queueClient.Create(ctx, accountName, queueName, metaData); err != nil { + return fmt.Errorf("Error creating Queue %q (Account %q): %+v", queueName, accountName, err) } - d.SetId(id) + d.SetId(resourceID) + return resourceArmStorageQueueRead(d, meta) } -func resourceArmStorageQueueRead(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext +func resourceArmStorageQueueUpdate(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageQueueID(d.Id()) + id, err := queues.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Queue %q (Account %s): %s", id.QueueName, id.AccountName, err) } - if resourceGroup == nil { - log.Printf("[WARN] Unable to determine Resource Group for Storage Account %q (assuming removed) - removing from state", id.storageAccountName) - d.SetId("") - return nil + return fmt.Errorf("Unable to locate Resource Group for Storage Queue %q (Account %s)", id.QueueName, id.AccountName) + } + + queuesClient, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Queues Client: %s", err) + } + + if _, err := queuesClient.SetMetaData(ctx, id.AccountName, id.QueueName, metaData); err != nil { + return fmt.Errorf("Error setting MetaData for Queue %q (Storage Account %q): %s", id.QueueName, id.AccountName, err) } - queueClient, accountExists, err := armClient.getQueueServiceClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + return resourceArmStorageQueueRead(d, meta) +} + +func resourceArmStorageQueueRead(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext + + id, err := queues.ParseResourceID(d.Id()) if err != nil { return err } - if !accountExists { - log.Printf("[DEBUG] Storage account %q not found, removing queue %q from state", id.storageAccountName, id.queueName) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Queue Container %q (Account %s): %s", id.QueueName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Queue %q (Account %s) - assuming removed & removing from state", id.QueueName, id.AccountName) d.SetId("") return nil } - log.Printf("[INFO] Checking for existence of storage queue %q.", id.queueName) - queueReference := queueClient.GetQueueReference(id.queueName) - exists, err := queueReference.Exists() + queuesClient, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return fmt.Errorf("error checking if storage queue %q exists: %s", id.queueName, err) + return fmt.Errorf("Error building Queues Client: %s", err) } - if !exists { - log.Printf("[INFO] Storage queue %q no longer exists, removing from state...", id.queueName) - d.SetId("") + metaData, err := queuesClient.GetMetaData(ctx, id.AccountName, id.QueueName) + if err != nil { + if utils.ResponseWasNotFound(metaData.Response) { + log.Printf("[INFO] Storage Queue %q no longer exists, removing from state...", id.QueueName) + d.SetId("") + return nil + } + return nil } - d.Set("name", id.queueName) - d.Set("storage_account_name", id.storageAccountName) - d.Set("resource_group_name", *resourceGroup) + d.Set("name", id.QueueName) + d.Set("storage_account_name", id.AccountName) + d.Set("resource_group_name", resourceGroup) + + if err := d.Set("metadata", storage.FlattenMetaData(metaData.MetaData)); err != nil { + return fmt.Errorf("Error setting `metadata`: %s", err) + } return nil } func resourceArmStorageQueueDelete(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageQueueID(d.Id()) + id, err := queues.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Queue %q (Account %s): %s", id.QueueName, id.AccountName, err) } - if resourceGroup == nil { - log.Printf("[WARN] Unable to determine Resource Group for Storage Account %q (assuming removed) - removing from state", id.storageAccountName) + log.Printf("[WARN] Unable to determine Resource Group for Storage Queue %q (Account %s) - assuming removed & removing from state", id.QueueName, id.AccountName) + d.SetId("") return nil } - queueClient, accountExists, err := armClient.getQueueServiceClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + queuesClient, err := storageClient.QueuesClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return err - } - if !accountExists { - log.Printf("[INFO]Storage Account %q doesn't exist so the blob won't exist", id.storageAccountName) - return nil + return fmt.Errorf("Error building Queues Client: %s", err) } - log.Printf("[INFO] Deleting storage queue %q", id.queueName) - queueReference := queueClient.GetQueueReference(id.queueName) - options := &storage.QueueServiceOptions{} - if err = queueReference.Delete(options); err != nil { - return fmt.Errorf("Error deleting storage queue %q: %s", id.queueName, err) + if _, err := queuesClient.Delete(ctx, id.AccountName, id.QueueName); err != nil { + return fmt.Errorf("Error deleting Storage Queue %q: %s", id.QueueName, err) } return nil } - -type storageQueueId struct { - storageAccountName string - queueName string -} - -func parseStorageQueueID(input string) (*storageQueueId, error) { - // https://myaccount.queue.core.windows.net/myqueue - uri, err := url.Parse(input) - if err != nil { - return nil, fmt.Errorf("Error parsing %q as a URI: %+v", input, err) - } - - segments := strings.Split(uri.Host, ".") - if len(segments) > 0 { - storageAccountName := segments[0] - // remove the leading `/` - queue := strings.TrimPrefix(uri.Path, "/") - id := storageQueueId{ - storageAccountName: storageAccountName, - queueName: queue, - } - return &id, nil - } - - return nil, nil -} diff --git a/azurerm/resource_arm_storage_queue_migration_test.go b/azurerm/resource_arm_storage_queue_migration_test.go index 8c9c32ccaef0..33ca2d6da849 100644 --- a/azurerm/resource_arm_storage_queue_migration_test.go +++ b/azurerm/resource_arm_storage_queue_migration_test.go @@ -16,7 +16,7 @@ func TestAccAzureRMStorageQueueMigrateState(t *testing.T) { return } - client, err := getArmClient(config, false, "") + client, err := getArmClient(config, false, "0.0.0", "", true) if err != nil { t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) return diff --git a/azurerm/resource_arm_storage_queue_test.go b/azurerm/resource_arm_storage_queue_test.go index 66ef960de07c..594da6192e6a 100644 --- a/azurerm/resource_arm_storage_queue_test.go +++ b/azurerm/resource_arm_storage_queue_test.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestResourceAzureRMStorageQueueName_Validation(t *testing.T) { @@ -55,7 +57,7 @@ func TestAccAzureRMStorageQueue_basic(t *testing.T) { resourceName := "azurerm_storage_queue.test" ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageQueue_basic(ri, rs, testLocation()) + location := testLocation() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -63,7 +65,7 @@ func TestAccAzureRMStorageQueue_basic(t *testing.T) { CheckDestroy: testCheckAzureRMStorageQueueDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageQueue_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMStorageQueueExists(resourceName), ), @@ -78,7 +80,7 @@ func TestAccAzureRMStorageQueue_basic(t *testing.T) { } func TestAccAzureRMStorageQueue_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -107,39 +109,76 @@ func TestAccAzureRMStorageQueue_requiresImport(t *testing.T) { }) } +func TestAccAzureRMStorageQueue_metaData(t *testing.T) { + resourceName := "azurerm_storage_queue.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageQueue_metaData(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageQueueExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageQueue_metaDataUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageQueueExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testCheckAzureRMStorageQueueExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage queue: %s", name) - } + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - queueClient, accountExists, err := armClient.getQueueServiceClientForStorageAccount(ctx, resourceGroup, storageAccountName) + ctx := testAccProvider.Meta().(*ArmClient).StopContext + storageClient := testAccProvider.Meta().(*ArmClient).Storage + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Queue %q (Account %s): %s", name, accountName, err) } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Queue %q (Account %s) - assuming removed", name, accountName) } - queueReference := queueClient.GetQueueReference(name) - exists, err := queueReference.Exists() + queueClient, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) if err != nil { - return err + return fmt.Errorf("Error building Queues Client: %s", err) } - if !exists { - return fmt.Errorf("Bad: Storage Queue %q (storage account: %q) does not exist", name, storageAccountName) + metaData, err := queueClient.GetMetaData(ctx, accountName, name) + if err != nil { + if utils.ResponseWasNotFound(metaData.Response) { + return fmt.Errorf("Bad: Storage Queue %q (storage account: %q) does not exist", name, accountName) + } + + return fmt.Errorf("Bad: error retrieving Storage Queue %q (storage account: %q): %s", name, accountName, err) } return nil @@ -153,61 +192,47 @@ func testCheckAzureRMStorageQueueDestroy(s *terraform.State) error { } name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage queue: %s", name) - } + accountName := rs.Primary.Attributes["storage_account_name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + storageClient := testAccProvider.Meta().(*ArmClient).Storage - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - queueClient, accountExists, err := armClient.getQueueServiceClientForStorageAccount(ctx, resourceGroup, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return nil + return fmt.Errorf("Error locating Resource Group for Storage Queue %q (Account %s): %s", name, accountName, err) } - if !accountExists { + + // expected if this has been deleted + if resourceGroup == nil { return nil } - queueReference := queueClient.GetQueueReference(name) - exists, err := queueReference.Exists() + queueClient, err := storageClient.QueuesClient(ctx, *resourceGroup, accountName) if err != nil { - return nil + return fmt.Errorf("Error building Queues Client: %s", err) } - if exists { - return fmt.Errorf("Bad: Storage Queue %q (storage account: %q) still exists", name, storageAccountName) + props, err := queueClient.GetMetaData(ctx, accountName, name) + if err != nil { + return nil } + + return fmt.Errorf("Queue still exists: %+v", props) } return nil } func testAccAzureRMStorageQueue_basic(rInt int, rString string, location string) string { + template := testAccAzureRMStorageQueue_template(rInt, rString, location) return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_storage_account" "test" { - name = "acctestacc%s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags = { - environment = "staging" - } -} +%s resource "azurerm_storage_queue" "test" { name = "mysamplequeue-%d" - resource_group_name = "${azurerm_resource_group.test.name}" storage_account_name = "${azurerm_storage_account.test.name}" } -`, rInt, location, rString, rInt) +`, template, rInt) } func testAccAzureRMStorageQueue_requiresImport(rInt int, rString string, location string) string { @@ -217,8 +242,62 @@ func testAccAzureRMStorageQueue_requiresImport(rInt int, rString string, locatio resource "azurerm_storage_queue" "import" { name = "${azurerm_storage_queue.test.name}" - resource_group_name = "${azurerm_storage_queue.test.resource_group_name}" storage_account_name = "${azurerm_storage_queue.test.storage_account_name}" } `, template) } + +func testAccAzureRMStorageQueue_metaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageQueue_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_queue" "test" { + name = "mysamplequeue-%d" + storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" + } +} +`, template, rInt) +} + +func testAccAzureRMStorageQueue_metaDataUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMStorageQueue_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_queue" "test" { + name = "mysamplequeue-%d" + storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" + rick = "M0rty" + } +} +`, template, rInt) +} + +func testAccAzureRMStorageQueue_template(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "staging" + } +} + +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_share.go b/azurerm/resource_arm_storage_share.go index e2cf9365127d..6419581aa712 100644 --- a/azurerm/resource_arm_storage_share.go +++ b/azurerm/resource_arm_storage_share.go @@ -4,14 +4,16 @@ import ( "fmt" "log" "regexp" - "strings" + "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/tf" - - "github.com/Azure/azure-sdk-for-go/storage" - "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/shares" ) func resourceArmStorageShare() *schema.Resource { @@ -23,8 +25,20 @@ func resourceArmStorageShare() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - SchemaVersion: 1, - MigrateState: resourceStorageShareMigrateState, + SchemaVersion: 2, + StateUpgraders: []schema.StateUpgrader{ + { + // this should have been applied from pre-0.12 migration system; backporting just in-case + Type: resourceStorageShareStateResourceV0V1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceStorageShareStateUpgradeV0ToV1, + Version: 0, + }, + { + Type: resourceStorageShareStateResourceV0V1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceStorageShareStateUpgradeV1ToV2, + Version: 1, + }, + }, Schema: map[string]*schema.Schema{ "name": { @@ -33,18 +47,61 @@ func resourceArmStorageShare() *schema.Resource { ForceNew: true, ValidateFunc: validateArmStorageShareName, }, - "resource_group_name": azure.SchemaResourceGroupName(), + + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), + "storage_account_name": { Type: schema.TypeString, Required: true, ForceNew: true, }, + "quota": { Type: schema.TypeInt, Optional: true, Default: 5120, - ValidateFunc: validation.IntBetween(1, 5120), + ValidateFunc: validation.IntBetween(1, 102400), }, + + "metadata": storage.MetaDataSchema(), + + "acl": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "access_policy": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "expiry": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "permissions": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + }, + }, + "url": { Type: schema.TypeString, Computed: true, @@ -53,56 +110,58 @@ func resourceArmStorageShare() *schema.Resource { } } func resourceArmStorageShareCreate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + accountName := d.Get("storage_account_name").(string) + shareName := d.Get("name").(string) + quota := d.Get("quota").(int) - resourceGroupName := d.Get("resource_group_name").(string) - storageAccountName := d.Get("storage_account_name").(string) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + aclsRaw := d.Get("acl").(*schema.Set).List() + acls := expandStorageShareACLs(aclsRaw) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", shareName, accountName, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", shareName, accountName) } - name := d.Get("name").(string) - metaData := make(map[string]string) // TODO: support MetaData - options := &storage.FileRequestOptions{} + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building File Share Client: %s", err) + } - log.Printf("[INFO] Creating share %q in storage account %q", name, storageAccountName) - reference := fileClient.GetShareReference(name) + id := client.GetResourceID(accountName, shareName) - id := fmt.Sprintf("%s/%s/%s", name, resourceGroupName, storageAccountName) - if requireResourcesToBeImported { - exists, e := reference.Exists() - if e != nil { - return fmt.Errorf("Error checking if Share %q exists (Account %q / Resource Group %q): %s", name, storageAccountName, resourceGroupName, e) + if features.ShouldResourcesBeImported() { + existing, err := client.GetProperties(ctx, accountName, shareName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for existence of existing Storage Share %q (Account %q / Resource Group %q): %+v", shareName, accountName, *resourceGroup, err) + } } - if exists { + if !utils.ResponseWasNotFound(existing.Response) { return tf.ImportAsExistsError("azurerm_storage_share", id) } } - err = reference.Create(options) - if err != nil { - return fmt.Errorf("Error creating Storage Share %q reference (storage account: %q) : %+v", name, storageAccountName, err) + log.Printf("[INFO] Creating Share %q in Storage Account %q", shareName, accountName) + input := shares.CreateInput{ + QuotaInGB: quota, + MetaData: metaData, } - - log.Printf("[INFO] Setting share %q metadata in storage account %q", name, storageAccountName) - reference.Metadata = metaData - if err := reference.SetMetadata(options); err != nil { - return fmt.Errorf("Error setting metadata on Storage Share %q: %+v", name, err) + if _, err := client.Create(ctx, accountName, shareName, input); err != nil { + return fmt.Errorf("Error creating Share %q (Account %q / Resource Group %q): %+v", shareName, accountName, *resourceGroup, err) } - log.Printf("[INFO] Setting share %q properties in storage account %q", name, storageAccountName) - reference.Properties = storage.ShareProperties{ - Quota: d.Get("quota").(int), - } - if err := reference.SetProperties(options); err != nil { - return fmt.Errorf("Error setting properties on Storage Share %q: %+v", name, err) + if _, err := client.SetACL(ctx, accountName, shareName, acls); err != nil { + return fmt.Errorf("Error setting ACL's for Share %q (Account %q / Resource Group %q): %+v", shareName, accountName, *resourceGroup, err) } d.SetId(id) @@ -110,124 +169,204 @@ func resourceArmStorageShareCreate(d *schema.ResourceData, meta interface{}) err } func resourceArmStorageShareRead(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - id := strings.Split(d.Id(), "/") - if len(id) != 3 { - return fmt.Errorf("ID was not in the expected format - expected `{name}/{resourceGroup}/{storageAccountName}` got %q", id) + id, err := shares.ParseResourceID(d.Id()) + if err != nil { + return err } - name := id[0] - resourceGroupName := id[1] - storageAccountName := id[2] - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", id.ShareName, id.AccountName, err) } - if !accountExists { - log.Printf("[DEBUG] Storage account %q not found, removing file %q from state", storageAccountName, d.Id()) + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", id.ShareName, id.AccountName) d.SetId("") return nil } - reference := fileClient.GetShareReference(name) - exists, err := reference.Exists() + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return fmt.Errorf("Error testing existence of share %q: %s", name, err) + return fmt.Errorf("Error building File Share Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) } - if !exists { - log.Printf("[INFO] Share %q no longer exists, removing from state...", name) - d.SetId("") - return nil + props, err := client.GetProperties(ctx, id.AccountName, id.ShareName) + if err != nil { + if utils.ResponseWasNotFound(props.Response) { + log.Printf("[DEBUG] File Share %q was not found in Account %q / Resource Group %q - assuming removed & removing from state", id.ShareName, id.AccountName, *resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving File Share %q (Account %q / Resource Group %q): %s", id.ShareName, id.AccountName, *resourceGroup, err) } - url := reference.URL() - if url == "" { - log.Printf("[INFO] URL for %q is empty", name) + acls, err := client.GetACL(ctx, id.AccountName, id.ShareName) + if err != nil { + return fmt.Errorf("Error retrieving ACL's for File Share %q (Account %q / Resource Group %q): %s", id.ShareName, id.AccountName, *resourceGroup, err) } - d.Set("name", name) - d.Set("resource_group_name", resourceGroupName) - d.Set("storage_account_name", storageAccountName) - d.Set("url", url) - if err := reference.FetchAttributes(nil); err != nil { - return fmt.Errorf("Error fetching properties on Storage Share %q: %+v", name, err) + d.Set("name", id.ShareName) + d.Set("storage_account_name", id.AccountName) + d.Set("url", client.GetResourceID(id.AccountName, id.ShareName)) + d.Set("quota", props.ShareQuota) + + if err := d.Set("metadata", storage.FlattenMetaData(props.MetaData)); err != nil { + return fmt.Errorf("Error flattening `metadata`: %+v", err) } - d.Set("quota", reference.Properties.Quota) + + if err := d.Set("acl", flattenStorageShareACLs(acls)); err != nil { + return fmt.Errorf("Error flattening `acl`: %+v", err) + } + + // Deprecated: remove in 2.0 + d.Set("resource_group_name", resourceGroup) return nil } func resourceArmStorageShareUpdate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - id := strings.Split(d.Id(), "/") - if len(id) != 3 { - return fmt.Errorf("ID was not in the expected format - expected `{name}/{resourceGroup}/{storageAccountName}` got %q", id) + id, err := shares.ParseResourceID(d.Id()) + if err != nil { + return err } - name := id[0] - resourceGroupName := id[1] - storageAccountName := id[2] - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", id.ShareName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", id.ShareName, id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building File Share Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + + if d.HasChange("quota") { + log.Printf("[DEBUG] Updating the Quota for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) + quota := d.Get("quota").(int) + if _, err := client.SetProperties(ctx, id.AccountName, id.ShareName, quota); err != nil { + return fmt.Errorf("Error updating Quota for File Share %q (Storage Account %q): %s", id.ShareName, id.AccountName, err) + } + + log.Printf("[DEBUG] Updated the Quota for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) } - options := &storage.FileRequestOptions{} + if d.HasChange("metadata") { + log.Printf("[DEBUG] Updating the MetaData for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) - reference := fileClient.GetShareReference(name) + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) + + if _, err := client.SetMetaData(ctx, id.AccountName, id.ShareName, metaData); err != nil { + return fmt.Errorf("Error updating MetaData for File Share %q (Storage Account %q): %s", id.ShareName, id.AccountName, err) + } - log.Printf("[INFO] Setting share %q properties in storage account %q", name, storageAccountName) - reference.Properties = storage.ShareProperties{ - Quota: d.Get("quota").(int), + log.Printf("[DEBUG] Updated the MetaData for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) } - if err := reference.SetProperties(options); err != nil { - return fmt.Errorf("Error setting properties on Storage Share %q: %+v", name, err) + + if d.HasChange("acl") { + log.Printf("[DEBUG] Updating the ACL's for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) + + aclsRaw := d.Get("acl").(*schema.Set).List() + acls := expandStorageShareACLs(aclsRaw) + + if _, err := client.SetACL(ctx, id.AccountName, id.ShareName, acls); err != nil { + return fmt.Errorf("Error updating ACL's for File Share %q (Storage Account %q): %s", id.ShareName, id.AccountName, err) + } + + log.Printf("[DEBUG] Updated the ACL's for File Share %q (Storage Account %q)", id.ShareName, id.AccountName) } return resourceArmStorageShareRead(d, meta) } func resourceArmStorageShareDelete(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - id := strings.Split(d.Id(), "/") - if len(id) != 3 { - return fmt.Errorf("ID was not in the expected format - expected `{name}/{resourceGroup}/{storageAccountName}` got %q", id) + id, err := shares.ParseResourceID(d.Id()) + if err != nil { + return err } - name := id[0] - resourceGroupName := id[1] - storageAccountName := id[2] - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", id.ShareName, id.AccountName, err) } - if !accountExists { - log.Printf("[INFO]Storage Account %q doesn't exist so the file won't exist", storageAccountName) + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", id.ShareName, id.AccountName) + d.SetId("") return nil } - reference := fileClient.GetShareReference(name) - options := &storage.FileRequestOptions{} + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building File Share Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } - if _, err = reference.DeleteIfExists(options); err != nil { - return fmt.Errorf("Error deleting storage file %q: %s", name, err) + deleteSnapshots := true + if _, err := client.Delete(ctx, id.AccountName, id.ShareName, deleteSnapshots); err != nil { + return fmt.Errorf("Error deleting File Share %q (Storage Account %q / Resource Group %q): %s", id.ShareName, id.AccountName, *resourceGroup, err) } - d.SetId("") return nil } -//Following the naming convention as laid out in the docs https://msdn.microsoft.com/library/azure/dn167011.aspx +func expandStorageShareACLs(input []interface{}) []shares.SignedIdentifier { + results := make([]shares.SignedIdentifier, 0) + + for _, v := range input { + vals := v.(map[string]interface{}) + + policies := vals["access_policy"].([]interface{}) + policy := policies[0].(map[string]interface{}) + + identifier := shares.SignedIdentifier{ + Id: vals["id"].(string), + AccessPolicy: shares.AccessPolicy{ + Start: policy["start"].(string), + Expiry: policy["expiry"].(string), + Permission: policy["permissions"].(string), + }, + } + results = append(results, identifier) + } + + return results +} + +func flattenStorageShareACLs(input shares.GetACLResult) []interface{} { + result := make([]interface{}, 0) + + for _, v := range input.SignedIdentifiers { + output := map[string]interface{}{ + "id": v.Id, + "access_policy": []interface{}{ + map[string]interface{}{ + "start": v.AccessPolicy.Start, + "expiry": v.AccessPolicy.Expiry, + "permissions": v.AccessPolicy.Permission, + }, + }, + } + + result = append(result, output) + } + + return result +} + +// Following the naming convention as laid out in the docs https://msdn.microsoft.com/library/azure/dn167011.aspx func validateArmStorageShareName(v interface{}, k string) (warnings []string, errors []error) { value := v.(string) if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { diff --git a/azurerm/resource_arm_storage_share_directory.go b/azurerm/resource_arm_storage_share_directory.go new file mode 100644 index 000000000000..0d788182712c --- /dev/null +++ b/azurerm/resource_arm_storage_share_directory.go @@ -0,0 +1,232 @@ +package azurerm + +import ( + "context" + "fmt" + "log" + "strconv" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories" +) + +func resourceArmStorageShareDirectory() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageShareDirectoryCreate, + Read: resourceArmStorageShareDirectoryRead, + Update: resourceArmStorageShareDirectoryUpdate, + Delete: resourceArmStorageShareDirectoryDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.StorageShareDirectoryName, + }, + "share_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "metadata": storage.MetaDataSchema(), + }, + } +} + +func resourceArmStorageShareDirectoryCreate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + accountName := d.Get("storage_account_name").(string) + shareName := d.Get("share_name").(string) + directoryName := d.Get("name").(string) + + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", directoryName, shareName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share Directory %q (Share %s, Account %s) ", directoryName, shareName, accountName) + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building File Share Client: %s", err) + } + + if features.ShouldResourcesBeImported() { + existing, err := client.Get(ctx, accountName, shareName, directoryName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Directory %q (File Share %q / Storage Account %q / Resource Group %q): %s", directoryName, shareName, accountName, *resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + id := client.GetResourceID(accountName, shareName, directoryName) + return tf.ImportAsExistsError("azurerm_storage_share_directory", id) + } + } + + if _, err := client.Create(ctx, accountName, shareName, directoryName, metaData); err != nil { + return fmt.Errorf("Error creating Directory %q (File Share %q / Account %q): %+v", directoryName, shareName, accountName, err) + } + + // Storage Share Directories are eventually consistent + log.Printf("[DEBUG] Waiting for Directory %q (File Share %q / Account %q) to become available", directoryName, shareName, accountName) + stateConf := &resource.StateChangeConf{ + Pending: []string{"404"}, + Target: []string{"200"}, + Refresh: storageShareDirectoryRefreshFunc(ctx, client, accountName, shareName, directoryName), + Timeout: 5 * time.Minute, + MinTimeout: 10 * time.Second, + ContinuousTargetOccurence: 5, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for Directory %q (File Share %q / Account %q) to become available: %s", directoryName, shareName, accountName, err) + } + + resourceID := client.GetResourceID(accountName, shareName, directoryName) + d.SetId(resourceID) + + return resourceArmStorageShareDirectoryRead(d, meta) +} + +func resourceArmStorageShareDirectoryUpdate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + id, err := directories.ParseResourceID(d.Id()) + if err != nil { + return err + } + + metaDataRaw := d.Get("metadata").(map[string]interface{}) + metaData := storage.ExpandMetaData(metaDataRaw) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", id.DirectoryName, id.ShareName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share Directory %q (Share %s, Account %s) - assuming removed & removing from state", id.DirectoryName, id.ShareName, id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building File Share Client: %s", err) + } + + if _, err := client.SetMetaData(ctx, id.AccountName, id.ShareName, id.DirectoryName, metaData); err != nil { + return fmt.Errorf("Error updating MetaData for Directory %q (File Share %q / Account %q): %+v", id.DirectoryName, id.ShareName, id.AccountName, err) + } + + return resourceArmStorageShareDirectoryRead(d, meta) +} + +func resourceArmStorageShareDirectoryRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + id, err := directories.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", id.DirectoryName, id.ShareName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share Directory %q (Share %s, Account %s) - assuming removed & removing from state", id.DirectoryName, id.ShareName, id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building File Share Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + props, err := client.Get(ctx, id.AccountName, id.ShareName, id.DirectoryName) + if err != nil { + return fmt.Errorf("Error retrieving Storage Share %q (File Share %q / Account %q / Resource Group %q): %s", id.DirectoryName, id.ShareName, id.AccountName, *resourceGroup, err) + } + + d.Set("name", id.DirectoryName) + d.Set("share_name", id.ShareName) + d.Set("storage_account_name", id.AccountName) + + if err := d.Set("metadata", storage.FlattenMetaData(props.MetaData)); err != nil { + return fmt.Errorf("Error setting `metadata`: %s", err) + } + + return nil +} + +func resourceArmStorageShareDirectoryDelete(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + id, err := directories.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", id.DirectoryName, id.ShareName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Share Directory %q (Share %s, Account %s) - assuming removed already", id.DirectoryName, id.ShareName, id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building File Share Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + if _, err := client.Delete(ctx, id.AccountName, id.ShareName, id.DirectoryName); err != nil { + return fmt.Errorf("Error deleting Storage Share %q (File Share %q / Account %q / Resource Group %q): %s", id.DirectoryName, id.ShareName, id.AccountName, *resourceGroup, err) + } + + return nil +} + +func storageShareDirectoryRefreshFunc(ctx context.Context, client *directories.Client, accountName, shareName, directoryName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, accountName, shareName, directoryName) + if err != nil { + return nil, strconv.Itoa(res.StatusCode), fmt.Errorf("Error retrieving Directory %q (File Share %q / Account %q): %s", directoryName, shareName, accountName, err) + } + + return res, strconv.Itoa(res.StatusCode), nil + } +} diff --git a/azurerm/resource_arm_storage_share_directory_test.go b/azurerm/resource_arm_storage_share_directory_test.go new file mode 100644 index 000000000000..4be64bd9aef2 --- /dev/null +++ b/azurerm/resource_arm_storage_share_directory_test.go @@ -0,0 +1,375 @@ +package azurerm + +import ( + "fmt" + "net/http" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMStorageShareDirectory_basic(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageShareDirectory_uppercase(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_uppercase(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageShareDirectory_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + Config: testAccAzureRMStorageShareDirectory_requiresImport(ri, rs, location), + ExpectError: testRequiresImportError("azurerm_storage_share_directory"), + }, + }, + }) +} + +func TestAccAzureRMStorageShareDirectory_complete(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_complete(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageShareDirectory_update(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_share_directory.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_complete(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageShareDirectory_updated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccAzureRMStorageShareDirectory_nested(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDirectoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShareDirectory_nested(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareDirectoryExists("azurerm_storage_share_directory.parent"), + testCheckAzureRMStorageShareDirectoryExists("azurerm_storage_share_directory.child"), + ), + }, + }, + }) +} + +func testCheckAzureRMStorageShareDirectoryExists(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"] + shareName := rs.Primary.Attributes["share_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", name, shareName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share Directory %q (Share %s, Account %s) ", name, shareName, accountName) + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building FileShare Client: %s", err) + } + + resp, err := client.Get(ctx, accountName, shareName, name) + if err != nil { + return fmt.Errorf("Bad: Get on FileShareDirectoriesClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Directory %q (File Share %q / Account %q / Resource Group %q) does not exist", name, shareName, accountName, *resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMStorageShareDirectoryDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_share_directory" { + continue + } + + name := rs.Primary.Attributes["name"] + shareName := rs.Primary.Attributes["share_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Share Directory %q (Share %s, Account %s): %s", name, shareName, accountName, err) + } + + // not found, the account's gone + if resourceGroup == nil { + return nil + } + + client, err := storageClient.FileShareDirectoriesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building FileShare Client: %s", err) + } + + resp, err := client.Get(ctx, accountName, shareName, name) + if err != nil { + return nil + } + + return fmt.Errorf("File Share still exists:\n%#v", resp) + } + + return nil +} + +func testAccAzureRMStorageShareDirectory_basic(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "test" { + name = "dir" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_uppercase(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "test" { + name = "UpperCaseCharacterS" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_requiresImport(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "import" { + name = "${azurerm_storage_share_directory.test.name}" + share_name = "${azurerm_storage_share_directory.test.share_name}" + storage_account_name = "${azurerm_storage_share_directory.test.storage_account_name}" +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_complete(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "test" { + name = "dir" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" + } +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_updated(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "test" { + name = "dir" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" + sunshine = "at dawn" + } +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_nested(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShareDirectory_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share_directory" "parent" { + name = "parent" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_storage_share_directory" "child" { + name = "${azurerm_storage_share_directory.parent.name}/child" + share_name = "${azurerm_storage_share.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, template) +} + +func testAccAzureRMStorageShareDirectory_template(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_share" "test" { + name = "fileshare" + storage_account_name = "${azurerm_storage_account.test.name}" + quota = 50 +} +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_storage_share_migration.go b/azurerm/resource_arm_storage_share_migration.go index f2bd6deccc46..d4355c495133 100644 --- a/azurerm/resource_arm_storage_share_migration.go +++ b/azurerm/resource_arm_storage_share_migration.go @@ -3,37 +3,76 @@ package azurerm import ( "fmt" "log" + "strings" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/shares" ) -func resourceStorageShareMigrateState( - v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) { - switch v { - case 0: - log.Println("[INFO] Found AzureRM Storage Share State v0; migrating to v1") - return migrateStorageShareStateV0toV1(is) - default: - return is, fmt.Errorf("Unexpected schema version: %d", v) +// the schema schema was used for both V0 and V1 +func resourceStorageShareStateResourceV0V1() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageShareName, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "quota": { + Type: schema.TypeInt, + Optional: true, + Default: 5120, + ValidateFunc: validation.IntBetween(1, 5120), + }, + "url": { + Type: schema.TypeString, + Computed: true, + }, + }, } } -func migrateStorageShareStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { - if is.Empty() { - log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") - return is, nil +func resourceStorageShareStateUpgradeV0ToV1(rawState map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + shareName := rawState["name"].(string) + resourceGroup := rawState["resource_group_name"].(string) + accountName := rawState["storage_account_name"].(string) + + id := rawState["id"].(string) + newResourceID := fmt.Sprintf("%s/%s/%s", shareName, resourceGroup, accountName) + log.Printf("[DEBUG] Updating ID from %q to %q", id, newResourceID) + + rawState["id"] = newResourceID + return rawState, nil +} + +func resourceStorageShareStateUpgradeV1ToV2(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + id := rawState["id"].(string) + + // name/resourceGroup/accountName + parsedId := strings.Split(id, "/") + if len(parsedId) != 3 { + return rawState, fmt.Errorf("Expected 3 segments in the ID but got %d", len(parsedId)) } - log.Printf("[DEBUG] ARM Storage Share Attributes before Migration: %#v", is.Attributes) + shareName := parsedId[0] + accountName := parsedId[2] + + environment := meta.(*ArmClient).environment + client := shares.NewWithEnvironment(environment) - name := is.Attributes["name"] - resourceGroupName := is.Attributes["resource_group_name"] - storageAccountName := is.Attributes["storage_account_name"] - newID := fmt.Sprintf("%s/%s/%s", name, resourceGroupName, storageAccountName) - is.Attributes["id"] = newID - is.ID = newID + newResourceId := client.GetResourceID(accountName, shareName) + log.Printf("[DEBUG] Updating Resource ID from %q to %q", id, newResourceId) - log.Printf("[DEBUG] ARM Storage Share Attributes after State Migration: %#v", is.Attributes) + rawState["id"] = newResourceId - return is, nil + return rawState, nil } diff --git a/azurerm/resource_arm_storage_share_migration_test.go b/azurerm/resource_arm_storage_share_migration_test.go index 378ba12030b4..b97526eb1322 100644 --- a/azurerm/resource_arm_storage_share_migration_test.go +++ b/azurerm/resource_arm_storage_share_migration_test.go @@ -1,49 +1,93 @@ package azurerm import ( + "fmt" + "reflect" "testing" - "github.com/hashicorp/terraform/terraform" + "github.com/Azure/go-autorest/autorest/azure" ) -func TestAzureRMStorageShareMigrateState(t *testing.T) { - cases := map[string]struct { - StateVersion int - ID string - InputAttributes map[string]string - ExpectedAttributes map[string]string - Meta interface{} - }{ - "v0_1": { - StateVersion: 0, - ID: "some_id", - InputAttributes: map[string]string{ - "name": "some_id", - "resource_group_name": "some_rgn", - "storage_account_name": "some_sgn", - }, - ExpectedAttributes: map[string]string{ - "id": "some_id/some_rgn/some_sgn", - }, - }, +func TestAzureRMStorageShareMigrateStateV0ToV1(t *testing.T) { + clouds := []azure.Environment{ + azure.ChinaCloud, + azure.GermanCloud, + azure.PublicCloud, + azure.USGovernmentCloud, } - for tn, tc := range cases { - is := &terraform.InstanceState{ - ID: tc.ID, - Attributes: tc.InputAttributes, + for _, cloud := range clouds { + t.Logf("[DEBUG] Testing with Cloud %q", cloud.Name) + + input := map[string]interface{}{ + "id": "share1", + "name": "share1", + "resource_group_name": "group1", + "storage_account_name": "account1", + "quota": 5120, + } + meta := &ArmClient{ + environment: cloud, + } + expected := map[string]interface{}{ + "id": "share1/group1/account1", + "name": "share1", + "resource_group_name": "group1", + "storage_account_name": "account1", + "quota": 5120, } - is, err := resourceStorageShareMigrateState(tc.StateVersion, is, tc.Meta) + actual, err := resourceStorageShareStateUpgradeV0ToV1(input, meta) if err != nil { - t.Fatalf("bad: %s, err: %#v", tn, err) + t.Fatalf("Expected no error but got: %s", err) } - for k, v := range tc.ExpectedAttributes { - actual := is.Attributes[k] - if actual != v { - t.Fatalf("Bad Storage Share Migrate for %q: %q\n\n expected: %q", k, actual, v) - } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected %+v. Got %+v. But expected them to be the same", expected, actual) } + + t.Logf("[DEBUG] Ok!") + } +} + +func TestAzureRMStorageShareMigrateStateV1ToV2(t *testing.T) { + clouds := []azure.Environment{ + azure.ChinaCloud, + azure.GermanCloud, + azure.PublicCloud, + azure.USGovernmentCloud, + } + + for _, cloud := range clouds { + t.Logf("[DEBUG] Testing with Cloud %q", cloud.Name) + + input := map[string]interface{}{ + "id": "share1/group1/account1", + "name": "share1", + "resource_group_name": "group1", + "storage_account_name": "account1", + "quota": 5120, + } + meta := &ArmClient{ + environment: cloud, + } + expected := map[string]interface{}{ + "id": fmt.Sprintf("https://account1.file.%s/share1", cloud.StorageEndpointSuffix), + "name": "share1", + "resource_group_name": "group1", + "storage_account_name": "account1", + "quota": 5120, + } + + actual, err := resourceStorageShareStateUpgradeV1ToV2(input, meta) + if err != nil { + t.Fatalf("Expected no error but got: %s", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected %+v. Got %+v. But expected them to be the same", expected, actual) + } + + t.Logf("[DEBUG] Ok!") } } diff --git a/azurerm/resource_arm_storage_share_test.go b/azurerm/resource_arm_storage_share_test.go index 4dc3a50879a7..deb257d2f4ed 100644 --- a/azurerm/resource_arm_storage_share_test.go +++ b/azurerm/resource_arm_storage_share_test.go @@ -2,23 +2,20 @@ package azurerm import ( "fmt" - "log" "strings" "testing" - "github.com/Azure/azure-sdk-for-go/storage" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMStorageShare_basic(t *testing.T) { - var sS storage.Share - ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageShare_basic(ri, rs, testLocation()) + location := testLocation() resourceName := "azurerm_storage_share.test" resource.ParallelTest(t, resource.TestCase{ @@ -27,9 +24,9 @@ func TestAccAzureRMStorageShare_basic(t *testing.T) { CheckDestroy: testCheckAzureRMStorageShareDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageShare_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageShareExists(resourceName, &sS), + testCheckAzureRMStorageShareExists(resourceName), ), }, { @@ -42,13 +39,11 @@ func TestAccAzureRMStorageShare_basic(t *testing.T) { } func TestAccAzureRMStorageShare_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } - var sS storage.Share - ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) location := testLocation() @@ -62,7 +57,7 @@ func TestAccAzureRMStorageShare_requiresImport(t *testing.T) { { Config: testAccAzureRMStorageShare_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageShareExists(resourceName, &sS), + testCheckAzureRMStorageShareExists(resourceName), ), }, { @@ -74,11 +69,9 @@ func TestAccAzureRMStorageShare_requiresImport(t *testing.T) { } func TestAccAzureRMStorageShare_disappears(t *testing.T) { - var sS storage.Share - ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageShare_basic(ri, rs, testLocation()) + location := testLocation() resourceName := "azurerm_storage_share.test" resource.ParallelTest(t, resource.TestCase{ @@ -87,10 +80,10 @@ func TestAccAzureRMStorageShare_disappears(t *testing.T) { CheckDestroy: testCheckAzureRMStorageShareDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageShare_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageShareExists(resourceName, &sS), - testAccARMStorageShareDisappears(resourceName, &sS), + testCheckAzureRMStorageShareExists(resourceName), + testCheckAzureRMStorageShareDisappears(resourceName), ), ExpectNonEmptyPlan: true, }, @@ -98,13 +91,84 @@ func TestAccAzureRMStorageShare_disappears(t *testing.T) { }) } -func TestAccAzureRMStorageShare_updateQuota(t *testing.T) { - var sS storage.Share +func TestAccAzureRMStorageShare_metaData(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + resourceName := "azurerm_storage_share.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShare_metaData(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageShare_metaDataUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageShare_acl(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + resourceName := "azurerm_storage_share.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageShareDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageShare_acl(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageShare_aclUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageShareExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccAzureRMStorageShare_updateQuota(t *testing.T) { ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) - config := testAccAzureRMStorageShare_basic(ri, rs, testLocation()) - config2 := testAccAzureRMStorageShare_updateQuota(ri, rs, testLocation()) + location := testLocation() resourceName := "azurerm_storage_share.test" resource.ParallelTest(t, resource.TestCase{ @@ -113,15 +177,15 @@ func TestAccAzureRMStorageShare_updateQuota(t *testing.T) { CheckDestroy: testCheckAzureRMStorageShareDestroy, Steps: []resource.TestStep{ { - Config: config, + Config: testAccAzureRMStorageShare_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageShareExists(resourceName, &sS), + testCheckAzureRMStorageShareExists(resourceName), ), }, { - Config: config2, + Config: testAccAzureRMStorageShare_updateQuota(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageShareExists(resourceName, &sS), + testCheckAzureRMStorageShareExists(resourceName), resource.TestCheckResourceAttr(resourceName, "quota", "5"), ), }, @@ -129,89 +193,68 @@ func TestAccAzureRMStorageShare_updateQuota(t *testing.T) { }) } -func testCheckAzureRMStorageShareExists(resourceName string, sS *storage.Share) resource.TestCheckFunc { +func testCheckAzureRMStorageShareExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroupName, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for share: %s", name) - } + shareName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) - if err != nil { - return err - } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) - } + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext - shares, err := fileClient.ListShares(storage.ListSharesParameters{ - Prefix: name, - Timeout: 90, - }) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return fmt.Errorf("Error listing Storage Share %q shares (storage account: %q) : %+v", name, storageAccountName, err) + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", shareName, accountName, err) } - - if len(shares.Shares) == 0 { - return fmt.Errorf("Bad: Share %q (storage account: %q) does not exist", name, storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", shareName, accountName) } - var found bool - for _, share := range shares.Shares { - if share.Name == name { - found = true - *sS = share - } + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building FileShare Client: %s", err) } - if !found { - return fmt.Errorf("Bad: Share %q (storage account: %q) does not exist", name, storageAccountName) + if _, err = client.GetProperties(ctx, accountName, shareName); err != nil { + return fmt.Errorf("Bad: Share %q (Storage Account: %q) does not exist", shareName, accountName) } return nil } } -func testAccARMStorageShareDisappears(resourceName string, sS *storage.Share) resource.TestCheckFunc { +func testCheckAzureRMStorageShareDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext + shareName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroupName, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage share: %s", sS.Name) - } + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", shareName, accountName, err) } - if !accountExists { - log.Printf("[INFO]Storage Account %q doesn't exist so the share won't exist", storageAccountName) - return nil + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", shareName, accountName) } - reference := fileClient.GetShareReference(sS.Name) - options := &storage.FileRequestOptions{} + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building FileShare Client: %s", err) + } - if _, err = reference.DeleteIfExists(options); err != nil { - return fmt.Errorf("Error deleting storage Share %q: %s", sS.Name, err) + if _, err := client.Delete(ctx, accountName, shareName, true); err != nil { + return fmt.Errorf("Error deleting Share %q (Account %q): %v", shareName, accountName, err) } return nil @@ -224,75 +267,138 @@ func testCheckAzureRMStorageShareDestroy(s *terraform.State) error { continue } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroupName, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for share: %s", name) - } + shareName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - fileClient, accountExists, err := armClient.getFileServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - //If we can't get keys then the blob can't exist - return nil + return fmt.Errorf("Error locating Resource Group for Storage Share %q (Account %s): %s", shareName, accountName, err) } - if !accountExists { + if resourceGroup == nil { return nil } - shares, err := fileClient.ListShares(storage.ListSharesParameters{ - Prefix: name, - Timeout: 90, - }) - + client, err := storageClient.FileSharesClient(ctx, *resourceGroup, accountName) if err != nil { - return nil + return fmt.Errorf("Error building FileShare Client: %s", err) } - var found bool - for _, share := range shares.Shares { - if share.Name == name { - found = true - } + props, err := client.GetProperties(ctx, accountName, shareName) + if err != nil { + return nil } - if found { - return fmt.Errorf("Bad: Share %q (storage account: %q) still exists", name, storageAccountName) - } + return fmt.Errorf("Share still exists: %+v", props) } return nil } func testAccAzureRMStorageShare_basic(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%[1]d" - location = "%[2]s" +%s + +resource "azurerm_storage_share" "test" { + name = "testshare%s" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, template, rString) } -resource "azurerm_storage_account" "test" { - name = "acctestacc%[3]s" - resource_group_name = "${azurerm_resource_group.test.name}" - location = "${azurerm_resource_group.test.location}" - account_tier = "Standard" - account_replication_type = "LRS" +func testAccAzureRMStorageShare_metaData(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) + return fmt.Sprintf(` +%s - tags = { - environment = "staging" +resource "azurerm_storage_share" "test" { + name = "testshare%s" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" } } +`, template, rString) +} + +func testAccAzureRMStorageShare_metaDataUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) + return fmt.Sprintf(` +%s resource "azurerm_storage_share" "test" { - name = "testshare%[3]s" + name = "testshare%s" resource_group_name = "${azurerm_resource_group.test.name}" storage_account_name = "${azurerm_storage_account.test.name}" + + metadata = { + hello = "world" + happy = "birthday" + } } -`, rInt, location, rString) +`, template, rString) } +func testAccAzureRMStorageShare_acl(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share" "test" { + name = "testshare%s" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + acl { + id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "rwd" + start = "2019-07-02T09:38:21.0000000Z" + expiry = "2019-07-02T10:38:21.0000000Z" + } + } +} +`, template, rString) +} + +func testAccAzureRMStorageShare_aclUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share" "test" { + name = "testshare%s" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + acl { + id = "AAAANDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "rwd" + start = "2019-07-02T09:38:21.0000000Z" + expiry = "2019-07-02T10:38:21.0000000Z" + } + } + acl { + id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "rwd" + start = "2019-07-02T09:38:21.0000000Z" + expiry = "2019-07-02T10:38:21.0000000Z" + } + } +} +`, template, rString) +} func testAccAzureRMStorageShare_requiresImport(rInt int, rString string, location string) string { template := testAccAzureRMStorageShare_basic(rInt, rString, location) return fmt.Sprintf(` @@ -307,14 +413,28 @@ resource "azurerm_storage_share" "import" { } func testAccAzureRMStorageShare_updateQuota(rInt int, rString string, location string) string { + template := testAccAzureRMStorageShare_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_share" "test" { + name = "testshare%s" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + quota = 5 +} +`, template, rString) +} + +func testAccAzureRMStorageShare_template(rInt int, rString string, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-%[1]d" - location = "%[2]s" + name = "acctestRG-%d" + location = "%s" } resource "azurerm_storage_account" "test" { - name = "acctestacc%[3]s" + name = "acctestacc%s" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" account_tier = "Standard" @@ -324,13 +444,6 @@ resource "azurerm_storage_account" "test" { environment = "staging" } } - -resource "azurerm_storage_share" "test" { - name = "testshare%[3]s" - resource_group_name = "${azurerm_resource_group.test.name}" - storage_account_name = "${azurerm_storage_account.test.name}" - quota = 5 -} `, rInt, location, rString) } diff --git a/azurerm/resource_arm_storage_table.go b/azurerm/resource_arm_storage_table.go index 4b9f79e70f38..632859473ff6 100644 --- a/azurerm/resource_arm_storage_table.go +++ b/azurerm/resource_arm_storage_table.go @@ -3,14 +3,16 @@ package azurerm import ( "fmt" "log" - "net/url" "regexp" - "strings" - "github.com/Azure/azure-sdk-for-go/storage" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/tables" ) func resourceArmStorageTable() *schema.Resource { @@ -18,11 +20,24 @@ func resourceArmStorageTable() *schema.Resource { Create: resourceArmStorageTableCreate, Read: resourceArmStorageTableRead, Delete: resourceArmStorageTableDelete, + Update: resourceArmStorageTableUpdate, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - SchemaVersion: 1, - MigrateState: resourceStorageTableMigrateState, + SchemaVersion: 2, + StateUpgraders: []schema.StateUpgrader{ + { + // this should have been applied from pre-0.12 migration system; backporting just in-case + Type: resourceStorageTableStateResourceV0V1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceStorageTableStateUpgradeV0ToV1, + Version: 0, + }, + { + Type: resourceStorageTableStateResourceV0V1().CoreConfigSchema().ImpliedType(), + Upgrade: resourceStorageTableStateUpgradeV1ToV2, + Version: 1, + }, + }, Schema: map[string]*schema.Schema{ "name": { @@ -31,73 +46,100 @@ func resourceArmStorageTable() *schema.Resource { ForceNew: true, ValidateFunc: validateArmStorageTableName, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "storage_account_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, }, - }, - } -} -func validateArmStorageTableName(v interface{}, k string) (warnings []string, errors []error) { - value := v.(string) - if value == "table" { - errors = append(errors, fmt.Errorf( - "Table Storage %q cannot use the word `table`: %q", - k, value)) - } - if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]{2,62}$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "Table Storage %q cannot begin with a numeric character, only alphanumeric characters are allowed and must be between 3 and 63 characters long: %q", - k, value)) + // TODO: deprecate this in the docs + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), + + "acl": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "access_policy": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "expiry": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "permissions": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + }, + }, + }, } - - return warnings, errors } func resourceArmStorageTableCreate(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext - environment := armClient.environment + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage - name := d.Get("name").(string) - resourceGroupName := d.Get("resource_group_name").(string) - storageAccountName := d.Get("storage_account_name").(string) + tableName := d.Get("name").(string) + accountName := d.Get("storage_account_name").(string) + aclsRaw := d.Get("acl").(*schema.Set).List() + acls := expandStorageTableACLs(aclsRaw) - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, resourceGroupName, storageAccountName) + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", tableName, accountName, err) } - if !accountExists { - return fmt.Errorf("Storage Account %q Not Found", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Share %q (Account %s) - assuming removed & removing from state", tableName, accountName) } - table := tableClient.GetTableReference(name) - id := fmt.Sprintf("https://%s.table.%s/%s", storageAccountName, environment.StorageEndpointSuffix, name) + client, err := storageClient.TablesClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Table Client: %s", err) + } - if requireResourcesToBeImported { - metaDataLevel := storage.MinimalMetadata - options := &storage.QueryTablesOptions{} - tables, e := tableClient.QueryTables(metaDataLevel, options) - if e != nil { - return fmt.Errorf("Error checking if Table %q exists (Account %q / Resource Group %q): %s", name, storageAccountName, resourceGroupName, e) + id := client.GetResourceID(accountName, tableName) + if features.ShouldResourcesBeImported() { + existing, err := client.Exists(ctx, accountName, tableName) + if err != nil { + if !utils.ResponseWasNotFound(existing) { + return fmt.Errorf("Error checking for existence of existing Storage Table %q (Account %q / Resource Group %q): %+v", tableName, accountName, *resourceGroup, err) + } } - for _, table := range tables.Tables { - if table.Name == name { - return tf.ImportAsExistsError("azurerm_storage_table", id) - } + if !utils.ResponseWasNotFound(existing) { + return tf.ImportAsExistsError("azurerm_storage_table", id) } } - log.Printf("[INFO] Creating table %q in storage account %q.", name, storageAccountName) - timeout := uint(60) - options := &storage.TableOptions{} - err = table.Create(timeout, storage.NoMetadata, options) - if err != nil { - return fmt.Errorf("Error creating table %q in storage account %q: %s", name, storageAccountName, err) + log.Printf("[DEBUG] Creating Table %q in Storage Account %q.", tableName, accountName) + if _, err := client.Create(ctx, accountName, tableName); err != nil { + return fmt.Errorf("Error creating Table %q within Storage Account %q: %s", tableName, accountName, err) + } + + if _, err := client.SetACL(ctx, accountName, tableName, acls); err != nil { + return fmt.Errorf("Error setting ACL's for Storage Table %q (Account %q / Resource Group %q): %+v", tableName, accountName, *resourceGroup, err) } d.SetId(id) @@ -105,126 +147,186 @@ func resourceArmStorageTableCreate(d *schema.ResourceData, meta interface{}) err } func resourceArmStorageTableRead(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageTableID(d.Id()) + id, err := tables.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", id.TableName, id.AccountName, err) } if resourceGroup == nil { - log.Printf("Unable to determine Resource Group for Storage Account %q (assuming removed)", id.storageAccountName) + log.Printf("Unable to determine Resource Group for Storage Storage Table %q (Account %s) - assuming removed & removing from state", id.TableName, id.AccountName) d.SetId("") return nil } - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + client, err := storageClient.TablesClient(ctx, *resourceGroup, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error building Table Client: %s", err) } - if !accountExists { - log.Printf("[DEBUG] Storage account %q not found, removing table %q from state", id.storageAccountName, id.tableName) - d.SetId("") - return nil - } - - metaDataLevel := storage.MinimalMetadata - options := &storage.QueryTablesOptions{} - tables, err := tableClient.QueryTables(metaDataLevel, options) + exists, err := client.Exists(ctx, id.AccountName, id.TableName) if err != nil { - return fmt.Errorf("Failed to retrieve Tables in Storage Account %q: %s", id.tableName, err) - } - - var storageTable *storage.Table - for _, table := range tables.Tables { - if table.Name == id.tableName { - storageTable = &table - break + if utils.ResponseWasNotFound(exists) { + log.Printf("[DEBUG] Storage Account %q not found, removing table %q from state", id.AccountName, id.TableName) + d.SetId("") + return nil } + + return fmt.Errorf("Error retrieving Table %q in Storage Account %q: %s", id.TableName, id.AccountName, err) } - if storageTable == nil { - log.Printf("[INFO] Table %q does not exist in Storage Account %q, removing from state...", id.tableName, id.storageAccountName) - d.SetId("") - return nil + acls, err := client.GetACL(ctx, id.AccountName, id.TableName) + if err != nil { + return fmt.Errorf("Error retrieving ACL's %q in Storage Account %q: %s", id.TableName, id.AccountName, err) } - d.Set("name", id.tableName) - d.Set("storage_account_name", id.storageAccountName) + d.Set("name", id.TableName) + d.Set("storage_account_name", id.AccountName) d.Set("resource_group_name", resourceGroup) + if err := d.Set("acl", flattenStorageTableACLs(acls)); err != nil { + return fmt.Errorf("Error flattening `acl`: %+v", err) + } + return nil } func resourceArmStorageTableDelete(d *schema.ResourceData, meta interface{}) error { - armClient := meta.(*ArmClient) - ctx := armClient.StopContext + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext - id, err := parseStorageTableID(d.Id()) + id, err := tables.ParseResourceID(d.Id()) if err != nil { return err } - resourceGroup, err := determineResourceGroupForStorageAccount(id.storageAccountName, armClient) + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", id.TableName, id.AccountName, err) } if resourceGroup == nil { - log.Printf("Unable to determine Resource Group for Storage Account %q (assuming removed)", id.storageAccountName) + log.Printf("Unable to determine Resource Group for Storage Storage Table %q (Account %s) - assuming removed & removing from state", id.TableName, id.AccountName) return nil } - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, *resourceGroup, id.storageAccountName) + client, err := storageClient.TablesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Table Client: %s", err) + } + + log.Printf("[INFO] Deleting Table %q in Storage Account %q", id.TableName, id.AccountName) + if _, err := client.Delete(ctx, id.AccountName, id.TableName); err != nil { + return fmt.Errorf("Error deleting Table %q from Storage Account %q: %s", id.TableName, id.AccountName, err) + } + + return nil +} + +func resourceArmStorageTableUpdate(d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*ArmClient).Storage + ctx := meta.(*ArmClient).StopContext + + id, err := tables.ParseResourceID(d.Id()) if err != nil { return err } - if !accountExists { - log.Printf("[INFO] Storage Account %q doesn't exist so the table won't exist", id.storageAccountName) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", id.TableName, id.AccountName, err) + } + + if resourceGroup == nil { + log.Printf("Unable to determine Resource Group for Storage Storage Table %q (Account %s) - assuming removed & removing from state", id.TableName, id.AccountName) + d.SetId("") return nil } - table := tableClient.GetTableReference(id.tableName) - timeout := uint(60) - options := &storage.TableOptions{} + client, err := storageClient.TablesClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Table Client: %s", err) + } + + if d.HasChange("acl") { + log.Printf("[DEBUG] Updating the ACL's for Storage Table %q (Storage Account %q)", id.TableName, id.AccountName) + + aclsRaw := d.Get("acl").(*schema.Set).List() + acls := expandStorageTableACLs(aclsRaw) + + if _, err := client.SetACL(ctx, id.AccountName, id.TableName, acls); err != nil { + return fmt.Errorf("Error updating ACL's for Storage Table %q (Storage Account %q): %s", id.TableName, id.AccountName, err) + } - log.Printf("[INFO] Deleting Table %q in Storage Account %q", id.tableName, id.storageAccountName) - if err := table.Delete(timeout, options); err != nil { - return fmt.Errorf("Error deleting table %q from Storage Account %q: %s", id.tableName, id.storageAccountName, err) + log.Printf("[DEBUG] Updated the ACL's for Storage Table %q (Storage Account %q)", id.TableName, id.AccountName) } - return nil + return resourceArmStorageTableRead(d, meta) } -type storageTableId struct { - storageAccountName string - tableName string +func validateArmStorageTableName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + if value == "table" { + errors = append(errors, fmt.Errorf( + "Table Storage %q cannot use the word `table`: %q", + k, value)) + } + if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]{2,62}$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "Table Storage %q cannot begin with a numeric character, only alphanumeric characters are allowed and must be between 3 and 63 characters long: %q", + k, value)) + } + + return warnings, errors } -func parseStorageTableID(input string) (*storageTableId, error) { - // https://myaccount.table.core.windows.net/table1 - uri, err := url.Parse(input) - if err != nil { - return nil, fmt.Errorf("Error parsing %q as a URI: %+v", input, err) +func expandStorageTableACLs(input []interface{}) []tables.SignedIdentifier { + results := make([]tables.SignedIdentifier, 0) + + for _, v := range input { + vals := v.(map[string]interface{}) + + policies := vals["access_policy"].([]interface{}) + policy := policies[0].(map[string]interface{}) + + identifier := tables.SignedIdentifier{ + Id: vals["id"].(string), + AccessPolicy: tables.AccessPolicy{ + Start: policy["start"].(string), + Expiry: policy["expiry"].(string), + Permission: policy["permissions"].(string), + }, + } + results = append(results, identifier) } - segments := strings.Split(uri.Host, ".") - if len(segments) > 0 { - storageAccountName := segments[0] - table := strings.Replace(uri.Path, "/", "", 1) - id := storageTableId{ - storageAccountName: storageAccountName, - tableName: table, + return results +} + +func flattenStorageTableACLs(input tables.GetACLResult) []interface{} { + result := make([]interface{}, 0) + + for _, v := range input.SignedIdentifiers { + output := map[string]interface{}{ + "id": v.Id, + "access_policy": []interface{}{ + map[string]interface{}{ + "start": v.AccessPolicy.Start, + "expiry": v.AccessPolicy.Expiry, + "permissions": v.AccessPolicy.Permission, + }, + }, } - return &id, nil + + result = append(result, output) } - return nil, nil + return result } diff --git a/azurerm/resource_arm_storage_table_entity.go b/azurerm/resource_arm_storage_table_entity.go new file mode 100644 index 000000000000..7f1e2288b1cc --- /dev/null +++ b/azurerm/resource_arm_storage_table_entity.go @@ -0,0 +1,213 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" +) + +func resourceArmStorageTableEntity() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageTableEntityCreateUpdate, + Read: resourceArmStorageTableEntityRead, + Update: resourceArmStorageTableEntityCreateUpdate, + Delete: resourceArmStorageTableEntityDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "table_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageTableName, + }, + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, + }, + "partition_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "row_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "entity": { + Type: schema.TypeMap, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceArmStorageTableEntityCreateUpdate(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + accountName := d.Get("storage_account_name").(string) + tableName := d.Get("table_name").(string) + partitionKey := d.Get("partition_key").(string) + rowKey := d.Get("row_key").(string) + entity := d.Get("entity").(map[string]interface{}) + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Stable %q (Account %s): %s", tableName, accountName, err) + } + if resourceGroup == nil { + if d.IsNewResource() { + return fmt.Errorf("Unable to locate Resource Group for Storage Table %q (Account %s)", tableName, accountName) + } else { + log.Printf("[DEBUG] Unable to locate Resource Group for Storage Table %q (Account %s) - assuming removed & removing from state", tableName, accountName) + d.SetId("") + return nil + } + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Entity Client: %s", err) + } + + if features.ShouldResourcesBeImported() { + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + MetaDataLevel: entities.NoMetaData, + } + existing, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + id := client.GetResourceID(accountName, tableName, partitionKey, rowKey) + return tf.ImportAsExistsError("azurerm_storage_table_entity", id) + } + } + + input := entities.InsertOrMergeEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + Entity: entity, + } + + if _, err := client.InsertOrMerge(ctx, accountName, tableName, input); err != nil { + return fmt.Errorf("Error creating Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %+v", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) + } + + resourceID := client.GetResourceID(accountName, tableName, partitionKey, rowKey) + d.SetId(resourceID) + + return resourceArmStorageTableEntityRead(d, meta) +} + +func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + id, err := entities.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", id.TableName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Table %q (Account %s) - assuming removed & removing from state", id.TableName, id.AccountName) + d.SetId("") + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Table Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + input := entities.GetEntityInput{ + PartitionKey: id.PartitionKey, + RowKey: id.RowKey, + MetaDataLevel: entities.NoMetaData, + } + + result, err := client.Get(ctx, id.AccountName, id.TableName, input) + if err != nil { + return fmt.Errorf("Error retrieving Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } + + d.Set("storage_account_name", id.AccountName) + d.Set("table_name", id.TableName) + d.Set("partition_key", id.PartitionKey) + d.Set("row_key", id.RowKey) + if err := d.Set("entity", flattenEntity(result.Entity)); err != nil { + return fmt.Errorf("Error setting `entity` for Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } + + return nil +} + +func resourceArmStorageTableEntityDelete(d *schema.ResourceData, meta interface{}) error { + ctx := meta.(*ArmClient).StopContext + storageClient := meta.(*ArmClient).Storage + + id, err := entities.ParseResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", id.TableName, id.AccountName, err) + } + if resourceGroup == nil { + log.Printf("[WARN] Unable to determine Resource Group for Storage Table %q (Account %s) - assuming removed already", id.TableName, id.AccountName) + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) + if err != nil { + return fmt.Errorf("Error building Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) + } + + input := entities.DeleteEntityInput{ + PartitionKey: id.PartitionKey, + RowKey: id.RowKey, + } + + if _, err := client.Delete(ctx, id.AccountName, id.TableName, input); err != nil { + return fmt.Errorf("Error deleting Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) + } + + return nil +} + +// The api returns extra information that we already have. We'll remove it here before setting it in state. +func flattenEntity(entity map[string]interface{}) map[string]interface{} { + delete(entity, "PartitionKey") + delete(entity, "RowKey") + delete(entity, "Timestamp") + + return entity +} diff --git a/azurerm/resource_arm_storage_table_entity_test.go b/azurerm/resource_arm_storage_table_entity_test.go new file mode 100644 index 000000000000..b968c3916ce2 --- /dev/null +++ b/azurerm/resource_arm_storage_table_entity_test.go @@ -0,0 +1,279 @@ +package azurerm + +import ( + "fmt" + "net/http" + "strings" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" +) + +func TestAccAzureRMTableEntity_basic(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMTableEntity_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + Config: testAccAzureRMTableEntity_requiresImport(ri, rs, location), + ExpectError: testRequiresImportError("azurerm_storage_table_entity"), + }, + }, + }) +} + +func TestAccAzureRMTableEntity_update(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(5)) + location := testLocation() + resourceName := "azurerm_storage_table_entity.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMTableEntityDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMTableEntity_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMTableEntity_updated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMTableEntityExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMTableEntityExists(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) + } + + tableName := rs.Primary.Attributes["table_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + partitionKey := rs.Primary.Attributes["partition_key"] + rowKey := rs.Primary.Attributes["row_key"] + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table Entity (Partition Key %q / Row Key %q) (Table %q / Account %q): %v", partitionKey, rowKey, tableName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Table Entity (Partition Key %q / Row Key %q) (Table %q / Account %q)", partitionKey, rowKey, tableName, accountName) + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building Table Entity Client: %s", err) + } + + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + MetaDataLevel: entities.NoMetaData, + } + resp, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + return fmt.Errorf("Bad: Get on Table EntityClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Entity (Partition Key %q / Row Key %q) (Table %q / Account %q / Resource Group %q) does not exist", partitionKey, rowKey, tableName, accountName, *resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMTableEntityDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_table_entity" { + continue + } + + tableName := rs.Primary.Attributes["table_name"] + accountName := rs.Primary.Attributes["storage_account_name"] + partitionKey := rs.Primary.Attributes["parititon_key"] + rowKey := rs.Primary.Attributes["row_key"] + + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table Entity (Partition Key %q / Row Key %q) (Table %q / Account %q / Resource Group %q): %v", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) + } + + // not found, the account's gone + if resourceGroup == nil { + return nil + } + + client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) + if err != nil { + return fmt.Errorf("Error building TableEntity Client: %s", err) + } + + input := entities.GetEntityInput{ + PartitionKey: partitionKey, + RowKey: rowKey, + } + resp, err := client.Get(ctx, accountName, tableName, input) + if err != nil { + return fmt.Errorf("Bad: Get on Table Entity: %+v", err) + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Table Entity still exists:\n%#v", resp) + } + } + + return nil +} + +func testAccAzureRMTableEntity_basic(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" + entity = { + Foo = "Bar" + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_requiresImport(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_basic(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" + entity = { + Foo = "Bar" + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_updated(rInt int, rString string, location string) string { + template := testAccAzureRMTableEntity_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_table_entity" "test" { + storage_account_name = "${azurerm_storage_account.test.name}" + table_name = "${azurerm_storage_table.test.name}" + + partition_key = "test_partition%d" + row_key = "test_row%d" + entity = { + Foo = "Bar" + Test = "Updated" + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMTableEntity_template(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_table" "test" { + name = "acctestst%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} +`, rInt, location, rString, rInt) +} diff --git a/azurerm/resource_arm_storage_table_migration.go b/azurerm/resource_arm_storage_table_migration.go index e2da0116cca7..d6320ea910df 100644 --- a/azurerm/resource_arm_storage_table_migration.go +++ b/azurerm/resource_arm_storage_table_migration.go @@ -4,37 +4,91 @@ import ( "fmt" "log" - "github.com/hashicorp/terraform/terraform" + "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/validate" ) -func resourceStorageTableMigrateState( - v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { - switch v { - case 0: - log.Println("[INFO] Found AzureRM Storage Table State v0; migrating to v1") - return migrateStorageTableStateV0toV1(is, meta) - default: - return is, fmt.Errorf("Unexpected schema version: %d", v) +// the schema schema was used for both V0 and V1 +func resourceStorageTableStateResourceV0V1() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageTableName, + }, + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, + }, + "resource_group_name": azure.SchemaResourceGroupNameDeprecated(), + "acl": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "access_policy": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "expiry": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "permissions": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + }, + }, + }, + }, } } -func migrateStorageTableStateV0toV1(is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { - if is.Empty() { - log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") - return is, nil - } +func resourceStorageTableStateUpgradeV0ToV1(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + tableName := rawState["name"].(string) + accountName := rawState["storage_account_name"].(string) + environment := meta.(*ArmClient).environment - log.Printf("[DEBUG] ARM Storage Table Attributes before Migration: %#v", is.Attributes) + id := rawState["id"].(string) + newResourceID := fmt.Sprintf("https://%s.table.%s/%s", accountName, environment.StorageEndpointSuffix, tableName) + log.Printf("[DEBUG] Updating ID from %q to %q", id, newResourceID) - environment := meta.(*ArmClient).environment + rawState["id"] = newResourceID + return rawState, nil +} - tableName := is.Attributes["name"] - storageAccountName := is.Attributes["storage_account_name"] - newID := fmt.Sprintf("https://%s.table.%s/%s", storageAccountName, environment.StorageEndpointSuffix, tableName) - is.Attributes["id"] = newID - is.ID = newID +func resourceStorageTableStateUpgradeV1ToV2(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + tableName := rawState["name"].(string) + accountName := rawState["storage_account_name"].(string) + environment := meta.(*ArmClient).environment - log.Printf("[DEBUG] ARM Storage Table Attributes after State Migration: %#v", is.Attributes) + id := rawState["id"].(string) + newResourceID := fmt.Sprintf("https://%s.table.%s/Tables('%s')", accountName, environment.StorageEndpointSuffix, tableName) + log.Printf("[DEBUG] Updating ID from %q to %q", id, newResourceID) - return is, nil + rawState["id"] = newResourceID + return rawState, nil } diff --git a/azurerm/resource_arm_storage_table_migration_test.go b/azurerm/resource_arm_storage_table_migration_test.go index 9122cb3b5643..5775cef3e7fc 100644 --- a/azurerm/resource_arm_storage_table_migration_test.go +++ b/azurerm/resource_arm_storage_table_migration_test.go @@ -2,65 +2,88 @@ package azurerm import ( "fmt" + "reflect" "testing" - "github.com/hashicorp/terraform/terraform" + "github.com/Azure/go-autorest/autorest/azure" ) -// NOTE: this is intentionally an acceptance test (and we're not explicitly setting the env) -// as we want to run this depending on the cloud we're in. -func TestAccAzureRMStorageTableMigrateState(t *testing.T) { - config := testGetAzureConfig(t) - if config == nil { - t.SkipNow() - return +func TestAzureRMStorageTableMigrateStateV0ToV1(t *testing.T) { + clouds := []azure.Environment{ + azure.ChinaCloud, + azure.GermanCloud, + azure.PublicCloud, + azure.USGovernmentCloud, } - client, err := getArmClient(config, false, "") - if err != nil { - t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) - return + for _, cloud := range clouds { + t.Logf("[DEBUG] Testing with Cloud %q", cloud.Name) + + input := map[string]interface{}{ + "id": "table1", + "name": "table1", + "storage_account_name": "account1", + } + meta := &ArmClient{ + environment: cloud, + } + suffix := meta.environment.StorageEndpointSuffix + + expected := map[string]interface{}{ + "id": fmt.Sprintf("https://account1.table.%s/table1", suffix), + "name": "table1", + "storage_account_name": "account1", + } + + actual, err := resourceStorageTableStateUpgradeV0ToV1(input, meta) + if err != nil { + t.Fatalf("Expected no error but got: %s", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected %+v. Got %+v. But expected them to be the same", expected, actual) + } + + t.Logf("[DEBUG] Ok!") } +} - client.StopContext = testAccProvider.StopContext() - - suffix := client.environment.StorageEndpointSuffix - - cases := map[string]struct { - StateVersion int - ID string - InputAttributes map[string]string - ExpectedAttributes map[string]string - }{ - "v0_1_without_value": { - StateVersion: 0, - ID: "some_id", - InputAttributes: map[string]string{ - "name": "table1", - "storage_account_name": "example", - }, - ExpectedAttributes: map[string]string{ - "id": fmt.Sprintf("https://example.table.%s/table1", suffix), - }, - }, +func TestAzureRMStorageTableMigrateStateV1ToV2(t *testing.T) { + clouds := []azure.Environment{ + azure.ChinaCloud, + azure.GermanCloud, + azure.PublicCloud, + azure.USGovernmentCloud, } - for tn, tc := range cases { - is := &terraform.InstanceState{ - ID: tc.ID, - Attributes: tc.InputAttributes, + for _, cloud := range clouds { + t.Logf("[DEBUG] Testing with Cloud %q", cloud.Name) + + meta := &ArmClient{ + environment: cloud, } - is, err := resourceStorageTableMigrateState(tc.StateVersion, is, client) + suffix := meta.environment.StorageEndpointSuffix + input := map[string]interface{}{ + "id": fmt.Sprintf("https://account1.table.%s/table1", suffix), + "name": "table1", + "storage_account_name": "account1", + } + expected := map[string]interface{}{ + "id": fmt.Sprintf("https://account1.table.%s/Tables('table1')", suffix), + "name": "table1", + "storage_account_name": "account1", + } + + actual, err := resourceStorageTableStateUpgradeV1ToV2(input, meta) if err != nil { - t.Fatalf("bad: %s, err: %#v", tn, err) + t.Fatalf("Expected no error but got: %s", err) } - for k, v := range tc.ExpectedAttributes { - actual := is.Attributes[k] - if actual != v { - t.Fatalf("Bad Storage Table Migrate for %q: %q\n\n expected: %q", k, actual, v) - } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected %+v. Got %+v. But expected them to be the same", expected, actual) } + + t.Logf("[DEBUG] Ok!") } } diff --git a/azurerm/resource_arm_storage_table_test.go b/azurerm/resource_arm_storage_table_test.go index 547242678a6b..0bb36c5a5b87 100644 --- a/azurerm/resource_arm_storage_table_test.go +++ b/azurerm/resource_arm_storage_table_test.go @@ -2,20 +2,19 @@ package azurerm import ( "fmt" - "log" "strings" "testing" - "github.com/Azure/azure-sdk-for-go/storage" "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestAccAzureRMStorageTable_basic(t *testing.T) { resourceName := "azurerm_storage_table.test" - var table storage.Table ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) @@ -29,7 +28,7 @@ func TestAccAzureRMStorageTable_basic(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageTableExists(resourceName, &table), + testCheckAzureRMStorageTableExists(resourceName), ), }, { @@ -42,13 +41,12 @@ func TestAccAzureRMStorageTable_basic(t *testing.T) { } func TestAccAzureRMStorageTable_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } resourceName := "azurerm_storage_table.test" - var table storage.Table ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) @@ -62,7 +60,7 @@ func TestAccAzureRMStorageTable_requiresImport(t *testing.T) { { Config: testAccAzureRMStorageTable_basic(ri, rs, location), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageTableExists(resourceName, &table), + testCheckAzureRMStorageTableExists(resourceName), ), }, { @@ -74,8 +72,6 @@ func TestAccAzureRMStorageTable_requiresImport(t *testing.T) { } func TestAccAzureRMStorageTable_disappears(t *testing.T) { - var table storage.Table - ri := tf.AccRandTimeInt() rs := strings.ToLower(acctest.RandString(11)) config := testAccAzureRMStorageTable_basic(ri, rs, testLocation()) @@ -88,8 +84,8 @@ func TestAccAzureRMStorageTable_disappears(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageTableExists("azurerm_storage_table.test", &table), - testAccARMStorageTableDisappears("azurerm_storage_table.test", &table), + testCheckAzureRMStorageTableExists("azurerm_storage_table.test"), + testAccARMStorageTableDisappears("azurerm_storage_table.test"), ), ExpectNonEmptyPlan: true, }, @@ -97,85 +93,122 @@ func TestAccAzureRMStorageTable_disappears(t *testing.T) { }) } -func testCheckAzureRMStorageTableExists(resourceName string, t *storage.Table) resource.TestCheckFunc { - return func(s *terraform.State) error { +func TestAccAzureRMStorageTable_acl(t *testing.T) { + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + resourceName := "azurerm_storage_table.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageTableDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageTable_acl(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageTableExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageTable_aclUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageTableExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func testCheckAzureRMStorageTableExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage table: %s", name) - } + tableName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, resourceGroup, storageAccountName) + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - return err + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", tableName, accountName, err) } - if !accountExists { - return fmt.Errorf("Bad: Storage Account %q does not exist", storageAccountName) + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Table %q (Account %s)", tableName, accountName) } - options := &storage.QueryTablesOptions{} - tables, err := tableClient.QueryTables(storage.MinimalMetadata, options) + client, err := storageClient.TablesClient(ctx, *resourceGroup, accountName) if err != nil { - return fmt.Errorf("Error querying Storage Table %q (storage account: %q) : %+v", name, storageAccountName, err) - } - if len(tables.Tables) == 0 { - return fmt.Errorf("Bad: Storage Table %q (storage account: %q) does not exist", name, storageAccountName) + return fmt.Errorf("Error building Table Client: %s", err) } - var found bool - for _, table := range tables.Tables { - if table.Name == name { - found = true - *t = table + props, err := client.Exists(ctx, accountName, tableName) + if err != nil { + if utils.ResponseWasNotFound(props) { + return fmt.Errorf("Table %q doesn't exist in Account %q!", tableName, accountName) } - } - if !found { - return fmt.Errorf("Bad: Storage Table %q (storage account: %q) does not exist", name, storageAccountName) + return fmt.Errorf("Error retrieving Table %q: %s", tableName, accountName) } return nil } } -func testAccARMStorageTableDisappears(resourceName string, t *storage.Table) resource.TestCheckFunc { +func testAccARMStorageTableDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { return fmt.Errorf("Not found: %s", resourceName) } - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext + tableName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage table: %s", t.Name) + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) + if err != nil { + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", tableName, accountName, err) + } + if resourceGroup == nil { + return fmt.Errorf("Unable to locate Resource Group for Storage Table %q (Account %s)", tableName, accountName) } - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, resourceGroup, storageAccountName) + client, err := storageClient.TablesClient(ctx, *resourceGroup, accountName) if err != nil { - return err + return fmt.Errorf("Error building Table Client: %s", err) } - if !accountExists { - log.Printf("[INFO]Storage Account %q doesn't exist so the table won't exist", storageAccountName) - return nil + + props, err := client.Exists(ctx, accountName, tableName) + if err != nil { + if utils.ResponseWasNotFound(props) { + return fmt.Errorf("Table %q doesn't exist in Account %q so it can't be deleted!", tableName, accountName) + } + + return fmt.Errorf("Error retrieving Table %q: %s", tableName, accountName) } - table := tableClient.GetTableReference(t.Name) - timeout := uint(60) - options := &storage.TableOptions{} - return table.Delete(timeout, options) + if _, err := client.Delete(ctx, accountName, tableName); err != nil { + return fmt.Errorf("Error deleting Table %q: %s", tableName, err) + } + + return nil } } @@ -185,40 +218,31 @@ func testCheckAzureRMStorageTableDestroy(s *terraform.State) error { continue } - name := rs.Primary.Attributes["name"] - storageAccountName := rs.Primary.Attributes["storage_account_name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for storage table: %s", name) - } + tableName := rs.Primary.Attributes["name"] + accountName := rs.Primary.Attributes["storage_account_name"] - armClient := testAccProvider.Meta().(*ArmClient) - ctx := armClient.StopContext - tableClient, accountExists, err := armClient.getTableServiceClientForStorageAccount(ctx, resourceGroup, storageAccountName) + storageClient := testAccProvider.Meta().(*ArmClient).Storage + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) if err != nil { - //If we can't get keys then the table can't exist - return nil + return fmt.Errorf("Error locating Resource Group for Storage Table %q (Account %s): %s", tableName, accountName, err) } - if !accountExists { + if resourceGroup == nil { return nil } - options := &storage.QueryTablesOptions{} - tables, err := tableClient.QueryTables(storage.NoMetadata, options) + client, err := storageClient.TablesClient(ctx, *resourceGroup, accountName) if err != nil { - return nil + return fmt.Errorf("Error building Table Client: %s", err) } - var found bool - for _, table := range tables.Tables { - if table.Name == name { - found = true - } + props, err := client.Exists(ctx, accountName, tableName) + if err != nil { + return nil } - if found { - return fmt.Errorf("Bad: Storage Table %q (storage account: %q) still exist", name, storageAccountName) - } + return fmt.Errorf("Table still exists: %+v", props) } return nil @@ -295,3 +319,85 @@ resource "azurerm_storage_table" "import" { } `, template) } + +func testAccAzureRMStorageTable_acl(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + 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_table" "test" { + name = "acctestst%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + acl { + id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "raud" + start = "2020-11-26T08:49:37.0000000Z" + expiry = "2020-11-27T08:49:37.0000000Z" + } + } +} +`, rInt, location, rString, rInt) +} + +func testAccAzureRMStorageTable_aclUpdated(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + 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_table" "test" { + name = "acctestst%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + + acl { + id = "AAAANDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "raud" + start = "2020-11-26T08:49:37.0000000Z" + expiry = "2020-11-27T08:49:37.0000000Z" + } + } + acl { + id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + + access_policy { + permissions = "raud" + start = "2019-07-02T09:38:21.0000000Z" + expiry = "2019-07-02T10:38:21.0000000Z" + } + } +} +`, rInt, location, rString, rInt) +} diff --git a/azurerm/resource_arm_stream_analytics_function_javascript_udf.go b/azurerm/resource_arm_stream_analytics_function_javascript_udf.go index cfb6e9d565d6..0facdbd5b8da 100644 --- a/azurerm/resource_arm_stream_analytics_function_javascript_udf.go +++ b/azurerm/resource_arm_stream_analytics_function_javascript_udf.go @@ -13,6 +13,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -100,7 +101,7 @@ func resourceArmStreamAnalyticsFunctionUDF() *schema.Resource { } func resourceArmStreamAnalyticsFunctionUDFCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).streamAnalyticsFunctionsClient + client := meta.(*ArmClient).StreamAnalytics.FunctionsClient ctx := meta.(*ArmClient).StopContext log.Printf("[INFO] preparing arguments for Azure Stream Analytics Function Javascript UDF creation.") @@ -108,7 +109,7 @@ func resourceArmStreamAnalyticsFunctionUDFCreateUpdate(d *schema.ResourceData, m jobName := d.Get("stream_analytics_job_name").(string) resourceGroup := d.Get("resource_group_name").(string) - if requireResourcesToBeImported && d.IsNewResource() { + if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, jobName, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { @@ -168,10 +169,10 @@ func resourceArmStreamAnalyticsFunctionUDFCreateUpdate(d *schema.ResourceData, m } func resourceArmStreamAnalyticsFunctionUDFRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).streamAnalyticsFunctionsClient + client := meta.(*ArmClient).StreamAnalytics.FunctionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } @@ -222,10 +223,10 @@ func resourceArmStreamAnalyticsFunctionUDFRead(d *schema.ResourceData, meta inte } func resourceArmStreamAnalyticsFunctionUDFDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).streamAnalyticsFunctionsClient + client := meta.(*ArmClient).StreamAnalytics.FunctionsClient ctx := meta.(*ArmClient).StopContext - id, err := parseAzureResourceID(d.Id()) + id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { return err } diff --git a/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go b/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go index dd44d507cc77..5e1bd2d530d8 100644 --- a/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go +++ b/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" ) func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(t *testing.T) { @@ -36,7 +37,7 @@ func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(t *testing.T) { } func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_requiresImport(t *testing.T) { - if !requireResourcesToBeImported { + if !features.ShouldResourcesBeImported() { t.Skip("Skipping since resources aren't required to be imported") return } @@ -112,7 +113,7 @@ func testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName str jobName := rs.Primary.Attributes["stream_analytics_job_name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - conn := testAccProvider.Meta().(*ArmClient).streamAnalyticsFunctionsClient + conn := testAccProvider.Meta().(*ArmClient).StreamAnalytics.FunctionsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext resp, err := conn.Get(ctx, resourceGroup, jobName, name) if err != nil { @@ -128,7 +129,7 @@ func testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName str } func testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).streamAnalyticsOutputsClient + conn := testAccProvider.Meta().(*ArmClient).StreamAnalytics.OutputsClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_stream_analytics_function_javascript_udf" { @@ -161,6 +162,7 @@ resource "azurerm_stream_analytics_function_javascript_udf" "test" { name = "acctestinput-%d" stream_analytics_job_name = "${azurerm_stream_analytics_job.test.name}" resource_group_name = "${azurerm_stream_analytics_job.test.resource_group_name}" + script = <