diff --git a/.github/labeler-pull-request-triage.yml b/.github/labeler-pull-request-triage.yml index 6fb7ff96aea3..246e625bd0c9 100644 --- a/.github/labeler-pull-request-triage.yml +++ b/.github/labeler-pull-request-triage.yml @@ -74,6 +74,9 @@ service/cost-management: service/custom-resource-provider: - internal/services/customproviders/**/* +service/dashboard: + - internal/services/dashboard/**/* + service/data-factory: - internal/services/datafactory/**/* diff --git a/.github/workflows/depscheck.yaml b/.github/workflows/depscheck.yaml index 29335ca659b5..8a4faf1ab843 100644 --- a/.github/workflows/depscheck.yaml +++ b/.github/workflows/depscheck.yaml @@ -1,5 +1,7 @@ --- name: Vendor Dependencies Check +permissions: + contents: read on: pull_request: types: ['opened', 'synchronize'] @@ -16,7 +18,6 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - - run: make tools - run: make depscheck diff --git a/.github/workflows/gencheck.yaml b/.github/workflows/gencheck.yaml index 00e1d253774a..005ecded4068 100644 --- a/.github/workflows/gencheck.yaml +++ b/.github/workflows/gencheck.yaml @@ -1,5 +1,7 @@ --- name: Generation Check +permissions: + contents: read on: pull_request: types: ['opened', 'synchronize'] @@ -20,7 +22,6 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - - run: make tools - run: make gencheck diff --git a/.github/workflows/golint.yaml b/.github/workflows/golint.yaml index 9e436960a83d..873be8a3b8a8 100644 --- a/.github/workflows/golint.yaml +++ b/.github/workflows/golint.yaml @@ -1,5 +1,7 @@ --- name: GoLang Linting +permissions: + contents: read on: pull_request: types: ['opened', 'synchronize'] @@ -20,7 +22,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - uses: golangci/golangci-lint-action@v2 with: version: 'v1.45.0' diff --git a/.github/workflows/gradually-deprecated.yaml b/.github/workflows/gradually-deprecated.yaml index 3b854851353f..caa4a0924bab 100644 --- a/.github/workflows/gradually-deprecated.yaml +++ b/.github/workflows/gradually-deprecated.yaml @@ -1,5 +1,7 @@ --- name: Check for new usages of deprecated functionality +permissions: + contents: read on: pull_request: types: ['opened', 'synchronize'] @@ -16,5 +18,5 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: ./scripts/run-gradually-deprecated.sh diff --git a/.github/workflows/issue-comment-created.yaml b/.github/workflows/issue-comment-created.yaml index c5e499b51140..9fc1a1778e70 100644 --- a/.github/workflows/issue-comment-created.yaml +++ b/.github/workflows/issue-comment-created.yaml @@ -4,6 +4,10 @@ on: issue_comment: types: [created] +permissions: + pull-requests: write + issues: write + jobs: issue_comment_triage: runs-on: ubuntu-latest @@ -13,6 +17,19 @@ jobs: github_token: "${{ secrets.GITHUB_TOKEN }}" labels: stale - uses: actions-ecosystem/action-remove-labels@v1 + if: ${{ !github.event.issue.pull_request }} with: github_token: "${{ secrets.GITHUB_TOKEN }}" labels: waiting-response + - uses: actions-ecosystem/action-remove-labels@v1 + if: (github.event.issue.pull_request && github.actor == github.event.issue.user.login) + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + labels: waiting-response + pull_request_comment: + runs-on: ubuntu-latest + if: github.event.issue.pull_request && endsWith(github.event.comment.body, '/wr') + steps: + - shell: bash + run: | + curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos${{ github.owner }}/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels" -d '{"labels":["waiting-response"]}' diff --git a/.github/workflows/issue-opened.yaml b/.github/workflows/issue-opened.yaml index c3a4c065d878..98d9143fbae7 100644 --- a/.github/workflows/issue-opened.yaml +++ b/.github/workflows/issue-opened.yaml @@ -1,5 +1,8 @@ name: Issue Opened Triage +permissions: + issues: write + on: issues: types: [opened] diff --git a/.github/workflows/link-milestone.yaml b/.github/workflows/link-milestone.yaml index b497805f8e22..a531f5136f01 100644 --- a/.github/workflows/link-milestone.yaml +++ b/.github/workflows/link-milestone.yaml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: | go install github.com/stephybun/link-milestone@latest link-milestone diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml index ed67648c7887..4e162901f70c 100644 --- a/.github/workflows/lock.yaml +++ b/.github/workflows/lock.yaml @@ -6,6 +6,9 @@ on: jobs: lock: + permissions: + issues: write + pull-requests: write runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v2 diff --git a/.github/workflows/pull-request-new-commit.yaml b/.github/workflows/pull-request-new-commit.yaml new file mode 100644 index 000000000000..7042b90f2a90 --- /dev/null +++ b/.github/workflows/pull-request-new-commit.yaml @@ -0,0 +1,18 @@ +--- +name: Pull Request New Commit + +permissions: + pull-requests: write + +on: + pull_request_target: + types: [synchronize] + +jobs: + issue_comment_triage: + runs-on: ubuntu-latest + steps: + - uses: actions-ecosystem/action-remove-labels@v1 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + labels: waiting-response diff --git a/.github/workflows/pull-request-reviewed-workflow.yaml b/.github/workflows/pull-request-reviewed-workflow.yaml new file mode 100644 index 000000000000..64f9da723f54 --- /dev/null +++ b/.github/workflows/pull-request-reviewed-workflow.yaml @@ -0,0 +1,48 @@ +--- +name: "Pull Request Reviewed Workflow" + +on: + workflow_run: + workflows: + - "Pull Request Reviewed" + types: + - completed + +permissions: + pull-requests: write + +jobs: + add-or-remove-waiting-response: + runs-on: ubuntu-latest + outputs: + ghrepo: ${{ steps.env_vars.outputs.ghrepo }} + ghowner: ${{ steps.env_vars.outputs.ghowner }} + prnumber: ${{ steps.env_vars.outputs.prnumber }} + action: ${{ steps.env_vars.outputs.action }} + steps: + - name: Get Artifact + id: get_artifact + continue-on-error: true + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: pull-request-reviewed.yaml + + - name: env_vars + id: env_vars + if: steps.get_artifact.outcome == 'success' + run: | + echo "::set-output name=ghrepo::$(cat artifact/ghrepo.txt)" + echo "::set-output name=ghowner::$(cat artifact/ghowner.txt)" + echo "::set-output name=prnumber::$(cat artifact/prnumber.txt)" + echo "::set-output name=action::$(cat artifact/action.txt)" + + - name: add waiting-reponse + if: steps.get_artifact.outcome == 'success' && steps.env_vars.outputs.action == 'add-waiting-response' + run: | + curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos${{ steps.env_vars.outputs.ghowner }}/${{ steps.env_vars.outputs.ghrepo }}/issues/${{ steps.env_vars.outputs.prnumber }}/labels" -d '{"labels":["waiting-response"]}' + + - name: remove waiting-reponse + if: steps.get_artifact.outcome == 'success' && steps.env_vars.outputs.action == 'remove-waiting-response' + run: | + curl -X DELETE -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos${{ steps.env_vars.outputs.ghowner }}/${{ steps.env_vars.outputs.ghrepo }}/issues/${{ steps.env_vars.outputs.prnumber }}/labels/waiting-response" diff --git a/.github/workflows/pull-request-reviewed.yaml b/.github/workflows/pull-request-reviewed.yaml new file mode 100644 index 000000000000..691170a65157 --- /dev/null +++ b/.github/workflows/pull-request-reviewed.yaml @@ -0,0 +1,36 @@ +--- +name: "Pull Request Reviewed" + +on: + pull_request_review: + types: [submitted] + +permissions: + pull-requests: read + +jobs: + add-or-remove-waiting-response: + runs-on: ubuntu-latest + steps: + - name: "Set Artifacts for add-waiting-response" + if: github.event.review.state != 'approved' && github.actor != github.event.pull_request.user.login + shell: bash + run: | + mkdir -p wr_actions + echo ${{ github.owner }} > wr_actions/ghowner.txt + echo ${{ github.repository }} > wr_actions/ghrepo.txt + echo ${{ github.event.pull_request.number }} > wr_actions/prnumber.txt + echo "add-waiting-response" > wr_actions/action.txt + - name: "Set Artifacts for remove-waiting-response" + if: github.actor == github.event.pull_request.user.login + shell: bash + run: | + mkdir -p wr_actions + echo ${{ github.owner }} > wr_actions/ghowner.txt + echo ${{ github.repository }} > wr_actions/ghrepo.txt + echo ${{ github.event.pull_request.number }} > wr_actions/prnumber.txt + echo "remove-waiting-response" > wr_actions/action.txt + - uses: actions/upload-artifact@v3 + with: + name: artifact + path: wr_actions diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 9c58e3d541c8..bc8541c25aae 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -1,5 +1,8 @@ name: "Pull Request Triage" +permissions: + pull-requests: write + on: [pull_request_target] jobs: diff --git a/.github/workflows/teamcity-test.yaml b/.github/workflows/teamcity-test.yaml index 6724f390dc50..1e691f7525b9 100644 --- a/.github/workflows/teamcity-test.yaml +++ b/.github/workflows/teamcity-test.yaml @@ -1,5 +1,9 @@ --- name: TeamCity Config Test + +permissions: + contents: read + on: pull_request: types: ['opened', 'synchronize'] diff --git a/.github/workflows/tflint.yaml b/.github/workflows/tflint.yaml index c8e4400abaa6..a958f2babfa8 100644 --- a/.github/workflows/tflint.yaml +++ b/.github/workflows/tflint.yaml @@ -1,5 +1,9 @@ --- name: Terraform Schema Linting + +permissions: + contents: read + on: pull_request: types: ['opened', 'synchronize'] @@ -20,7 +24,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - run: make tools - run: make tflint diff --git a/.github/workflows/thirty-two-bit.yaml b/.github/workflows/thirty-two-bit.yaml index 658d2f5c5ab0..e0ddd0fdc45a 100644 --- a/.github/workflows/thirty-two-bit.yaml +++ b/.github/workflows/thirty-two-bit.yaml @@ -1,5 +1,9 @@ --- name: 32 Bit Build + +permissions: + pull-requests: read + on: pull_request: types: ['opened', 'synchronize'] @@ -20,7 +24,6 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - - run: make tools - run: GOARCH=386 GOOS=linux go build -o 32bitbuild . diff --git a/.github/workflows/unit-test.yaml b/.github/workflows/unit-test.yaml index cb1820ea7ef1..c295f5f1bef1 100644 --- a/.github/workflows/unit-test.yaml +++ b/.github/workflows/unit-test.yaml @@ -1,5 +1,9 @@ --- name: Unit Tests + +permissions: + pull-requests: read + on: pull_request: types: ['opened', 'synchronize'] @@ -19,7 +23,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - run: make test env: diff --git a/.github/workflows/validate-examples.yaml b/.github/workflows/validate-examples.yaml index 7ef44548a1dc..28a586d6a6a9 100644 --- a/.github/workflows/validate-examples.yaml +++ b/.github/workflows/validate-examples.yaml @@ -1,5 +1,9 @@ --- name: Validate Examples + +permissions: + pull-requests: read + on: pull_request: types: ['opened', 'synchronize'] @@ -18,7 +22,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - run: make tools - run: make validate-examples diff --git a/.github/workflows/website-lint.yaml b/.github/workflows/website-lint.yaml index 57fd18b081d5..48339db39c0f 100644 --- a/.github/workflows/website-lint.yaml +++ b/.github/workflows/website-lint.yaml @@ -1,5 +1,9 @@ --- name: Website Linting + +permissions: + pull-requests: read + on: pull_request: types: ['opened', 'synchronize'] @@ -14,7 +18,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.18.5' - run: bash scripts/gogetcookie.sh - run: make tools - run: make website-lint diff --git a/.go-version b/.go-version index 5ce8b3959987..8e8b0a9335a8 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.18.1 \ No newline at end of file +1.18.5 diff --git a/.teamcity/components/generated/services.kt b/.teamcity/components/generated/services.kt index dc82027890cb..5bfbe77a004e 100644 --- a/.teamcity/components/generated/services.kt +++ b/.teamcity/components/generated/services.kt @@ -28,12 +28,14 @@ var services = mapOf( "costmanagement" to "Cost Management", "customproviders" to "Custom Providers", "dns" to "DNS", + "dashboard" to "Dashboard", "datafactory" to "Data Factory", "datashare" to "Data Share", "databricks" to "DataBricks", "dataprotection" to "DataProtection", "databasemigration" to "Database Migration", "databoxedge" to "Databox Edge", + "datadog" to "Datadog", "desktopvirtualization" to "Desktop Virtualization", "devtestlabs" to "Dev Test", "digitaltwins" to "Digital Twins", @@ -93,6 +95,7 @@ var services = mapOf( "servicefabric" to "Service Fabric", "servicefabricmanaged" to "Service Fabric Managed Clusters", "servicebus" to "ServiceBus", + "serviceconnector" to "ServiceConnector", "signalr" to "SignalR", "springcloud" to "Spring Cloud", "storage" to "Storage", diff --git a/CHANGELOG-v2.md b/CHANGELOG-v2.md index dedcf63ee7d0..0c4f2b693619 100644 --- a/CHANGELOG-v2.md +++ b/CHANGELOG-v2.md @@ -4136,7 +4136,7 @@ NOTES: FEATURES: -* **Custom Timeouts:** - all resources within the Azure Provider now allow configuring custom timeouts - please [see Terraform's Timeout documentation](https://www.terraform.io/docs/configuration/resources.html#operation-timeouts) and the documentation in each data source resource for more information. +* **Custom Timeouts:** - all resources within the Azure Provider now allow configuring custom timeouts - please [see Terraform's Timeout documentation](https://www.terraform.io/language/resources/syntax#operation-timeouts) and the documentation in each data source resource for more information. * **Requires Import:** The Azure Provider now checks for the presence of an existing resource prior to creating it - which means that if you try and create a resource which already exists (without importing it) you'll be prompted to import this into the state. * **New Data Source:** `azurerm_app_service_environment` ([#5508](https://github.com/hashicorp/terraform-provider-azurerm/issues/5508)) * **New Data Source:** `azurerm_eventhub_authorization_rule` ([#5805](https://github.com/hashicorp/terraform-provider-azurerm/issues/5805)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28134090362e..dadef38c51a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,279 @@ -## 3.14.0 (Unreleased) +## 3.21.0 (Unreleased) + +FEATURES: + +* **New Data Source**: `azurerm_monitor_data_collection_endpoint` [GH-17992] +* **New Resource**: `azurerm_app_service_connection` [GH-16907] +* **New Resource**: `azurerm_api_management_gateway_certificate_authority` [GH-17879] +* **New Resource**: `azurerm_api_management_product_tag` [GH-17798] +* **New Resource**: `azurerm_automation_connection_type` [GH-17538] +* **New Resource**: `azurerm_automation_hybrid_runbook_worker_group` [GH-17881] +* **New Resource**: `azurerm_container_registry_task_schedule_run_now` [GH-15120] +* **New Resource**: `azurerm_cosmosdb_sql_dedicated_gateway` [GH-18133] +* **New Resource**: `azurerm_dashboard_grafana` [GH-17840] +* **New Resource**: `azurerm_log_analytics_query_pack_query` [GH-17929] +* **New Resource**: `azurerm_healthcare_medtech_service` [GH-15967] +* **New Resource**: `azurerm_spring_cloud_connection` [GH-16907] +* **New Resource**: `azurerm_search_shared_private_link_service` [GH-17744] +* **New Resource**: `azurerm_sentinel_alert_rule_nrt` [GH-15999] + +ENHANCEMENTS: + +* dependencies: updating to version `v0.20220830.1105041` of `github.com/hashicorp/go-azure-sdk` [GH-18183] +* `azurerm_container_registry` - support for the `azuread_authentication_as_arm_policy_enabled` and `soft_delete_policy` properties [GH-17926] +* `azurerm_hdinsight_kafka_cluster` - add support for the `disk_encryption` property [GH-17351] +* `azurerm_hdinsight_spark_cluster` - add support for the `disk_encryption` property [GH-17351] +* `azurerm_hdinsight_interactive_query_cluster` - add support for the `disk_encryption` property [GH-17351] +* `azurerm_hdinsight_hbase_cluster` - add support for the `disk_encryption` property [GH-17351] +* `azurerm_hdinsight_hadoop_cluster` - add support for the `disk_encryption` property [GH-17351] +* `azurerm_iothub_dps` - support for the `resource_count`, `parallel_deployments`, and `failure_percentage` properties [GH-18151] +* `azurerm_kubernetes_node_pool` - spot node pools can now be upgraded [GH-18124] +* `azurerm_management_group_policy_remediation` - support for the `resource_count`, `parallel_deployments`, and `failure_percentage` properties [GH-17313] +* `azurerm_monitor_diagnostic_setting` - support for the `category_group` property [GH-16367] +* `azurerm_resource_group_policy_remediation` - support for the `resource_count`, `parallel_deployments`, and `failure_percentage` properties [GH-17313] +* `azurerm_resource_policy_remediation` - support for the `resource_count`, `parallel_deployments`, and `failure_percentage` properties [GH-17313] +* `azurerm_servicebus_namespace` - support for the `public_network_access_enabled` and `minimum_tls_version` properties [GH-17805] +* `azurerm_storage_account` - support for the `public_network_access_enabled` property [GH-18005] +* `azurerm_stream_analytics_output_eventhub` - support for the `authentication_mode` property [GH-18096] +* `azurerm_stream_analytics_output_mssql` - support for the `authentication_mode` property [GH-18096] +* `azurerm_stream_analytics_output_servicebus_topic` - support for the `authentication_mode` property [GH-18096] +* `azurerm_stream_analytics_output_powerbi` - support for the `token_user_principal_name` and `token_user_display_name` properties [GH-18117] +* `azurerm_stream_analytics_output_cosmosdb` - support for the `partition_key` property [GH-18120] +* `azurerm_stream_analytics_reference_input_blob` - support for the `authentication_mode` property [GH-18137] +* `azurerm_subscription_policy_remediation` - support for the `resource_count`, `parallel_deployments`, and `failure_percentage` properties [GH-17313] +* Dependencies: `log_analytics` - update to use hashicorp/go-azure-sdk [GH-18098] + +BUG FIXES: + +* `azurerm_kubernetes_cluster` - `kube_config` is now set when AAD is enabled for a v1.24 cluster [GH-18142] +* `azurerm_redis_cache` - will now recreate the cache when downgrading the SKU [GH-17767] +* `azurerm_spring_cloud_service` - ignore default zero value for `read_timeout_seconds` [GH-18161] + +## 3.20.0 (August 25, 2022) + +FEATURES: + +* **Provider:** support for generic OIDC authentication providers ([#18118](https://github.com/hashicorp/terraform-provider-azurerm/issues/18118)) +* **New Resource**: `azurerm_backup_policy_vm_workload` ([#17765](https://github.com/hashicorp/terraform-provider-azurerm/issues/17765)) +* **New Resource**: `azurerm_monitor_scheduled_query_rules_alert_v2` ([#17772](https://github.com/hashicorp/terraform-provider-azurerm/issues/17772)) + +ENHANCEMENTS: + +* Dependencies: update `go-azure-sdk` to `v0.20220824.1090858` ([#18100](https://github.com/hashicorp/terraform-provider-azurerm/issues/18100)) +* Dependencies: `consumption` - updating to use `hashicorp/go-azure-sdk` ([#18101](https://github.com/hashicorp/terraform-provider-azurerm/issues/18101)) +* `azurerm_data_factory_dataset_json` - `filename` and `path` in `azure_blob_storage_location` block can now be empty ([#18061](https://github.com/hashicorp/terraform-provider-azurerm/issues/18061)) + +BUG FIXES: + +* `data.azurerm_kubernetes_cluster` - `kube_config` is now set when AAD is enabled for a v1.24 cluster ([#18131](https://github.com/hashicorp/terraform-provider-azurerm/issues/18131)) +* `azurerm_cosmosdb_sql_database` - prevent panic in autoacale settings ([#18070](https://github.com/hashicorp/terraform-provider-azurerm/issues/18070)) +* `azurerm_kubernetes_cluster_node_pool` - fix a crash in expanding upgrade settings ([#18074](https://github.com/hashicorp/terraform-provider-azurerm/issues/18074)) +* `azurerm_mssql_elastic_pool` - list of values for `maintenance_configuration_name` is now correct ([#18041](https://github.com/hashicorp/terraform-provider-azurerm/issues/18041)) +* `azurerm_postgresql_flexible_server` - `point_in_time_restore_time_in_utc` correctly converts to RFC3339 ([#18106](https://github.com/hashicorp/terraform-provider-azurerm/issues/18106)) + +## 3.19.1 (August 19, 2022) + +BUG FIXES: + +* `azurerm_dns_a_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_aaaa_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_caa_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_cname_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_mx_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_ns_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_ptr_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_srv_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_txt_record` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) +* `azurerm_dns_zone` - parse resource IDs insensitively in the read functions due to casing on the dnsZones segment ([#18048](https://github.com/hashicorp/terraform-provider-azurerm/issues/18048)) + +## 3.19.0 (August 18, 2022) + +FEATURES: + +* **New Data Source**: `azurerm_dns_a_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_aaaa_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_caa_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_cname_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_mx_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_ns_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_ptr_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_soa_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_srv_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_dns_txt_record` ([#17477](https://github.com/hashicorp/terraform-provider-azurerm/issues/17477)) +* **New Data Source**: `azurerm_private_dns_a_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_aaaa_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_cname_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_mx_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_ptr_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_soa_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_srv_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Data Source**: `azurerm_private_dns_txt_record` ([#18036](https://github.com/hashicorp/terraform-provider-azurerm/issues/18036)) +* **New Resource**: `azurerm_eventhub_namespace_schema_group` ([#17635](https://github.com/hashicorp/terraform-provider-azurerm/issues/17635)) +* **New Resource**: `azurerm_cdn_frontdoor_firewall_policy` ([#17715](https://github.com/hashicorp/terraform-provider-azurerm/issues/17715)) +* **New Resource**: `azurerm_cdn_frontdoor_security_policy` ([#17715](https://github.com/hashicorp/terraform-provider-azurerm/issues/17715)) +* **New Resource**: `azurerm_data_factory_flowlet_data_flow` ([#16987](https://github.com/hashicorp/terraform-provider-azurerm/issues/16987)) + +ENHANCEMENTS: + +* Dependencies: update `go-azure-helpers` to `v0.39.1` ([#18015](https://github.com/hashicorp/terraform-provider-azurerm/issues/18015)) +* Dependencies: update `go-azure-sdk` to `v0.20220815.1092453` ([#17998](https://github.com/hashicorp/terraform-provider-azurerm/issues/17998)) +* Dependencies: `dedicated_host_*` to use `hashicorp/go-azure-sdk` ([#17616](https://github.com/hashicorp/terraform-provider-azurerm/issues/17616)) +* Dependencies: `dataprotection`: updating to use `hashicorp/go-azure-sdk` ([#17700](https://github.com/hashicorp/terraform-provider-azurerm/issues/17700)) +* Dependencies: `dns` - updating to use `hashicorp/go-azure-sdk` ([#17986](https://github.com/hashicorp/terraform-provider-azurerm/issues/17986)) +* Dependencies: `maintenance` - updating to use `hashicorp/go-azure-sdk` ([#17954](https://github.com/hashicorp/terraform-provider-azurerm/issues/17954)) +* Data Source: `azurerm_images` - now uses a logical id ([#17766](https://github.com/hashicorp/terraform-provider-azurerm/issues/17766)) +* Data Source: `azurerm_management_group` - now exports the `management_group_ids`, `all_management_group_ids`, and `all_subscription_ids` attributes ([#16208](https://github.com/hashicorp/terraform-provider-azurerm/issues/16208)) +* `azurerm_active_directory_domain_service` - support for the `kerberos_armoring_enabled` and `kerberos_rc4_encryption_enabled` properties ([#17853](https://github.com/hashicorp/terraform-provider-azurerm/issues/17853)) +* `azurerm_application_gateway` - support for the `global` block ([#17651](https://github.com/hashicorp/terraform-provider-azurerm/issues/17651)) +* `azurerm_application_gateway` - support for `components` in `rewrite_rule_set.rewrite_rule.url` ([#13899](https://github.com/hashicorp/terraform-provider-azurerm/issues/13899)) +* `azurerm_automation_account` - support for the `private_endpoint_connection` property ([#17934](https://github.com/hashicorp/terraform-provider-azurerm/issues/17934)) +* `azurerm_automation_account` - support for the `encryption` block and `local_authentication_enabled` property ([#17454](https://github.com/hashicorp/terraform-provider-azurerm/issues/17454)) +* `azurerm_batch_account` - support for the `storage_account_authentication_mode`, `storage_account_node_identit`, and `allowed_authentication_modes` properties ([#16758](https://github.com/hashicorp/terraform-provider-azurerm/issues/16758)) +* `azurerm_batch_pool` - support for identity referencees in container registries ([#17416](https://github.com/hashicorp/terraform-provider-azurerm/issues/17416)) +* `azurerm_data_factory_data_flow` - support for the `flowlet` block ([#16987](https://github.com/hashicorp/terraform-provider-azurerm/issues/16987)) +* `azurerm_data_factory_integration_runtime_azure_ssis` - support for the `express_vnet_injection` property ([#17756](https://github.com/hashicorp/terraform-provider-azurerm/issues/17756)) +* `azurerm_firewall_policy_resource` - support for the `private_ranges` and `allow_sql_redirect` properties ([#17842](https://github.com/hashicorp/terraform-provider-azurerm/issues/17842)) +* `azurerm_key_vault` - support for the `public_network_access_enabled` property ([#17552](https://github.com/hashicorp/terraform-provider-azurerm/issues/17552)) +* `azurerm_linux_virtual_machine` - now supports delete Eviction policies ([#17226](https://github.com/hashicorp/terraform-provider-azurerm/issues/17226)) +* `azurerm_linux_virtual_machine_scale_set` - now supports delete Eviction policies ([#17226](https://github.com/hashicorp/terraform-provider-azurerm/issues/17226)) +* `azurerm_mssql_elastic_pool` - support for the `maintenance_configuration_name` property ([#17790](https://github.com/hashicorp/terraform-provider-azurerm/issues/17790)) +* `azurerm_mssql_server` - support `Disabled` for the `minimum_tls_version` property ([#16595](https://github.com/hashicorp/terraform-provider-azurerm/issues/16595)) +* `azurerm_spring_cloud_app` - support the `public_endpoint_enabled` property ([#17630](https://github.com/hashicorp/terraform-provider-azurerm/issues/17630)) +* `azurerm_spring_cloud_gateway_route_config` - support for the `open_api;azurerm_spring_cloud_service` and `log_stream_public_endpoint_enabledread_timeout_seconds` properties ([#17630](https://github.com/hashicorp/terraform-provider-azurerm/issues/17630)) +* `azurerm_shared_image` - support for the `architecture` property ([#17250](https://github.com/hashicorp/terraform-provider-azurerm/issues/17250)) +* `azurerm_storage_account` - support for the `default_to_oauth_authentication` property ([#17116](https://github.com/hashicorp/terraform-provider-azurerm/issues/17116)) +* `azurerm_storage_table_entity` - support for specifying data types on entity properties ([#15782](https://github.com/hashicorp/terraform-provider-azurerm/issues/15782)) +* `azurerm_shared_image_version` - support for `blob_uri` and `storage_account_id` ([#17768](https://github.com/hashicorp/terraform-provider-azurerm/issues/17768)) +* `azurerm_windows_virtual_machine` - now supports delete Eviction policies ([#17226](https://github.com/hashicorp/terraform-provider-azurerm/issues/17226)) +* `azurerm_windows_virtual_machine_scale_set` - now supports delete Eviction policies ([#17226](https://github.com/hashicorp/terraform-provider-azurerm/issues/17226)) +* `azurerm_web_application_firewall_policy` - support for the `excluded_rule_set` property ([#17757](https://github.com/hashicorp/terraform-provider-azurerm/issues/17757)) +* `azurerm_log_analytics_workspace` - support for the `cmk_for_query_forced` property ([#17365](https://github.com/hashicorp/terraform-provider-azurerm/issues/17365)) +* `azurerm_lb_backend_address_pool_address` - support for the `backend_address_ip_configuration_id` property ([#17770](https://github.com/hashicorp/terraform-provider-azurerm/issues/17770)) + +BUG FIXES: + +* Data Source: `azurerm_windows_web_app` - add missing schema definition for 'virtual_network_subnet_id' ([#18028](https://github.com/hashicorp/terraform-provider-azurerm/issues/18028)) +* `azurerm_cdn_endpoint_custom_domain` - deprecating the `key_vault_certificate_id` property in favour of the `key_vault_secret_id` property withing the `user_managed https_allows` block ([#17114](https://github.com/hashicorp/terraform-provider-azurerm/issues/17114)) +* `azurerm_data_protection_backup_policy_postgresql_resource` - prevent a crash when given an empty criteria block ([#17904](https://github.com/hashicorp/terraform-provider-azurerm/issues/17904)) +* `azurerm_disk_encryption_set` - prevent an issue during creation when the disk encryption set and key vault are in different subscriptions ([#17964](https://github.com/hashicorp/terraform-provider-azurerm/issues/17964)) +* `azurerm_windows_function_app` fix a bug with setting values for `WindowsFxString` ([#18014](https://github.com/hashicorp/terraform-provider-azurerm/issues/18014)) +* `azurerm_windows_function_app_slot` - fix a bug with setting values for `WindowsFxString` ([#18014](https://github.com/hashicorp/terraform-provider-azurerm/issues/18014)) +* `azurerm_linux_function_app` - correctly send `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_linux_function_app` - fix content settings when `storage_uses_managed_identity` is set to `true` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_linux_function_app_slot` - correctly send `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_linux_function_app_slot` - fix content settings when `storage_uses_managed_identity` is set to `true` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_windows_function_app` - correctly send `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_windows_function_app` - fix content settings when `storage_uses_managed_identity` is set to `true` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_windows_function_app_slot` - correctly send `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) +* `azurerm_windows_function_app_slot` - fix content settings when `storage_uses_managed_identity` is set to `true` ([#18035](https://github.com/hashicorp/terraform-provider-azurerm/issues/18035)) + +## 3.18.0 (August 11, 2022) + +FEATURES: + +* **New Resource**: `azurerm_monitor_data_collection_endpoint` ([#17684](https://github.com/hashicorp/terraform-provider-azurerm/issues/17684)) + +ENHANCEMENTS: + +* dependencies: updating `github.com/hashicorp/go-azure-sdk` to `v0.20220809.1122626` ([#17905](https://github.com/hashicorp/terraform-provider-azurerm/issues/17905)) +* storage: updating to use API Version `2021-09-01` ([#17523](https://github.com/hashicorp/terraform-provider-azurerm/issues/17523)) +* `azurerm_express_route_circuit_peering` - support for the `ipv4_enabled` and `gateway_manager_etag` properties ([#17338](https://github.com/hashicorp/terraform-provider-azurerm/issues/17338)) +* `azurerm_site_recovery_replicated_vm` - support for the `target_disk_encryption` block ([#15783](https://github.com/hashicorp/terraform-provider-azurerm/issues/15783)) +* `azurerm_subnet` - deprecate `enforce_private_link_endpoint_network_policies` property in favour of `private_endpoint_network_policies_enabled` ([#17464](https://github.com/hashicorp/terraform-provider-azurerm/issues/17464)) +* `azurerm_subnet` - deprecate `enforce_private_link_service_network_policies` property in favour of `private_link_service_network_policies_enabled` ([#17464](https://github.com/hashicorp/terraform-provider-azurerm/issues/17464)) +* `azurerm_servicebus_subscription` - support for the `client_scoped_subscription_enabled` property and the `client_scoped_subscription` block ([#17101](https://github.com/hashicorp/terraform-provider-azurerm/issues/17101)) + +BUG FIXES: + +* `azurerm_backup_policy_vm` - now prevents crash when `frequency` is set to Hourly and, `hour_interval` and `hour_duration`are not set ([#17880](https://github.com/hashicorp/terraform-provider-azurerm/issues/17880)) +* Data Source: `azurerm_blueprint_definition` - Fix `version` property output ([#16299](https://github.com/hashicorp/terraform-provider-azurerm/issues/16299)) + +## 3.17.0 (August 04, 2022) + +ENHANCEMENTS: + +* domainservice: updating to use API Version `2021-05-01` ([#17737](https://github.com/hashicorp/terraform-provider-azurerm/issues/17737)) +* Data Source: `azurerm_proximity_placement_group` - refactoring to use `hashicorp/go-azure-sdk` ([#17776](https://github.com/hashicorp/terraform-provider-azurerm/issues/17776)) +* `azurerm_api_management` - update the `sku_name` property validation to accept newer Premium SKUs ([#17887](https://github.com/hashicorp/terraform-provider-azurerm/issues/17887)) +* `azurerm_firewall` - the property `sku_tier` is now updateable ([#17577](https://github.com/hashicorp/terraform-provider-azurerm/issues/17577)) +* `azurerm_linux_virtual_machine_scale_set` - the property `instances` is now Optional and defaults to `0` ([#17836](https://github.com/hashicorp/terraform-provider-azurerm/issues/17836)) +* `azurerm_log_analytics_cluster` - updated validation for the `size_gb` property ([#17780](https://github.com/hashicorp/terraform-provider-azurerm/issues/17780)) +* `azurerm_proximity_placement_group` - refactoring to use `hashicorp/go-azure-sdk` ([#17776](https://github.com/hashicorp/terraform-provider-azurerm/issues/17776)) +* `azurerm_shared_image` - improved validation for the `publisher`, `offer` and `sku` properties in the `identifier` block ([#17547](https://github.com/hashicorp/terraform-provider-azurerm/issues/17547)) +* `azurerm_subnet` - support for the service delegation `Microsoft.Orbital/orbitalGateway` ([#17854](https://github.com/hashicorp/terraform-provider-azurerm/issues/17854)) +* `azurerm_eventhub_namespace` - support for the `local_authentication_enabled`, `public_network_access_enabled`, and `minimum_tls_version` properties ([#17194](https://github.com/hashicorp/terraform-provider-azurerm/issues/17194)) + +BUG FIXES: + +* Data Source: `azurerm_private_dns_zone` - returning the correct Resource ID when not specifying the `resource_group_name` ([#17729](https://github.com/hashicorp/terraform-provider-azurerm/issues/17729)) + +## 3.16.0 (July 28, 2022) + +FEATURES: + +* **New Resource**: `azurerm_datadog_monitor` ([#16131](https://github.com/hashicorp/terraform-provider-azurerm/issues/16131)) +* **New Resource**: `azurerm_kusto_cluster_managed_private_endpoint` ([#17667](https://github.com/hashicorp/terraform-provider-azurerm/issues/17667)) +* **New Resource**: `azurerm_log_analytics_query_pack` ([#17685](https://github.com/hashicorp/terraform-provider-azurerm/issues/17685)) +* **New Resource**: `azurerm_logz_sub_account_tag_rule` ([#17557](https://github.com/hashicorp/terraform-provider-azurerm/issues/17557)) +* **New Resource**: `azurerm_signalr_shared_private_link_resource` ([#16187](https://github.com/hashicorp/terraform-provider-azurerm/issues/16187)) + +ENHANCEMENTS: + +* dependencies: updating to version `v0.20220725.1163004` of `github.com/hashicorp/go-azure-sdk` ([#17753](https://github.com/hashicorp/terraform-provider-azurerm/issues/17753)) +* automationaccount: updating to use `hashicorp/go-azure-sdk` ([#17347](https://github.com/hashicorp/terraform-provider-azurerm/issues/17347)) +* Data Source: `azurerm_linux_function_app` - support the `virtual_network_subnet_id` property for for vNet integration ([#17494](https://github.com/hashicorp/terraform-provider-azurerm/issues/17494)) +* Data Source: `azurerm_windows_function_app` - support the `virtual_network_subnet_id` property for for vNet integration ([#17572](https://github.com/hashicorp/terraform-provider-azurerm/issues/17572)) +* Data Source: `azurerm_windows_web_app` - support the `virtual_network_subnet_id` property for for vNet integration ([#17576](https://github.com/hashicorp/terraform-provider-azurerm/issues/17576)) +* `eventhub`: updating all data sources/resources onto single API Version `2021-11-01` ([#17719](https://github.com/hashicorp/terraform-provider-azurerm/issues/17719)) +* `azurerm_bot_service_azure_bot` - support for the `streaming_endpoint_enabled` property ([#17423](https://github.com/hashicorp/terraform-provider-azurerm/issues/17423)) +* `azurerm_cognitive_account` - support for the `custom_question_answering_search_service_key` property ([#17683](https://github.com/hashicorp/terraform-provider-azurerm/issues/17683)) +* `asurerm_iothub_dps_certificate` - support for the `is_verified` property ([#17106](https://github.com/hashicorp/terraform-provider-azurerm/issues/17106)) +* `azurerm_linux_web_app`  - the `virtual_network_subnet_id` property is no longer `ForceNew` ([#17584](https://github.com/hashicorp/terraform-provider-azurerm/issues/17584)) +* `azurerm_linux_web_app_slot` - the `virtual_network_subnet_id` property is no longer `ForceNew` ([#17584](https://github.com/hashicorp/terraform-provider-azurerm/issues/17584)) +* `azurerm_linux_function_app` support the `virtual_network_subnet_id` property for for vNet integration ([#17494](https://github.com/hashicorp/terraform-provider-azurerm/issues/17494)) +* `azurerm_linux_function_app_slot` support the `virtual_network_subnet_id` property for for vNet integration ([#17494](https://github.com/hashicorp/terraform-provider-azurerm/issues/17494)) +* `azurerm_stream_analytics_stream_input_eventhub` - support for the `authentication_mode` property ([#17739](https://github.com/hashicorp/terraform-provider-azurerm/issues/17739)) +* `azurerm_windows_function_app` support the `virtual_network_subnet_id` property for for vNet integration ([#17572](https://github.com/hashicorp/terraform-provider-azurerm/issues/17572)) +* `azurerm_windows_function_app_slot` support the `virtual_network_subnet_id` property for for vNet integration ([#17572](https://github.com/hashicorp/terraform-provider-azurerm/issues/17572)) +* `azurerm_windows_web_app` support the `virtual_network_subnet_id` property for for vNet integration ([#17576](https://github.com/hashicorp/terraform-provider-azurerm/issues/17576)) +* `azurerm_windows_web_app_slot` support the `virtual_network_subnet_id` property for for vNet integration ([#17576](https://github.com/hashicorp/terraform-provider-azurerm/issues/17576)) + +BUG FIXES: + +* `azurerm_linux_function_app` - fix casing bug with the `linux_fx_string` property for Node apps ([#17789](https://github.com/hashicorp/terraform-provider-azurerm/issues/17789)) +* `azurerm_linux_function_app_slot` - fix casing bug with the `linux_fx_string` property for Node apps ([#17789](https://github.com/hashicorp/terraform-provider-azurerm/issues/17789)) +* `azurerm_resource_group_template_deployment` - fixing a bug where the same Resource Provider defined in different casings would cause the API Version to not be identified ([#17707](https://github.com/hashicorp/terraform-provider-azurerm/issues/17707)) + +## 3.15.1 (July 25, 2022) + +BUG FIXES: + +* `data.azurerm_servicebus_queue` - fix a regression around `namespace_id` ([#17755](https://github.com/hashicorp/terraform-provider-azurerm/issues/17755)) +* `azurerm_postgresql_aad_administrator` - fix the state migration ([#17732](https://github.com/hashicorp/terraform-provider-azurerm/issues/17732)) +* `azurerm_postgresql_server` - fix a regression around `id` ([#17755](https://github.com/hashicorp/terraform-provider-azurerm/issues/17755)) + +## 3.15.0 (July 21, 2022) + +FEATURES: + +* **New Data Source**: `azurerm_cdn_frontdoor_origin_group` ([#17089](https://github.com/hashicorp/terraform-provider-azurerm/issues/17089)) +* **New Data Source**: `azurerm_cdn_frontdoor_origin` ([#17089](https://github.com/hashicorp/terraform-provider-azurerm/issues/17089)) +* **New Resource**: `azurerm_cdn_frontdoor_origin_group` ([#17089](https://github.com/hashicorp/terraform-provider-azurerm/issues/17089)) +* **New Resource**: `azurerm_cdn_frontdoor_origin` ([#17089](https://github.com/hashicorp/terraform-provider-azurerm/issues/17089)) +* **New Resource**: `azurerm_application_insights_workbook` ([#17368](https://github.com/hashicorp/terraform-provider-azurerm/issues/17368)) +* **New Resource**: `azurerm_monitor_data_collection_rule` ([#17342](https://github.com/hashicorp/terraform-provider-azurerm/issues/17342)) +* **New Resource**: `azurerm_route_server` ([#16578](https://github.com/hashicorp/terraform-provider-azurerm/issues/16578)) +* **New Resource**: `azurerm_route_server_bgp_connection` ([#16578](https://github.com/hashicorp/terraform-provider-azurerm/issues/16578)) +* **New Resource**: `azurerm_web_pubsub_private_link_resource` ([#15550](https://github.com/hashicorp/terraform-provider-azurerm/issues/15550)) ENHANCEMENTS: -* dependencies: updating to `v0.20220715.1071215` of `github.com/hashicorp/go-azure-sdk` [GH-17645] -* servicebus: refactoring to use `hashicorp/go-azure-sdk` [GH-17628] +* dependencies: updating to `v0.20220715.1071215` of `github.com/hashicorp/go-azure-sdk` ([#17645](https://github.com/hashicorp/terraform-provider-azurerm/issues/17645)) +* domainservice: to use `hashicorp/go-azure-sdk` ([#17595](https://github.com/hashicorp/terraform-provider-azurerm/issues/17595)) +* servicebus: refactoring to use `hashicorp/go-azure-sdk` ([#17628](https://github.com/hashicorp/terraform-provider-azurerm/issues/17628)) +* postgres: refactoring to use `hashicorp/go-azure-sdk` ([#17625](https://github.com/hashicorp/terraform-provider-azurerm/issues/17625)) +* `azurerm_kusto_cluster_resource` - support for the `allowed_fqdns`, `allowed_ip_ranges`, and `outbound_network_access_restricted` properties ([#17581](https://github.com/hashicorp/terraform-provider-azurerm/issues/17581)) +* `azurerm_storage_account` - supports for the `change_feed_retention_in_days` property ([#17130](https://github.com/hashicorp/terraform-provider-azurerm/issues/17130)) ## 3.14.0 (July 14, 2022) diff --git a/README.md b/README.md index 764ae6ad4c3e..d3a28291429f 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,4 @@ resource "azurerm_virtual_network" "example" { ## Developing & Contributing to the Provider -The [DEVELOPER.md](DEVELOPER.md) file is a basic outline on how to build and develop the provider while more detailed guides geared towards contributors can be found in the [`/contributing`] (https://github.com/hashicorp/terraform-provider-azurerm/tree/main/contributing) directory of this repository. +The [DEVELOPER.md](DEVELOPER.md) file is a basic outline on how to build and develop the provider while more detailed guides geared towards contributors can be found in the [`/contributing`](https://github.com/hashicorp/terraform-provider-azurerm/tree/main/contributing) directory of this repository. diff --git a/contributing/topics/guide-new-data-source.md b/contributing/topics/guide-new-data-source.md index a74260961805..b6385c92359b 100644 --- a/contributing/topics/guide-new-data-source.md +++ b/contributing/topics/guide-new-data-source.md @@ -588,7 +588,7 @@ In addition to the Arguments listed above - the following Attributes are exporte ## Timeouts -The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: * `read` - (Defaults to 5 minutes) Used when retrieving the Resource Group. ``` diff --git a/contributing/topics/guide-new-resource.md b/contributing/topics/guide-new-resource.md index 4e9aaa68531a..a453b82182b6 100644 --- a/contributing/topics/guide-new-resource.md +++ b/contributing/topics/guide-new-resource.md @@ -833,7 +833,7 @@ In addition to the Arguments listed above - the following Attributes are exporte ## Timeouts -The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: * `create` - (Defaults to 30 minutes) Used when creating the Resource Group. * `read` - (Defaults to 5 minutes) Used when retrieving the Resource Group. diff --git a/contributing/topics/guide-opening-a-pr.md b/contributing/topics/guide-opening-a-pr.md index a09351ac76e6..bb17d9c196d1 100644 --- a/contributing/topics/guide-opening-a-pr.md +++ b/contributing/topics/guide-opening-a-pr.md @@ -2,7 +2,7 @@ Firstly all contributions are welcome! -There is no change to small for us to accept and minor formatting, consistency and documentation PRs are very welcome! However, before making any large or structural changes it is recommended to seek feedback (preferably by reaching out in our community slack) to prevent wasted time and effort. We may already be working on a solution, or have a different direction we would like to take. +There is no change too small for us to accept and minor formatting, consistency and documentation PRs are very welcome! However, before making any large or structural changes it is recommended to seek feedback (preferably by reaching out in our community slack) to prevent wasted time and effort. We may already be working on a solution, or have a different direction we would like to take. If you are ever unsure please just reach out, we are more than happy to guide you in the right direction! @@ -54,6 +54,7 @@ Examples of good PR titles: - `azurerm_storage_management_policy - Mark rule.filters.blob_type as required` - `azurerm_container_registry - support updating replications on demand` +- `azurerm_automation_account - support for the encrytion, local_authentication_enabled, and tags properties` - `Data Source: azurerm_automation_account - prevent panic (#15474) by adding a nil check` - `Upgrade bot API version from 2021-03-01 to 2021-05-01-preview` - `New Resource: azurerm_managed_disk_sas_token` @@ -68,6 +69,7 @@ Examples of poorly written PR titles: - `upgrade sdk` - `upgrade compute api` - `add cosmos property` +- `support encryption, local_authentication_enabled properties` ### Description @@ -77,4 +79,4 @@ It should also link to any related issues/PRs and include the following for any ``` fixes #1234,#5678 - ``` \ No newline at end of file + ``` diff --git a/examples/app-service/docker-compose/README.md b/examples/app-service/docker-compose/README.md index 8b6110ed6320..fec82c6ee2dd 100644 --- a/examples/app-service/docker-compose/README.md +++ b/examples/app-service/docker-compose/README.md @@ -5,7 +5,7 @@ This example provisions a Linux App Service which runs multiple Docker Container ### Notes * The Container is launched on the first HTTP Request, which can take a while. -* If you're not using App Service Slots and Deployments are handled outside of Terraform - [it's possible to ignore changes to specific fields in the configuration using `ignore_changes` within Terraform's `lifecycle` block](https://www.terraform.io/docs/configuration/resources.html#lifecycle), for example: +* If you're not using App Service Slots and Deployments are handled outside of Terraform - [it's possible to ignore changes to specific fields in the configuration using `ignore_changes` within Terraform's `lifecycle` block](https://www.terraform.io/language/meta-arguments/lifecycle#ignore_changes), for example: ```hcl resource "azurerm_app_service" "test" { diff --git a/examples/app-service/docker-kubernetes/README.md b/examples/app-service/docker-kubernetes/README.md index 412036f32e3f..340f7ef5905f 100644 --- a/examples/app-service/docker-kubernetes/README.md +++ b/examples/app-service/docker-kubernetes/README.md @@ -5,7 +5,7 @@ This example provisions a Linux App Service which runs multiple Docker Container ### Notes * The Container is launched on the first HTTP Request, which can take a while. -* If you're not using App Service Slots and Deployments are handled outside of Terraform - [it's possible to ignore changes to specific fields in the configuration using `ignore_changes` within Terraform's `lifecycle` block](https://www.terraform.io/docs/configuration/resources.html#lifecycle), for example: +* If you're not using App Service Slots and Deployments are handled outside of Terraform - [it's possible to ignore changes to specific fields in the configuration using `ignore_changes` within Terraform's `lifecycle` block](https://www.terraform.io/language/meta-arguments/lifecycle#ignore_changes), for example: ```hcl resource "azurerm_app_service" "test" { diff --git a/examples/private-endpoint/private-link-scope/README.md b/examples/private-endpoint/private-link-scope/README.md new file mode 100644 index 000000000000..e5411e01a7b3 --- /dev/null +++ b/examples/private-endpoint/private-link-scope/README.md @@ -0,0 +1,9 @@ +## Example: Private Endpoint + +This example provisions a Private Endpoint which connects to a Private Link Scope within Azure. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. + +* `location` - (Required) The Azure Region in which all resources in this example should be created. diff --git a/examples/private-endpoint/private-link-scope/main.tf b/examples/private-endpoint/private-link-scope/main.tf new file mode 100644 index 000000000000..a3a3198658f2 --- /dev/null +++ b/examples/private-endpoint/private-link-scope/main.tf @@ -0,0 +1,64 @@ +provider "azurerm" { + features {} +} + +locals { + private_dns_zones_names = toset([ + "privatelink.agentsvc.azure-automation.net", + "privatelink.blob.core.windows.net", + "privatelink.monitor.azure.com", + "privatelink.ods.opinsights.azure.com", + "privatelink.oms.opinsights.azure.com", + ]) +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location +} + +resource "azurerm_virtual_network" "example" { + name = "${var.prefix}-vnet" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_subnet" "example" { + name = "${var.prefix}-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.0.1.0/24"] + enforce_private_link_service_network_policies = true +} + +resource "azurerm_private_dns_zone" "example" { + for_each = local.private_dns_zones_names + + name = each.value + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_monitor_private_link_scope" "example" { + name = "${var.prefix}-ampls" + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_private_endpoint" "this" { + name = "${var.prefix}-ape" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + subnet_id = azurerm_subnet.example.id + + private_dns_zone_group { + name = "default" + private_dns_zone_ids = [for _, v in azurerm_private_dns_zone.example : v.id] + } + + private_service_connection { + name = "${var.prefix}-psc" + is_manual_connection = false + private_connection_resource_id = azurerm_monitor_private_link_scope.example.id + subresource_names = ["azuremonitor"] + } +} diff --git a/examples/private-endpoint/private-link-scope/variables.tf b/examples/private-endpoint/private-link-scope/variables.tf new file mode 100644 index 000000000000..0e6145faa285 --- /dev/null +++ b/examples/private-endpoint/private-link-scope/variables.tf @@ -0,0 +1,7 @@ +variable "prefix" { + description = "The Prefix used for all resources in this example" +} + +variable "location" { + description = "The Azure Region in which all resources in this example should be created." +} diff --git a/examples/virtual-machines/linux/load-balanced/main.tf b/examples/virtual-machines/linux/load-balanced/main.tf index b1e7bdbbe0d1..6b678c5e71cf 100644 --- a/examples/virtual-machines/linux/load-balanced/main.tf +++ b/examples/virtual-machines/linux/load-balanced/main.tf @@ -72,6 +72,11 @@ resource "azurerm_network_security_group" "webserver" { } } +resource "azurerm_subnet_network_security_group_association" "example" { + subnet_id = azurerm_subnet.internal.id + network_security_group_id = azurerm_network_security_group.webserver.id +} + resource "azurerm_lb" "example" { name = "${var.prefix}-lb" location = azurerm_resource_group.main.location diff --git a/go.mod b/go.mod index 070258b4990f..72ea61c6509a 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,8 @@ require ( github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.1.2 - github.com/hashicorp/go-azure-helpers v0.37.0 - github.com/hashicorp/go-azure-sdk v0.20220715.1071215 + github.com/hashicorp/go-azure-helpers v0.40.0 + github.com/hashicorp/go-azure-sdk v0.20220830.1105041 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 @@ -23,7 +23,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9 github.com/sergi/go-diff v1.2.0 - github.com/shopspring/decimal v1.2.0 github.com/tombuildsstuff/giovanni v0.20.0 golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 diff --git a/go.sum b/go.sum index c7d21e59ece3..c1989567e546 100644 --- a/go.sum +++ b/go.sum @@ -214,10 +214,10 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-azure-helpers v0.12.0/go.mod h1:Zc3v4DNeX6PDdy7NljlYpnrdac1++qNW0I4U+ofGwpg= -github.com/hashicorp/go-azure-helpers v0.37.0 h1:6UOoQ9esE4MJ4wHJr21qU81IJQ9zsXQ9FbANYUbeE4U= -github.com/hashicorp/go-azure-helpers v0.37.0/go.mod h1:gcutZ/Hf/O7YN9M3UIvyZ9l0Rxv7Yrc9x5sSfM9cuSw= -github.com/hashicorp/go-azure-sdk v0.20220715.1071215 h1:PZYeATYa/qh1KM5RxKC8eTSKms5/sqbZohSCcemzrJo= -github.com/hashicorp/go-azure-sdk v0.20220715.1071215/go.mod h1:yjQPw8DCOtQR8E8+FNaTxF6yz+tyQGkDNiVAGCNoLOo= +github.com/hashicorp/go-azure-helpers v0.40.0 h1:NjiyF+jN+0mRdFBU894yzZSxu1SNrbvj8l4rEDpCB0A= +github.com/hashicorp/go-azure-helpers v0.40.0/go.mod h1:gcutZ/Hf/O7YN9M3UIvyZ9l0Rxv7Yrc9x5sSfM9cuSw= +github.com/hashicorp/go-azure-sdk v0.20220830.1105041 h1:LNW0aW2CMNmfTyN4u+h6yxXrAsRcQdNSWSL/NVs6ZVY= +github.com/hashicorp/go-azure-sdk v0.20220830.1105041/go.mod h1:jOhjVttoXh2We/glz4BC/0t0Lo8+M9WQBA4sbAPQPMY= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -347,8 +347,6 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/helpers/validate/float.go b/helpers/validate/float.go index 199505be6f42..7d712ac334a4 100644 --- a/helpers/validate/float.go +++ b/helpers/validate/float.go @@ -25,3 +25,32 @@ func FloatInSlice(valid []float64) func(interface{}, string) ([]string, []error) return warnings, errors } } + +func FloatInRange(start, end float64) func(interface{}, string) ([]string, []error) { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(float64) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be float", i)) + return warnings, errors + } + + if v < start || v > end { + errors = append(errors, fmt.Errorf("expected %s to be in range [%f, %f], got %f", k, start, end, v)) + } + + return warnings, errors + } +} + +func IntegerPositive(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be int", i)) + return + } + if v <= 0 { + errors = append(errors, fmt.Errorf("expected %s to be positive, got %d", k, v)) + return + } + return +} diff --git a/internal/clients/client.go b/internal/clients/client.go index a1db6778fa33..966279125650 100644 --- a/internal/clients/client.go +++ b/internal/clients/client.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/validation" + dns_v2018_05_01 "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01" "github.com/hashicorp/terraform-provider-azurerm/internal/common" "github.com/hashicorp/terraform-provider-azurerm/internal/features" aadb2c "github.com/hashicorp/terraform-provider-azurerm/internal/services/aadb2c/client" @@ -32,9 +33,11 @@ import ( cosmosdb "github.com/hashicorp/terraform-provider-azurerm/internal/services/cosmos/client" costmanagement "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/client" customproviders "github.com/hashicorp/terraform-provider-azurerm/internal/services/customproviders/client" + dashboard "github.com/hashicorp/terraform-provider-azurerm/internal/services/dashboard/client" datamigration "github.com/hashicorp/terraform-provider-azurerm/internal/services/databasemigration/client" databoxedge "github.com/hashicorp/terraform-provider-azurerm/internal/services/databoxedge/client" databricks "github.com/hashicorp/terraform-provider-azurerm/internal/services/databricks/client" + datadog "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog/client" datafactory "github.com/hashicorp/terraform-provider-azurerm/internal/services/datafactory/client" dataprotection "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/client" datashare "github.com/hashicorp/terraform-provider-azurerm/internal/services/datashare/client" @@ -96,6 +99,7 @@ import ( securityCenter "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter/client" sentinel "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/client" serviceBus "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicebus/client" + serviceConnector "github.com/hashicorp/terraform-provider-azurerm/internal/services/serviceconnector/client" serviceFabric "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicefabric/client" serviceFabricManaged "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicefabricmanaged/client" signalr "github.com/hashicorp/terraform-provider-azurerm/internal/services/signalr/client" @@ -144,9 +148,11 @@ type Client struct { Cosmos *cosmosdb.Client CostManagement *costmanagement.Client CustomProviders *customproviders.Client + Dashboard *dashboard.Client DatabaseMigration *datamigration.Client DataBricks *databricks.Client DataboxEdge *databoxedge.Client + Datadog *datadog.Client DataFactory *datafactory.Client DataProtection *dataprotection.Client DataShare *datashare.Client @@ -154,7 +160,7 @@ type Client struct { DevTestLabs *devtestlabs.Client DigitalTwins *digitaltwins.Client Disks *disks.Client - Dns *dns.Client + Dns *dns_v2018_05_01.Client DomainServices *domainservices.Client Elastic *elastic.Client EventGrid *eventgrid.Client @@ -208,6 +214,7 @@ type Client struct { SecurityCenter *securityCenter.Client Sentinel *sentinel.Client ServiceBus *serviceBus.Client + ServiceConnector *serviceConnector.Client ServiceFabric *serviceFabric.Client ServiceFabricManaged *serviceFabricManaged.Client SignalR *signalr.Client @@ -258,9 +265,11 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error client.Cosmos = cosmosdb.NewClient(o) client.CostManagement = costmanagement.NewClient(o) client.CustomProviders = customproviders.NewClient(o) + client.Dashboard = dashboard.NewClient(o) client.DatabaseMigration = datamigration.NewClient(o) client.DataBricks = databricks.NewClient(o) client.DataboxEdge = databoxedge.NewClient(o) + client.Datadog = datadog.NewClient(o) client.DataFactory = datafactory.NewClient(o) client.DataProtection = dataprotection.NewClient(o) client.DataShare = datashare.NewClient(o) @@ -322,6 +331,7 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error client.SecurityCenter = securityCenter.NewClient(o) client.Sentinel = sentinel.NewClient(o) client.ServiceBus = serviceBus.NewClient(o) + client.ServiceConnector = serviceConnector.NewClient(o) client.ServiceFabric = serviceFabric.NewClient(o) client.ServiceFabricManaged = serviceFabricManaged.NewClient(o) client.SignalR = signalr.NewClient(o) diff --git a/internal/common/client_options.go b/internal/common/client_options.go index b8330a62f929..7fb708fdc141 100644 --- a/internal/common/client_options.go +++ b/internal/common/client_options.go @@ -82,6 +82,7 @@ func setUserAgent(client *autorest.Client, tfVersion, partnerID string, disableT } if partnerID != "" { - client.UserAgent = fmt.Sprintf("%s pid-%s", client.UserAgent, partnerID) + // Tolerate partnerID UUIDs without the "pid-" prefix + client.UserAgent = fmt.Sprintf("%s pid-%s", client.UserAgent, strings.TrimPrefix(partnerID, "pid-")) } } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1e6faa4798b6..ed2904c5a569 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -25,6 +25,67 @@ func TestAzureProvider() *schema.Provider { return azureProvider(true) } +func ValidatePartnerID(i interface{}, k string) ([]string, []error) { + // ValidatePartnerID checks if partner_id is any of the following: + // * a valid UUID - will add "pid-" prefix to the ID if it is not already present + // * a valid UUID prefixed with "pid-" + // * a valid UUID prefixed with "pid-" and suffixed with "-partnercenter" + + debugLog := func(f string, v ...interface{}) { + if os.Getenv("TF_LOG") == "" { + return + } + + if os.Getenv("TF_ACC") != "" { + return + } + + log.Printf(f, v...) + } + + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if v == "" { + return nil, nil + } + + // Check for pid=-partnercenter format + if strings.HasPrefix(v, "pid-") && strings.HasSuffix(v, "-partnercenter") { + g := strings.TrimPrefix(v, "pid-") + g = strings.TrimSuffix(g, "-partnercenter") + + if _, err := validation.IsUUID(g, ""); err != nil { + return nil, []error{fmt.Errorf("expected %q to contain a valid UUID", v)} + } + + debugLog("[DEBUG] %q partner_id matches pid--partnercenter...", v) + return nil, nil + } + + // Check for pid= (without the -partnercenter suffix) + if strings.HasPrefix(v, "pid-") && !strings.HasSuffix(v, "-partnercenter") { + g := strings.TrimPrefix(v, "pid-") + + if _, err := validation.IsUUID(g, ""); err != nil { + return nil, []error{fmt.Errorf("expected %q to be a valid UUID", k)} + } + + debugLog("[DEBUG] %q partner_id matches pid-...", v) + return nil, nil + } + + // Check for straight UUID + if _, err := validation.IsUUID(v, ""); err != nil { + return nil, []error{fmt.Errorf("expected %q to be a valid UUID", k)} + } else { + debugLog("[DEBUG] %q partner_id is an un-prefixed UUID...", v) + return nil, nil + } +} + func azureProvider(supportLegacyTestSuite bool) *schema.Provider { // avoids this showing up in test output debugLog := func(f string, v ...interface{}) { @@ -171,13 +232,20 @@ func azureProvider(supportLegacyTestSuite bool) *schema.Provider { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"}, ""), - Description: "The bearer token for the request to the OIDC provider. For use When authenticating as a Service Principal using OpenID Connect.", + Description: "The bearer token for the request to the OIDC provider. For use when authenticating as a Service Principal using OpenID Connect.", }, "oidc_request_url": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_URL"}, ""), - Description: "The URL for the OIDC provider from which to request an ID token. For use When authenticating as a Service Principal using OpenID Connect.", + Description: "The URL for the OIDC provider from which to request an ID token. For use when authenticating as a Service Principal using OpenID Connect.", + }, + + "oidc_token": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("ARM_OIDC_TOKEN", ""), + Description: "The OIDC ID token for use when authenticating as a Service Principal using OpenID Connect.", }, "use_oidc": { @@ -205,7 +273,7 @@ func azureProvider(supportLegacyTestSuite bool) *schema.Provider { "partner_id": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.Any(validation.IsUUID, validation.StringIsEmpty), + ValidateFunc: validation.Any(ValidatePartnerID, validation.StringIsEmpty), DefaultFunc: schema.EnvDefaultFunc("ARM_PARTNER_ID", ""), Description: "A GUID/UUID that is registered with Microsoft to facilitate partner resource usage attribution.", }, @@ -279,6 +347,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { ClientCertPath: d.Get("client_certificate_path").(string), IDTokenRequestToken: d.Get("oidc_request_token").(string), IDTokenRequestURL: d.Get("oidc_request_url").(string), + IDToken: d.Get("oidc_token").(string), // Feature Toggles SupportsClientCertAuth: true, diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 611f97cdf7e7..17323b9ce387 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -76,7 +76,15 @@ func TestResourcesSupportCustomTimeouts(t *testing.T) { if resource.Timeouts.Read == nil { t.Fatalf("Resource %q doesn't define a Read timeout", resourceName) } else if *resource.Timeouts.Read > 5*time.Minute { - t.Fatalf("Read timeouts shouldn't be more than 5 minutes, this indicates a bug which needs to be fixed") + exceptionResources := map[string]bool{ + // The key vault item resources have longer read timeout for mitigating issue: https://github.com/hashicorp/terraform-provider-azurerm/issues/11059. + "azurerm_key_vault_key": true, + "azurerm_key_vault_secret": true, + "azurerm_key_vault_certificate": true, + } + if !exceptionResources[resourceName] { + t.Fatalf("Read timeouts shouldn't be more than 5 minutes, this indicates a bug which needs to be fixed") + } } // Optional diff --git a/internal/provider/services.go b/internal/provider/services.go index a0a52d2c40d3..a7e8080fbdf8 100644 --- a/internal/provider/services.go +++ b/internal/provider/services.go @@ -28,9 +28,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/cosmos" "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement" "github.com/hashicorp/terraform-provider-azurerm/internal/services/customproviders" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dashboard" "github.com/hashicorp/terraform-provider-azurerm/internal/services/databasemigration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/databoxedge" "github.com/hashicorp/terraform-provider-azurerm/internal/services/databricks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog" "github.com/hashicorp/terraform-provider-azurerm/internal/services/datafactory" "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection" "github.com/hashicorp/terraform-provider-azurerm/internal/services/datashare" @@ -92,6 +94,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/securitycenter" "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel" "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicebus" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/serviceconnector" "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicefabric" "github.com/hashicorp/terraform-provider-azurerm/internal/services/servicefabricmanaged" "github.com/hashicorp/terraform-provider-azurerm/internal/services/signalr" @@ -116,12 +119,15 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration { appconfiguration.Registration{}, applicationinsights.Registration{}, appservice.Registration{}, + automation.Registration{}, batch.Registration{}, bot.Registration{}, compute.Registration{}, consumption.Registration{}, containers.Registration{}, + cosmos.Registration{}, costmanagement.Registration{}, + dashboard.Registration{}, disks.Registration{}, domainservices.Registration{}, eventhub.Registration{}, @@ -129,12 +135,17 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration { keyvault.Registration{}, loadbalancer.Registration{}, loadtest.Registration{}, + loganalytics.Registration{}, + monitor.Registration{}, mssql.Registration{}, policy.Registration{}, + recoveryservices.Registration{}, resource.Registration{}, sentinel.Registration{}, + serviceconnector.Registration{}, servicefabricmanaged.Registration{}, streamanalytics.Registration{}, + search.Registration{}, web.Registration{}, } } @@ -166,7 +177,9 @@ func SupportedUntypedServices() []sdk.UntypedServiceRegistration { consumption.Registration{}, cosmos.Registration{}, customproviders.Registration{}, + dashboard.Registration{}, databricks.Registration{}, + datadog.Registration{}, datafactory.Registration{}, databasemigration.Registration{}, databoxedge.Registration{}, diff --git a/internal/services/analysisservices/analysis_services_server_resource.go b/internal/services/analysisservices/analysis_services_server_resource.go index cffade59cf1b..ce6723425ff4 100644 --- a/internal/services/analysisservices/analysis_services_server_resource.go +++ b/internal/services/analysisservices/analysis_services_server_resource.go @@ -107,7 +107,7 @@ func resourceAnalysisServicesServer() *pluginsdk.Resource { }, }, }, - Set: hashAnalysisServicesServerIpv4FirewallRule, + Set: hashAnalysisServicesServerIPv4FirewallRule, }, "querypool_connection_mode": { @@ -323,7 +323,7 @@ func expandAnalysisServicesServerProperties(d *pluginsdk.ResourceData) *servers. AsAdministrators: adminUsers, } - serverProperties.IpV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) + serverProperties.IPV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) if querypoolConnectionMode, ok := d.GetOk("querypool_connection_mode"); ok { connectionMode := servers.ConnectionMode(querypoolConnectionMode.(string)) @@ -344,7 +344,7 @@ func expandAnalysisServicesServerMutableProperties(d *pluginsdk.ResourceData) *s AsAdministrators: adminUsers, } - serverProperties.IpV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) + serverProperties.IPV4FirewallSettings = expandAnalysisServicesServerFirewallSettings(d) connectionMode := servers.ConnectionMode(d.Get("querypool_connection_mode").(string)) serverProperties.QuerypoolConnectionMode = &connectionMode @@ -393,11 +393,11 @@ func expandAnalysisServicesServerFirewallSettings(d *pluginsdk.ResourceData) *se } func flattenAnalysisServicesServerFirewallSettings(serverProperties *servers.AnalysisServicesServerProperties) (*bool, *pluginsdk.Set) { - if serverProperties == nil || serverProperties.IpV4FirewallSettings == nil { - return utils.Bool(false), pluginsdk.NewSet(hashAnalysisServicesServerIpv4FirewallRule, make([]interface{}, 0)) + if serverProperties == nil || serverProperties.IPV4FirewallSettings == nil { + return utils.Bool(false), pluginsdk.NewSet(hashAnalysisServicesServerIPv4FirewallRule, make([]interface{}, 0)) } - firewallSettings := serverProperties.IpV4FirewallSettings + firewallSettings := serverProperties.IPV4FirewallSettings enablePowerBi := utils.Bool(false) if firewallSettings.EnablePowerBIService != nil { @@ -424,10 +424,10 @@ func flattenAnalysisServicesServerFirewallSettings(serverProperties *servers.Ana } } - return enablePowerBi, pluginsdk.NewSet(hashAnalysisServicesServerIpv4FirewallRule, fwRules) + return enablePowerBi, pluginsdk.NewSet(hashAnalysisServicesServerIPv4FirewallRule, fwRules) } -func hashAnalysisServicesServerIpv4FirewallRule(v interface{}) int { +func hashAnalysisServicesServerIPv4FirewallRule(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) diff --git a/internal/services/apimanagement/api_management_gateway_certificate_authority_resource.go b/internal/services/apimanagement/api_management_gateway_certificate_authority_resource.go new file mode 100644 index 000000000000..4fa00c2e78af --- /dev/null +++ b/internal/services/apimanagement/api_management_gateway_certificate_authority_resource.go @@ -0,0 +1,151 @@ +package apimanagement + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2021-08-01/apimanagement" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/schemaz" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceApiManagementGatewayCertificateAuthority() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceApiManagementGatewayCertificateAuthorityCreateUpdate, + Read: resourceApiManagementGatewayCertificateAuthorityRead, + Update: resourceApiManagementGatewayCertificateAuthorityCreateUpdate, + Delete: resourceApiManagementGatewayCertificateAuthorityDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.GatewayCertificateAuthorityID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "api_management_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ApiManagementID, + }, + + "certificate_name": schemaz.SchemaApiManagementChildName(), + + "gateway_name": schemaz.SchemaApiManagementChildName(), + + "is_trusted": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + }, + } +} + +func resourceApiManagementGatewayCertificateAuthorityCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.GatewayCertificateAuthorityClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + apimId, err := parse.ApiManagementID(d.Get("api_management_id").(string)) + if err != nil { + return fmt.Errorf("parsing `api_management_id`: %v", err) + } + + id := parse.NewGatewayCertificateAuthorityID(apimId.SubscriptionId, apimId.ResourceGroup, apimId.ServiceName, d.Get("gateway_name").(string), d.Get("certificate_name").(string)) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.ServiceName, id.GatewayName, d.Get("certificate_name").(string)) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %s", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_api_management_gateway_certificate_authority", id.ID()) + } + } + + parameters := apimanagement.GatewayCertificateAuthorityContract{ + GatewayCertificateAuthorityContractProperties: &apimanagement.GatewayCertificateAuthorityContractProperties{ + IsTrusted: utils.Bool(d.Get("is_trusted").(bool)), + }, + } + + resp, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServiceName, d.Get("gateway_name").(string), d.Get("certificate_name").(string), parameters, "") + if err != nil { + return fmt.Errorf("creating or updating %s: %+v", id, err) + } + + d.SetId(*resp.ID) + + return resourceApiManagementGatewayCertificateAuthorityRead(d, meta) +} + +func resourceApiManagementGatewayCertificateAuthorityRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.GatewayCertificateAuthorityClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.GatewayCertificateAuthorityID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.ServiceName, id.GatewayName, id.CertificateAuthorityName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("%s was not found - removing from state!", *id) + d.SetId("") + return nil + } + + return fmt.Errorf("making read request for %s: %+v", *id, err) + } + + apimId := parse.NewApiManagementID(id.SubscriptionId, id.ResourceGroup, id.ServiceName) + + d.Set("certificate_name", resp.Name) + d.Set("api_management_id", apimId.ID()) + d.Set("gateway_name", id.GatewayName) + + if properties := resp.GatewayCertificateAuthorityContractProperties; properties != nil { + d.Set("is_trusted", properties.IsTrusted) + } + + return nil +} + +func resourceApiManagementGatewayCertificateAuthorityDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.GatewayCertificateAuthorityClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.GatewayCertificateAuthorityID(d.Id()) + if err != nil { + return err + } + + log.Printf("[DEBUG] Deleting %s", *id) + if resp, err := client.Delete(ctx, id.ResourceGroup, id.ServiceName, id.GatewayName, id.CertificateAuthorityName, ""); err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + } + + return nil +} diff --git a/internal/services/apimanagement/api_management_gateway_certificate_authority_resource_test.go b/internal/services/apimanagement/api_management_gateway_certificate_authority_resource_test.go new file mode 100644 index 000000000000..5b162c86518d --- /dev/null +++ b/internal/services/apimanagement/api_management_gateway_certificate_authority_resource_test.go @@ -0,0 +1,254 @@ +package apimanagement_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ApiManagementGatewayCertificateAuthorityResource struct{} + +func TestAccApiManagementGatewayCertificateAuthority_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_gateway_certificate_authority", "test") + r := ApiManagementGatewayCertificateAuthorityResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApiManagementGatewayCertificateAuthority_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_gateway_certificate_authority", "test") + r := ApiManagementGatewayCertificateAuthorityResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccApiManagementGatewayCertificateAuthority_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_gateway_certificate_authority", "test") + r := ApiManagementGatewayCertificateAuthorityResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApiManagementGatewayCertificateAuthority_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_gateway_certificate_authority", "test") + r := ApiManagementGatewayCertificateAuthorityResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + }) +} + +func (ApiManagementGatewayCertificateAuthorityResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.GatewayCertificateAuthorityID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.ApiManagement.GatewayCertificateAuthorityClient.Get(ctx, id.ResourceGroup, id.ServiceName, id.GatewayName, id.CertificateAuthorityName) + if err != nil { + return nil, fmt.Errorf("reading Api Management Gateway Certificate Authority (%s): %+v", id, err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (ApiManagementGatewayCertificateAuthorityResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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" + + sku_name = "Developer_1" +} + +resource "azurerm_api_management_gateway" "test" { + name = "acctestAMGateway-%d" + api_management_id = azurerm_api_management.test.id + + location_data { + name = "test" + } +} + +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_gateway_certificate_authority" "test" { + api_management_id = azurerm_api_management.test.id + certificate_name = azurerm_api_management_certificate.test.name + gateway_name = azurerm_api_management_gateway.test.name +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r ApiManagementGatewayCertificateAuthorityResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_gateway_certificate_authority" "import" { + api_management_id = azurerm_api_management_gateway_certificate_authority.test.api_management_id + certificate_name = azurerm_api_management_gateway_certificate_authority.test.certificate_name + gateway_name = azurerm_api_management_gateway_certificate_authority.test.gateway_name +} +`, r.basic(data)) +} + +func (ApiManagementGatewayCertificateAuthorityResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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" + + sku_name = "Developer_1" +} + +resource "azurerm_api_management_gateway" "test" { + name = "acctestAMGateway-%d" + api_management_id = azurerm_api_management.test.id + + location_data { + name = "test" + } +} + +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_gateway_certificate_authority" "test" { + api_management_id = azurerm_api_management.test.id + certificate_name = azurerm_api_management_certificate.test.name + gateway_name = azurerm_api_management_gateway.test.name + is_trusted = true +} + +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (ApiManagementGatewayCertificateAuthorityResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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" + + sku_name = "Developer_1" +} + +resource "azurerm_api_management_gateway" "test" { + name = "acctestAMGateway-%d" + api_management_id = azurerm_api_management.test.id + + location_data { + name = "test" + } +} + +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_gateway_certificate_authority" "test" { + api_management_id = azurerm_api_management.test.id + certificate_name = azurerm_api_management_certificate.test.name + gateway_name = azurerm_api_management_gateway.test.name + is_trusted = false +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/apimanagement/api_management_product_tag_resource.go b/internal/services/apimanagement/api_management_product_tag_resource.go new file mode 100644 index 000000000000..5533c1e1922a --- /dev/null +++ b/internal/services/apimanagement/api_management_product_tag_resource.go @@ -0,0 +1,130 @@ +package apimanagement + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/schemaz" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceApiManagementProductTag() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceApiManagementProductTagCreate, + Read: resourceApiManagementProductTagRead, + Delete: resourceApiManagementProductTagDelete, + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.ProductTagID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "api_management_product_id": schemaz.SchemaApiManagementChildName(), + + "api_management_name": schemaz.SchemaApiManagementName(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "name": schemaz.SchemaApiManagementChildName(), + }, + } +} + +func resourceApiManagementProductTagCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.TagClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id := parse.NewProductTagID(subscriptionId, d.Get("resource_group_name").(string), d.Get("api_management_name").(string), d.Get("api_management_product_id").(string), d.Get("name").(string)) + + if d.IsNewResource() { + existing, err := client.GetByProduct(ctx, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %s", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_api_management_product_tag", id.ID()) + } + } + + resp, err := client.AssignToProduct(ctx, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) + if err != nil { + return fmt.Errorf(" creating product tag (id : %s): %+v", id, err) + } + d.SetId(*resp.ID) + + return resourceApiManagementProductTagRead(d, meta) +} + +func resourceApiManagementProductTagRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.TagClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ProductTagID(d.Id()) + if err != nil { + return err + } + + resp, err := client.GetByProduct(ctx, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("%s was not found - removing from state!", *id) + d.SetId("") + return nil + } + + return fmt.Errorf("making Read request on %s: %+v", *id, err) + } + + productTagId, err := parse.ProductTagID(*resp.ID) + if err != nil { + return err + } + + d.Set("api_management_product_id", productTagId.ProductName) + d.Set("api_management_name", productTagId.ServiceName) + d.Set("resource_group_name", productTagId.ResourceGroup) + d.Set("name", productTagId.TagName) + + return nil +} + +func resourceApiManagementProductTagDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ApiManagement.TagClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ProductTagID(d.Id()) + if err != nil { + return err + } + + log.Printf("[DEBUG] Deleting %s", *id) + resp, err := client.DetachFromProduct(ctx, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + } + + return nil +} diff --git a/internal/services/apimanagement/api_management_product_tag_resource_test.go b/internal/services/apimanagement/api_management_product_tag_resource_test.go new file mode 100644 index 000000000000..080b8c08de4e --- /dev/null +++ b/internal/services/apimanagement/api_management_product_tag_resource_test.go @@ -0,0 +1,121 @@ +package apimanagement_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ApiManagementProductTagResource struct{} + +func TestAccApiManagementProductTag_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_product_tag", "test") + r := ApiManagementProductTagResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("api_management_product_id").Exists(), + check.That(data.ResourceName).Key("api_management_name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("name").Exists(), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApiManagementProductTag_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_product_tag", "test") + r := ApiManagementProductTagResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (ApiManagementProductTagResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.ProductTagID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.ApiManagement.TagClient.GetByProduct(ctx, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) + if err != nil { + return nil, fmt.Errorf("reading %s: %+v", *id, err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (ApiManagementProductTagResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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" + + sku_name = "Consumption_0" +} + +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 = false + published = false +} + +resource "azurerm_api_management_tag" "test" { + api_management_id = azurerm_api_management.test.id + name = "acctestTag-%d" +} + +resource "azurerm_api_management_product_tag" "test" { + api_management_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 + name = azurerm_api_management_tag.test.name +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r ApiManagementProductTagResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_product_tag" "import" { + api_management_product_id = azurerm_api_management_product_tag.test.api_management_product_id + api_management_name = azurerm_api_management_product_tag.test.api_management_name + resource_group_name = azurerm_api_management_product_tag.test.resource_group_name + name = azurerm_api_management_product_tag.test.name +} +`, r.basic(data)) +} diff --git a/internal/services/apimanagement/client/client.go b/internal/services/apimanagement/client/client.go index 12c09025c667..8fb138b155cb 100644 --- a/internal/services/apimanagement/client/client.go +++ b/internal/services/apimanagement/client/client.go @@ -6,43 +6,44 @@ import ( ) type Client struct { - ApiClient *apimanagement.APIClient - ApiDiagnosticClient *apimanagement.APIDiagnosticClient - ApiPoliciesClient *apimanagement.APIPolicyClient - ApiOperationsClient *apimanagement.APIOperationClient - ApiOperationPoliciesClient *apimanagement.APIOperationPolicyClient - ApiReleasesClient *apimanagement.APIReleaseClient - ApiSchemasClient *apimanagement.APISchemaClient - ApiVersionSetClient *apimanagement.APIVersionSetClient - AuthorizationServersClient *apimanagement.AuthorizationServerClient - BackendClient *apimanagement.BackendClient - CacheClient *apimanagement.CacheClient - CertificatesClient *apimanagement.CertificateClient - DiagnosticClient *apimanagement.DiagnosticClient - DeletedServicesClient *apimanagement.DeletedServicesClient - EmailTemplateClient *apimanagement.EmailTemplateClient - GatewayClient *apimanagement.GatewayClient - GatewayApisClient *apimanagement.GatewayAPIClient - GroupClient *apimanagement.GroupClient - GroupUsersClient *apimanagement.GroupUserClient - IdentityProviderClient *apimanagement.IdentityProviderClient - LoggerClient *apimanagement.LoggerClient - NamedValueClient *apimanagement.NamedValueClient - NotificationRecipientEmailClient *apimanagement.NotificationRecipientEmailClient - NotificationRecipientUserClient *apimanagement.NotificationRecipientUserClient - OpenIdConnectClient *apimanagement.OpenIDConnectProviderClient - PolicyClient *apimanagement.PolicyClient - ProductsClient *apimanagement.ProductClient - ProductApisClient *apimanagement.ProductAPIClient - ProductGroupsClient *apimanagement.ProductGroupClient - ProductPoliciesClient *apimanagement.ProductPolicyClient - ServiceClient *apimanagement.ServiceClient - SignInClient *apimanagement.SignInSettingsClient - SignUpClient *apimanagement.SignUpSettingsClient - SubscriptionsClient *apimanagement.SubscriptionClient - TagClient *apimanagement.TagClient - TenantAccessClient *apimanagement.TenantAccessClient - UsersClient *apimanagement.UserClient + ApiClient *apimanagement.APIClient + ApiDiagnosticClient *apimanagement.APIDiagnosticClient + ApiPoliciesClient *apimanagement.APIPolicyClient + ApiOperationsClient *apimanagement.APIOperationClient + ApiOperationPoliciesClient *apimanagement.APIOperationPolicyClient + ApiReleasesClient *apimanagement.APIReleaseClient + ApiSchemasClient *apimanagement.APISchemaClient + ApiVersionSetClient *apimanagement.APIVersionSetClient + AuthorizationServersClient *apimanagement.AuthorizationServerClient + BackendClient *apimanagement.BackendClient + CacheClient *apimanagement.CacheClient + CertificatesClient *apimanagement.CertificateClient + DiagnosticClient *apimanagement.DiagnosticClient + DeletedServicesClient *apimanagement.DeletedServicesClient + EmailTemplateClient *apimanagement.EmailTemplateClient + GatewayClient *apimanagement.GatewayClient + GatewayCertificateAuthorityClient *apimanagement.GatewayCertificateAuthorityClient + GatewayApisClient *apimanagement.GatewayAPIClient + GroupClient *apimanagement.GroupClient + GroupUsersClient *apimanagement.GroupUserClient + IdentityProviderClient *apimanagement.IdentityProviderClient + LoggerClient *apimanagement.LoggerClient + NamedValueClient *apimanagement.NamedValueClient + NotificationRecipientEmailClient *apimanagement.NotificationRecipientEmailClient + NotificationRecipientUserClient *apimanagement.NotificationRecipientUserClient + OpenIdConnectClient *apimanagement.OpenIDConnectProviderClient + PolicyClient *apimanagement.PolicyClient + ProductsClient *apimanagement.ProductClient + ProductApisClient *apimanagement.ProductAPIClient + ProductGroupsClient *apimanagement.ProductGroupClient + ProductPoliciesClient *apimanagement.ProductPolicyClient + ServiceClient *apimanagement.ServiceClient + SignInClient *apimanagement.SignInSettingsClient + SignUpClient *apimanagement.SignUpSettingsClient + SubscriptionsClient *apimanagement.SubscriptionClient + TagClient *apimanagement.TagClient + TenantAccessClient *apimanagement.TenantAccessClient + UsersClient *apimanagement.UserClient } func NewClient(o *common.ClientOptions) *Client { @@ -94,6 +95,9 @@ func NewClient(o *common.ClientOptions) *Client { gatewayClient := apimanagement.NewGatewayClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&gatewayClient.Client, o.ResourceManagerAuthorizer) + gatewayCertificateAuthorityClient := apimanagement.NewGatewayCertificateAuthorityClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&gatewayCertificateAuthorityClient.Client, o.ResourceManagerAuthorizer) + gatewayApisClient := apimanagement.NewGatewayAPIClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&gatewayApisClient.Client, o.ResourceManagerAuthorizer) @@ -158,42 +162,43 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&usersClient.Client, o.ResourceManagerAuthorizer) return &Client{ - ApiClient: &apiClient, - ApiDiagnosticClient: &apiDiagnosticClient, - ApiPoliciesClient: &apiPoliciesClient, - ApiOperationsClient: &apiOperationsClient, - ApiOperationPoliciesClient: &apiOperationPoliciesClient, - ApiReleasesClient: &apiReleasesClient, - ApiSchemasClient: &apiSchemasClient, - ApiVersionSetClient: &apiVersionSetClient, - AuthorizationServersClient: &authorizationServersClient, - BackendClient: &backendClient, - CacheClient: &cacheClient, - CertificatesClient: &certificatesClient, - DiagnosticClient: &diagnosticClient, - DeletedServicesClient: &deletedServicesClient, - EmailTemplateClient: &emailTemplateClient, - GatewayClient: &gatewayClient, - GatewayApisClient: &gatewayApisClient, - GroupClient: &groupClient, - GroupUsersClient: &groupUsersClient, - IdentityProviderClient: &identityProviderClient, - LoggerClient: &loggerClient, - NamedValueClient: &namedValueClient, - NotificationRecipientEmailClient: ¬ificationRecipientEmailClient, - NotificationRecipientUserClient: ¬ificationRecipientUserClient, - OpenIdConnectClient: &openIdConnectClient, - PolicyClient: &policyClient, - ProductsClient: &productsClient, - ProductApisClient: &productApisClient, - ProductGroupsClient: &productGroupsClient, - ProductPoliciesClient: &productPoliciesClient, - ServiceClient: &serviceClient, - SignInClient: &signInClient, - SignUpClient: &signUpClient, - SubscriptionsClient: &subscriptionsClient, - TagClient: &tagClient, - TenantAccessClient: &tenantAccessClient, - UsersClient: &usersClient, + ApiClient: &apiClient, + ApiDiagnosticClient: &apiDiagnosticClient, + ApiPoliciesClient: &apiPoliciesClient, + ApiOperationsClient: &apiOperationsClient, + ApiOperationPoliciesClient: &apiOperationPoliciesClient, + ApiReleasesClient: &apiReleasesClient, + ApiSchemasClient: &apiSchemasClient, + ApiVersionSetClient: &apiVersionSetClient, + AuthorizationServersClient: &authorizationServersClient, + BackendClient: &backendClient, + CacheClient: &cacheClient, + CertificatesClient: &certificatesClient, + DiagnosticClient: &diagnosticClient, + DeletedServicesClient: &deletedServicesClient, + EmailTemplateClient: &emailTemplateClient, + GatewayClient: &gatewayClient, + GatewayCertificateAuthorityClient: &gatewayCertificateAuthorityClient, + GatewayApisClient: &gatewayApisClient, + GroupClient: &groupClient, + GroupUsersClient: &groupUsersClient, + IdentityProviderClient: &identityProviderClient, + LoggerClient: &loggerClient, + NamedValueClient: &namedValueClient, + NotificationRecipientEmailClient: ¬ificationRecipientEmailClient, + NotificationRecipientUserClient: ¬ificationRecipientUserClient, + OpenIdConnectClient: &openIdConnectClient, + PolicyClient: &policyClient, + ProductsClient: &productsClient, + ProductApisClient: &productApisClient, + ProductGroupsClient: &productGroupsClient, + ProductPoliciesClient: &productPoliciesClient, + ServiceClient: &serviceClient, + SignInClient: &signInClient, + SignUpClient: &signUpClient, + SubscriptionsClient: &subscriptionsClient, + TagClient: &tagClient, + TenantAccessClient: &tenantAccessClient, + UsersClient: &usersClient, } } diff --git a/internal/services/apimanagement/parse/gateway_certificate_authority.go b/internal/services/apimanagement/parse/gateway_certificate_authority.go new file mode 100644 index 000000000000..d8a8f530f78d --- /dev/null +++ b/internal/services/apimanagement/parse/gateway_certificate_authority.go @@ -0,0 +1,81 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type GatewayCertificateAuthorityId struct { + SubscriptionId string + ResourceGroup string + ServiceName string + GatewayName string + CertificateAuthorityName string +} + +func NewGatewayCertificateAuthorityID(subscriptionId, resourceGroup, serviceName, gatewayName, certificateAuthorityName string) GatewayCertificateAuthorityId { + return GatewayCertificateAuthorityId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ServiceName: serviceName, + GatewayName: gatewayName, + CertificateAuthorityName: certificateAuthorityName, + } +} + +func (id GatewayCertificateAuthorityId) String() string { + segments := []string{ + fmt.Sprintf("Certificate Authority Name %q", id.CertificateAuthorityName), + fmt.Sprintf("Gateway Name %q", id.GatewayName), + fmt.Sprintf("Service Name %q", id.ServiceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Gateway Certificate Authority", segmentsStr) +} + +func (id GatewayCertificateAuthorityId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ApiManagement/service/%s/gateways/%s/certificateAuthorities/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServiceName, id.GatewayName, id.CertificateAuthorityName) +} + +// GatewayCertificateAuthorityID parses a GatewayCertificateAuthority ID into an GatewayCertificateAuthorityId struct +func GatewayCertificateAuthorityID(input string) (*GatewayCertificateAuthorityId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := GatewayCertificateAuthorityId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ServiceName, err = id.PopSegment("service"); err != nil { + return nil, err + } + if resourceId.GatewayName, err = id.PopSegment("gateways"); err != nil { + return nil, err + } + if resourceId.CertificateAuthorityName, err = id.PopSegment("certificateAuthorities"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/apimanagement/parse/gateway_certificate_authority_test.go b/internal/services/apimanagement/parse/gateway_certificate_authority_test.go new file mode 100644 index 000000000000..ab5b9ab7fbf7 --- /dev/null +++ b/internal/services/apimanagement/parse/gateway_certificate_authority_test.go @@ -0,0 +1,144 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = GatewayCertificateAuthorityId{} + +func TestGatewayCertificateAuthorityIDFormatter(t *testing.T) { + actual := NewGatewayCertificateAuthorityID("12345678-1234-9876-4563-123456789012", "resGroup1", "service1", "gateway1", "cert1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/cert1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestGatewayCertificateAuthorityID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *GatewayCertificateAuthorityId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/", + Error: true, + }, + + { + // missing value for ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/", + Error: true, + }, + + { + // missing GatewayName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/", + Error: true, + }, + + { + // missing value for GatewayName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/", + Error: true, + }, + + { + // missing CertificateAuthorityName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/", + Error: true, + }, + + { + // missing value for CertificateAuthorityName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/cert1", + Expected: &GatewayCertificateAuthorityId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ServiceName: "service1", + GatewayName: "gateway1", + CertificateAuthorityName: "cert1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.APIMANAGEMENT/SERVICE/SERVICE1/GATEWAYS/GATEWAY1/CERTIFICATEAUTHORITIES/CERT1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := GatewayCertificateAuthorityID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ServiceName != v.Expected.ServiceName { + t.Fatalf("Expected %q but got %q for ServiceName", v.Expected.ServiceName, actual.ServiceName) + } + if actual.GatewayName != v.Expected.GatewayName { + t.Fatalf("Expected %q but got %q for GatewayName", v.Expected.GatewayName, actual.GatewayName) + } + if actual.CertificateAuthorityName != v.Expected.CertificateAuthorityName { + t.Fatalf("Expected %q but got %q for CertificateAuthorityName", v.Expected.CertificateAuthorityName, actual.CertificateAuthorityName) + } + } +} diff --git a/internal/services/apimanagement/parse/product_tag.go b/internal/services/apimanagement/parse/product_tag.go new file mode 100644 index 000000000000..799a0a3cd5ea --- /dev/null +++ b/internal/services/apimanagement/parse/product_tag.go @@ -0,0 +1,81 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type ProductTagId struct { + SubscriptionId string + ResourceGroup string + ServiceName string + ProductName string + TagName string +} + +func NewProductTagID(subscriptionId, resourceGroup, serviceName, productName, tagName string) ProductTagId { + return ProductTagId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ServiceName: serviceName, + ProductName: productName, + TagName: tagName, + } +} + +func (id ProductTagId) String() string { + segments := []string{ + fmt.Sprintf("Tag Name %q", id.TagName), + fmt.Sprintf("Product Name %q", id.ProductName), + fmt.Sprintf("Service Name %q", id.ServiceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Product Tag", segmentsStr) +} + +func (id ProductTagId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ApiManagement/service/%s/products/%s/tags/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServiceName, id.ProductName, id.TagName) +} + +// ProductTagID parses a ProductTag ID into an ProductTagId struct +func ProductTagID(input string) (*ProductTagId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ProductTagId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ServiceName, err = id.PopSegment("service"); err != nil { + return nil, err + } + if resourceId.ProductName, err = id.PopSegment("products"); err != nil { + return nil, err + } + if resourceId.TagName, err = id.PopSegment("tags"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/apimanagement/parse/product_tag_test.go b/internal/services/apimanagement/parse/product_tag_test.go new file mode 100644 index 000000000000..90a6dc472ca8 --- /dev/null +++ b/internal/services/apimanagement/parse/product_tag_test.go @@ -0,0 +1,144 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = ProductTagId{} + +func TestProductTagIDFormatter(t *testing.T) { + actual := NewProductTagID("12345678-1234-9876-4563-123456789012", "resGroup1", "service1", "product1", "tagId1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/tagId1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestProductTagID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ProductTagId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/", + Error: true, + }, + + { + // missing value for ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/", + Error: true, + }, + + { + // missing ProductName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/", + Error: true, + }, + + { + // missing value for ProductName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/", + Error: true, + }, + + { + // missing TagName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/", + Error: true, + }, + + { + // missing value for TagName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/tagId1", + Expected: &ProductTagId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ServiceName: "service1", + ProductName: "product1", + TagName: "tagId1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.APIMANAGEMENT/SERVICE/SERVICE1/PRODUCTS/PRODUCT1/TAGS/TAGID1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ProductTagID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ServiceName != v.Expected.ServiceName { + t.Fatalf("Expected %q but got %q for ServiceName", v.Expected.ServiceName, actual.ServiceName) + } + if actual.ProductName != v.Expected.ProductName { + t.Fatalf("Expected %q but got %q for ProductName", v.Expected.ProductName, actual.ProductName) + } + if actual.TagName != v.Expected.TagName { + t.Fatalf("Expected %q but got %q for TagName", v.Expected.TagName, actual.TagName) + } + } +} diff --git a/internal/services/apimanagement/registration.go b/internal/services/apimanagement/registration.go index c743a0563791..272ef9c4e33b 100644 --- a/internal/services/apimanagement/registration.go +++ b/internal/services/apimanagement/registration.go @@ -41,45 +41,47 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_api_management": resourceApiManagementService(), - "azurerm_api_management_api": resourceApiManagementApi(), - "azurerm_api_management_api_diagnostic": resourceApiManagementApiDiagnostic(), - "azurerm_api_management_api_operation": resourceApiManagementApiOperation(), - "azurerm_api_management_api_operation_tag": resourceApiManagementApiOperationTag(), - "azurerm_api_management_api_operation_policy": resourceApiManagementApiOperationPolicy(), - "azurerm_api_management_api_policy": resourceApiManagementApiPolicy(), - "azurerm_api_management_api_release": resourceApiManagementApiRelease(), - "azurerm_api_management_api_schema": resourceApiManagementApiSchema(), - "azurerm_api_management_api_tag": resourceApiManagementApiTag(), - "azurerm_api_management_api_version_set": resourceApiManagementApiVersionSet(), - "azurerm_api_management_authorization_server": resourceApiManagementAuthorizationServer(), - "azurerm_api_management_backend": resourceApiManagementBackend(), - "azurerm_api_management_certificate": resourceApiManagementCertificate(), - "azurerm_api_management_custom_domain": resourceApiManagementCustomDomain(), - "azurerm_api_management_diagnostic": resourceApiManagementDiagnostic(), - "azurerm_api_management_email_template": resourceApiManagementEmailTemplate(), - "azurerm_api_management_gateway": resourceApiManagementGateway(), - "azurerm_api_management_gateway_api": resourceApiManagementGatewayApi(), - "azurerm_api_management_group": resourceApiManagementGroup(), - "azurerm_api_management_group_user": resourceApiManagementGroupUser(), - "azurerm_api_management_identity_provider_aad": resourceApiManagementIdentityProviderAAD(), - "azurerm_api_management_identity_provider_aadb2c": resourceArmApiManagementIdentityProviderAADB2C(), - "azurerm_api_management_identity_provider_facebook": resourceApiManagementIdentityProviderFacebook(), - "azurerm_api_management_identity_provider_google": resourceApiManagementIdentityProviderGoogle(), - "azurerm_api_management_identity_provider_microsoft": resourceApiManagementIdentityProviderMicrosoft(), - "azurerm_api_management_identity_provider_twitter": resourceApiManagementIdentityProviderTwitter(), - "azurerm_api_management_logger": resourceApiManagementLogger(), - "azurerm_api_management_named_value": resourceApiManagementNamedValue(), - "azurerm_api_management_openid_connect_provider": resourceApiManagementOpenIDConnectProvider(), - "azurerm_api_management_policy": resourceApiManagementPolicy(), - "azurerm_api_management_product": resourceApiManagementProduct(), - "azurerm_api_management_product_api": resourceApiManagementProductApi(), - "azurerm_api_management_product_group": resourceApiManagementProductGroup(), - "azurerm_api_management_product_policy": resourceApiManagementProductPolicy(), - "azurerm_api_management_redis_cache": resourceApiManagementRedisCache(), - "azurerm_api_management_subscription": resourceApiManagementSubscription(), - "azurerm_api_management_tag": resourceApiManagementTag(), - "azurerm_api_management_user": resourceApiManagementUser(), + "azurerm_api_management": resourceApiManagementService(), + "azurerm_api_management_api": resourceApiManagementApi(), + "azurerm_api_management_api_diagnostic": resourceApiManagementApiDiagnostic(), + "azurerm_api_management_api_operation": resourceApiManagementApiOperation(), + "azurerm_api_management_api_operation_tag": resourceApiManagementApiOperationTag(), + "azurerm_api_management_api_operation_policy": resourceApiManagementApiOperationPolicy(), + "azurerm_api_management_api_policy": resourceApiManagementApiPolicy(), + "azurerm_api_management_api_release": resourceApiManagementApiRelease(), + "azurerm_api_management_api_schema": resourceApiManagementApiSchema(), + "azurerm_api_management_api_tag": resourceApiManagementApiTag(), + "azurerm_api_management_api_version_set": resourceApiManagementApiVersionSet(), + "azurerm_api_management_authorization_server": resourceApiManagementAuthorizationServer(), + "azurerm_api_management_backend": resourceApiManagementBackend(), + "azurerm_api_management_certificate": resourceApiManagementCertificate(), + "azurerm_api_management_custom_domain": resourceApiManagementCustomDomain(), + "azurerm_api_management_diagnostic": resourceApiManagementDiagnostic(), + "azurerm_api_management_email_template": resourceApiManagementEmailTemplate(), + "azurerm_api_management_gateway": resourceApiManagementGateway(), + "azurerm_api_management_gateway_api": resourceApiManagementGatewayApi(), + "azurerm_api_management_gateway_certificate_authority": resourceApiManagementGatewayCertificateAuthority(), + "azurerm_api_management_group": resourceApiManagementGroup(), + "azurerm_api_management_group_user": resourceApiManagementGroupUser(), + "azurerm_api_management_identity_provider_aad": resourceApiManagementIdentityProviderAAD(), + "azurerm_api_management_identity_provider_aadb2c": resourceArmApiManagementIdentityProviderAADB2C(), + "azurerm_api_management_identity_provider_facebook": resourceApiManagementIdentityProviderFacebook(), + "azurerm_api_management_identity_provider_google": resourceApiManagementIdentityProviderGoogle(), + "azurerm_api_management_identity_provider_microsoft": resourceApiManagementIdentityProviderMicrosoft(), + "azurerm_api_management_identity_provider_twitter": resourceApiManagementIdentityProviderTwitter(), + "azurerm_api_management_logger": resourceApiManagementLogger(), + "azurerm_api_management_named_value": resourceApiManagementNamedValue(), + "azurerm_api_management_openid_connect_provider": resourceApiManagementOpenIDConnectProvider(), + "azurerm_api_management_policy": resourceApiManagementPolicy(), + "azurerm_api_management_product": resourceApiManagementProduct(), + "azurerm_api_management_product_tag": resourceApiManagementProductTag(), + "azurerm_api_management_product_api": resourceApiManagementProductApi(), + "azurerm_api_management_product_group": resourceApiManagementProductGroup(), + "azurerm_api_management_product_policy": resourceApiManagementProductPolicy(), + "azurerm_api_management_redis_cache": resourceApiManagementRedisCache(), + "azurerm_api_management_subscription": resourceApiManagementSubscription(), + "azurerm_api_management_tag": resourceApiManagementTag(), + "azurerm_api_management_user": resourceApiManagementUser(), } } diff --git a/internal/services/apimanagement/resourceids.go b/internal/services/apimanagement/resourceids.go index 23a8c7fe2898..a9cfc97f3952 100644 --- a/internal/services/apimanagement/resourceids.go +++ b/internal/services/apimanagement/resourceids.go @@ -30,6 +30,7 @@ package apimanagement //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProductApi -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/apis/api1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProductGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/groups/group1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProductPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/policies/policy1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProductTag -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/tagId1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Property -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/namedValues/namedvalue1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Subscription -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/subscriptions/subscription1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=User -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/users/user1 @@ -37,3 +38,4 @@ package apimanagement //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ApiRelease -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/apis/api1/releases/release1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Tag -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/tags/tag1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ApiTag -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/apis/api1/tags/tag1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=GatewayCertificateAuthority -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/cert1 diff --git a/internal/services/apimanagement/validate/api_management_sku.go b/internal/services/apimanagement/validate/api_management_sku.go index e73eab348767..0616c65f6a82 100644 --- a/internal/services/apimanagement/validate/api_management_sku.go +++ b/internal/services/apimanagement/validate/api_management_sku.go @@ -9,7 +9,7 @@ import ( func ApimSkuName() pluginsdk.SchemaValidateFunc { return validation.StringMatch( - regexp.MustCompile(`^Consumption_0$|^Basic_(1|2)$|^Developer_1$|^Premium_([1-9]|10)$|^Standard_[1-4]$`), + regexp.MustCompile(`^Consumption_0$|^Basic_(1|2)$|^Developer_1$|^Premium_([1-9][0-9]{0,1})$|^Standard_[1-4]$`), `This is not a valid Api Management sku name.`, ) } diff --git a/internal/services/apimanagement/validate/api_management_sku_test.go b/internal/services/apimanagement/validate/api_management_sku_test.go index fdac8f2a6786..8ae2e7a74d01 100644 --- a/internal/services/apimanagement/validate/api_management_sku_test.go +++ b/internal/services/apimanagement/validate/api_management_sku_test.go @@ -39,10 +39,20 @@ func TestApimSkuName(t *testing.T) { valid: false, }, { - name: "Premium_11", - input: "Premium_11", + name: "Premium_101", + input: "Premium_101", valid: false, }, + { + name: "Premium_10", + input: "Premium_10", + valid: true, + }, + { + name: "Premium_12", + input: "Premium_12", + valid: true, + }, { name: "Premium_7", input: "Premium_7", diff --git a/internal/services/apimanagement/validate/gateway_certificate_authority_id.go b/internal/services/apimanagement/validate/gateway_certificate_authority_id.go new file mode 100644 index 000000000000..f12688afa3fe --- /dev/null +++ b/internal/services/apimanagement/validate/gateway_certificate_authority_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" +) + +func GatewayCertificateAuthorityID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.GatewayCertificateAuthorityID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/apimanagement/validate/gateway_certificate_authority_id_test.go b/internal/services/apimanagement/validate/gateway_certificate_authority_id_test.go new file mode 100644 index 000000000000..a467e6f53c6f --- /dev/null +++ b/internal/services/apimanagement/validate/gateway_certificate_authority_id_test.go @@ -0,0 +1,100 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestGatewayCertificateAuthorityID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/", + Valid: false, + }, + + { + // missing value for ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/", + Valid: false, + }, + + { + // missing GatewayName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/", + Valid: false, + }, + + { + // missing value for GatewayName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/", + Valid: false, + }, + + { + // missing CertificateAuthorityName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/", + Valid: false, + }, + + { + // missing value for CertificateAuthorityName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/gateways/gateway1/certificateAuthorities/cert1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.APIMANAGEMENT/SERVICE/SERVICE1/GATEWAYS/GATEWAY1/CERTIFICATEAUTHORITIES/CERT1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := GatewayCertificateAuthorityID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/apimanagement/validate/product_tag_id.go b/internal/services/apimanagement/validate/product_tag_id.go new file mode 100644 index 000000000000..9827d25af5b5 --- /dev/null +++ b/internal/services/apimanagement/validate/product_tag_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" +) + +func ProductTagID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ProductTagID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/apimanagement/validate/product_tag_id_test.go b/internal/services/apimanagement/validate/product_tag_id_test.go new file mode 100644 index 000000000000..75d10408ab8f --- /dev/null +++ b/internal/services/apimanagement/validate/product_tag_id_test.go @@ -0,0 +1,100 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestProductTagID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/", + Valid: false, + }, + + { + // missing value for ServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/", + Valid: false, + }, + + { + // missing ProductName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/", + Valid: false, + }, + + { + // missing value for ProductName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/", + Valid: false, + }, + + { + // missing TagName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/", + Valid: false, + }, + + { + // missing value for TagName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ApiManagement/service/service1/products/product1/tags/tagId1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.APIMANAGEMENT/SERVICE/SERVICE1/PRODUCTS/PRODUCT1/TAGS/TAGID1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ProductTagID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/appconfiguration/app_configuration_data_source.go b/internal/services/appconfiguration/app_configuration_data_source.go index 8b4e22355910..ad4feb0ac044 100644 --- a/internal/services/appconfiguration/app_configuration_data_source.go +++ b/internal/services/appconfiguration/app_configuration_data_source.go @@ -44,6 +44,11 @@ func dataSourceAppConfiguration() *pluginsdk.Resource { Computed: true, }, + "public_network_access": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "primary_read_key": { Type: pluginsdk.TypeList, Computed: true, @@ -177,6 +182,7 @@ func dataSourceAppConfigurationRead(d *pluginsdk.ResourceData, meta interface{}) if props := model.Properties; props != nil { d.Set("endpoint", props.Endpoint) + d.Set("public_network_access", props.PublicNetworkAccess) } accessKeys := flattenAppConfigurationAccessKeys(resultPage.Items) diff --git a/internal/services/appconfiguration/app_configuration_data_source_test.go b/internal/services/appconfiguration/app_configuration_data_source_test.go index df74ffc04ae3..922a6da7900f 100644 --- a/internal/services/appconfiguration/app_configuration_data_source_test.go +++ b/internal/services/appconfiguration/app_configuration_data_source_test.go @@ -30,6 +30,7 @@ func TestAccAppConfigurationDataSource_basic(t *testing.T) { check.That(data.ResourceName).Key("primary_write_key.0.connection_string").Exists(), check.That(data.ResourceName).Key("primary_write_key.0.id").Exists(), check.That(data.ResourceName).Key("primary_write_key.0.secret").Exists(), + check.That(data.ResourceName).Key("public_network_access").Exists(), check.That(data.ResourceName).Key("secondary_read_key.0.connection_string").Exists(), check.That(data.ResourceName).Key("secondary_read_key.0.id").Exists(), check.That(data.ResourceName).Key("secondary_read_key.0.secret").Exists(), diff --git a/internal/services/appconfiguration/app_configuration_resource.go b/internal/services/appconfiguration/app_configuration_resource.go index ce4b3054605d..9261f7333176 100644 --- a/internal/services/appconfiguration/app_configuration_resource.go +++ b/internal/services/appconfiguration/app_configuration_resource.go @@ -69,6 +69,13 @@ func resourceAppConfiguration() *pluginsdk.Resource { Computed: true, }, + "public_network_access": { + Type: pluginsdk.TypeString, + Optional: true, + Default: nil, + ValidateFunc: validation.StringInSlice(configurationstores.PossibleValuesForPublicNetworkAccess(), true), + }, + "primary_read_key": { Type: pluginsdk.TypeList, Computed: true, @@ -199,6 +206,20 @@ func resourceAppConfigurationCreate(d *pluginsdk.ResourceData, meta interface{}) Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + publicNetworkAccessValue, publicNetworkAccessNotEmpty := d.GetOk("public_network_access") + + if publicNetworkAccessNotEmpty { + + publicNetworkAccess, err := parsePublicNetworkAccess(publicNetworkAccessValue.(string)) + if err != nil { + return fmt.Errorf("unable to parse public_network_access: %+v", err) + } + properties := &configurationstores.ConfigurationStoreProperties{ + PublicNetworkAccess: publicNetworkAccess, + } + parameters.Properties = properties + } + identity, err := identity.ExpandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) @@ -231,6 +252,18 @@ func resourceAppConfigurationUpdate(d *pluginsdk.ResourceData, meta interface{}) Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + publicNetworkAccessValue, publicNetworkAccessNotEmpty := d.GetOk("public_network_access") + if publicNetworkAccessNotEmpty { + publicNetworkAccess, err := parsePublicNetworkAccess(publicNetworkAccessValue.(string)) + if err != nil { + return fmt.Errorf("unable to parse public_network_access: %+v", err) + } + properties := &configurationstores.ConfigurationStorePropertiesUpdateParameters{ + PublicNetworkAccess: publicNetworkAccess, + } + parameters.Properties = properties + } + if d.HasChange("identity") { identity, err := identity.ExpandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) if err != nil { @@ -280,6 +313,7 @@ func resourceAppConfigurationRead(d *pluginsdk.ResourceData, meta interface{}) e if props := model.Properties; props != nil { d.Set("endpoint", props.Endpoint) + d.Set("public_network_access", props.PublicNetworkAccess) } accessKeys := flattenAppConfigurationAccessKeys(resultPage.Items) @@ -388,3 +422,17 @@ func flattenAppConfigurationAccessKey(input configurationstores.ApiKey) []interf }, } } + +func parsePublicNetworkAccess(input string) (*configurationstores.PublicNetworkAccess, error) { + vals := map[string]configurationstores.PublicNetworkAccess{ + "disabled": configurationstores.PublicNetworkAccessDisabled, + "enabled": configurationstores.PublicNetworkAccessEnabled, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := configurationstores.PublicNetworkAccess(input) + return &out, nil +} diff --git a/internal/services/appconfiguration/app_configuration_resource_test.go b/internal/services/appconfiguration/app_configuration_resource_test.go index 053e7f7a8098..d457bfcd697a 100644 --- a/internal/services/appconfiguration/app_configuration_resource_test.go +++ b/internal/services/appconfiguration/app_configuration_resource_test.go @@ -203,10 +203,11 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_app_configuration" "test" { - name = "testaccappconf%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "standard" + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + public_network_access = "Disabled" + sku = "standard" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } @@ -237,10 +238,11 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_app_configuration" "test" { - name = "testaccappconf%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "standard" + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + public_network_access = "Disabled" + sku = "standard" tags = { environment = "development" @@ -261,10 +263,11 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_app_configuration" "test" { - name = "testaccappconf%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "standard" + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + public_network_access = "Disabled" + sku = "standard" identity { type = "SystemAssigned" @@ -295,10 +298,11 @@ resource "azurerm_user_assigned_identity" "test" { } resource "azurerm_app_configuration" "test" { - name = "testaccappconf%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "standard" + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + public_network_access = "Disabled" + sku = "standard" identity { type = "UserAssigned" @@ -326,10 +330,11 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_app_configuration" "test" { - name = "testaccappconf%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - sku = "standard" + name = "testaccappconf%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + public_network_access = "Disabled" + sku = "standard" tags = { Environment = "Production" diff --git a/internal/services/applicationinsights/application_insights_workbook_resource.go b/internal/services/applicationinsights/application_insights_workbook_resource.go new file mode 100644 index 000000000000..51fbb666fc4b --- /dev/null +++ b/internal/services/applicationinsights/application_insights_workbook_resource.go @@ -0,0 +1,331 @@ +package applicationinsights + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + workbooks "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2022-04-01/workbooksapis" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/applicationinsights/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ApplicationInsightsWorkbookModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Category string `tfschema:"category"` + Description string `tfschema:"description"` + DisplayName string `tfschema:"display_name"` + Location string `tfschema:"location"` + DataJson string `tfschema:"data_json"` + SourceId string `tfschema:"source_id"` + StorageContainerId string `tfschema:"storage_container_id"` + Tags map[string]string `tfschema:"tags"` +} + +type ApplicationInsightsWorkbookResource struct{} + +var _ sdk.ResourceWithUpdate = ApplicationInsightsWorkbookResource{} + +func (r ApplicationInsightsWorkbookResource) ResourceType() string { + return "azurerm_application_insights_workbook" +} + +func (r ApplicationInsightsWorkbookResource) ModelObject() interface{} { + return &ApplicationInsightsWorkbookModel{} +} + +func (r ApplicationInsightsWorkbookResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return workbooks.ValidateWorkbookID +} + +func (r ApplicationInsightsWorkbookResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.IsUUID, + validate.StringDoesNotContainUpperCaseLetter, + ), + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "display_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "data_json": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: pluginsdk.SuppressJsonDiff, + }, + + "source_id": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "azure monitor", + ValidateFunc: validate.StringDoesNotContainUpperCaseLetter, + }, + + "category": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "workbook", + ValidateFunc: validation.StringIsNotEmpty, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "identity": commonschema.SystemAssignedUserAssignedIdentityOptionalForceNew(), + + "storage_container_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + RequiredWith: []string{ + "identity", + }, + }, + + "tags": { + Type: pluginsdk.TypeMap, + Optional: true, + ValidateFunc: validate.WorkbookTags, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + } +} + +func (r ApplicationInsightsWorkbookResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r ApplicationInsightsWorkbookResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model ApplicationInsightsWorkbookModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.AppInsights.WorkbookClient + subscriptionId := metadata.Client.Account.SubscriptionId + id := workbooks.NewWorkbookID(subscriptionId, model.ResourceGroupName, model.Name) + existing, err := client.WorkbooksGet(ctx, id, workbooks.WorkbooksGetOperationOptions{CanFetchContent: utils.Bool(true)}) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + identityValue, err := identity.ExpandLegacySystemAndUserAssignedMap(metadata.ResourceData.Get("identity").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + kindValue := workbooks.WorkbookSharedTypeKindShared + properties := &workbooks.Workbook{ + Identity: identityValue, + Kind: &kindValue, + Location: utils.String(location.Normalize(model.Location)), + Properties: &workbooks.WorkbookProperties{ + Category: model.Category, + DisplayName: model.DisplayName, + SerializedData: model.DataJson, + SourceId: &model.SourceId, + }, + + Tags: &model.Tags, + } + + if model.Description != "" { + properties.Properties.Description = &model.Description + } + + if model.StorageContainerId != "" { + properties.Properties.StorageUri = &model.StorageContainerId + } + + if _, err := client.WorkbooksCreateOrUpdate(ctx, id, *properties, workbooks.WorkbooksCreateOrUpdateOperationOptions{SourceId: &model.SourceId}); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r ApplicationInsightsWorkbookResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AppInsights.WorkbookClient + + id, err := workbooks.ParseWorkbookID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model ApplicationInsightsWorkbookModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.WorkbooksGet(ctx, *id, workbooks.WorkbooksGetOperationOptions{CanFetchContent: utils.Bool(true)}) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := resp.Model + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + if metadata.ResourceData.HasChange("category") { + properties.Properties.Category = model.Category + } + + if metadata.ResourceData.HasChange("description") { + properties.Properties.Description = &model.Description + } + + if metadata.ResourceData.HasChange("display_name") { + properties.Properties.DisplayName = model.DisplayName + } + + if metadata.ResourceData.HasChange("data_json") { + properties.Properties.SerializedData = model.DataJson + } + + if metadata.ResourceData.HasChange("tags") { + properties.Tags = &model.Tags + } + + if _, err := client.WorkbooksCreateOrUpdate(ctx, *id, *properties, workbooks.WorkbooksCreateOrUpdateOperationOptions{SourceId: &model.SourceId}); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r ApplicationInsightsWorkbookResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AppInsights.WorkbookClient + + id, err := workbooks.ParseWorkbookID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.WorkbooksGet(ctx, *id, workbooks.WorkbooksGetOperationOptions{CanFetchContent: utils.Bool(true)}) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + state := ApplicationInsightsWorkbookModel{ + Name: id.ResourceName, + ResourceGroupName: id.ResourceGroupName, + Location: location.NormalizeNilable(model.Location), + } + + identityValue, err := identity.FlattenLegacySystemAndUserAssignedMap(model.Identity) + if err != nil { + return fmt.Errorf("flattening `identity`: %+v", err) + } + + if err := metadata.ResourceData.Set("identity", identityValue); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if properties := model.Properties; properties != nil { + state.Category = properties.Category + + if properties.Description != nil { + state.Description = *properties.Description + } + + state.DisplayName = properties.DisplayName + + state.DataJson = properties.SerializedData + + if properties.SourceId != nil { + state.SourceId = *properties.SourceId + } + + if properties.StorageUri != nil { + state.StorageContainerId = *properties.StorageUri + } + } + + if model.Tags != nil { + // The backend returns a tags with key `hidden-title` by default. Since it has the same value with `display_name` and will cause inconsistency with user's configuration, remove it as a workaround. + if _, ok := (*model.Tags)["hidden-title"]; ok { + delete(*model.Tags, "hidden-title") + } + + state.Tags = *model.Tags + } + + return metadata.Encode(&state) + }, + } +} + +func (r ApplicationInsightsWorkbookResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AppInsights.WorkbookClient + + id, err := workbooks.ParseWorkbookID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.WorkbooksDelete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} diff --git a/internal/services/applicationinsights/application_insights_workbook_resource_test.go b/internal/services/applicationinsights/application_insights_workbook_resource_test.go new file mode 100644 index 000000000000..dab1fdc98ea3 --- /dev/null +++ b/internal/services/applicationinsights/application_insights_workbook_resource_test.go @@ -0,0 +1,352 @@ +package applicationinsights_test + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + workbooks "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2022-04-01/workbooksapis" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ApplicationInsightsWorkbookResource struct{} + +func TestAccApplicationInsightsWorkbook_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_insights_workbook", "test") + r := ApplicationInsightsWorkbookResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationInsightsWorkbook_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_insights_workbook", "test") + r := ApplicationInsightsWorkbookResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccApplicationInsightsWorkbook_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_insights_workbook", "test") + r := ApplicationInsightsWorkbookResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationInsightsWorkbook_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_insights_workbook", "test") + r := ApplicationInsightsWorkbookResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationInsightsWorkbook_hiddenTitleInTags(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_insights_workbook", "test") + r := ApplicationInsightsWorkbookResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.hiddenTitleInTags(data), + ExpectError: regexp.MustCompile("a tag with the key `hidden-title` should not be used to set the display name. Please Use `display_name` instead"), + }, + }) +} + +func (r ApplicationInsightsWorkbookResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := workbooks.ParseWorkbookID(state.ID) + if err != nil { + return nil, err + } + + client := clients.AppInsights.WorkbookClient + resp, err := client.WorkbooksGet(ctx, *id, workbooks.WorkbooksGetOperationOptions{CanFetchContent: utils.Bool(true)}) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (r ApplicationInsightsWorkbookResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%d" + location = "%s" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r ApplicationInsightsWorkbookResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_application_insights_workbook" "test" { + name = "be1ad266-d329-4454-b693-8287e4d3b35d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + display_name = "acctest-amw-%d" + data_json = jsonencode({ + "version" = "Notebook/1.0", + "items" = [ + { + "type" = 1, + "content" = { + "json" = "Test2022" + }, + "name" = "text - 0" + } + ], + "isLocked" = false, + "fallbackResourceIds" = [ + "Azure Monitor" + ] + }) +} +`, template, data.RandomInteger) +} + +func (r ApplicationInsightsWorkbookResource) hiddenTitleInTags(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_application_insights_workbook" "test" { + name = "be1ad266-d329-4454-b693-8287e4d3b35d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + display_name = "acctest-amw-%d" + data_json = jsonencode({ + "version" = "Notebook/1.0", + "items" = [ + { + "type" = 1, + "content" = { + "json" = "Test2022" + }, + "name" = "text - 0" + } + ], + "isLocked" = false, + "fallbackResourceIds" = [ + "Azure Monitor" + ] + }) + tags = { + hidden-title = "Test Display Name" + } +} +`, template, data.RandomInteger) +} + +func (r ApplicationInsightsWorkbookResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` + %s + +resource "azurerm_application_insights_workbook" "import" { + name = azurerm_application_insights_workbook.test.name + resource_group_name = azurerm_application_insights_workbook.test.resource_group_name + location = azurerm_application_insights_workbook.test.location + category = azurerm_application_insights_workbook.test.category + display_name = azurerm_application_insights_workbook.test.display_name + source_id = azurerm_application_insights_workbook.test.source_id + data_json = azurerm_application_insights_workbook.test.data_json +} +`, config) +} + +func (r ApplicationInsightsWorkbookResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestUAI-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_storage_account" "test" { + name = "acctestsads%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" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_storage_account.test.id + role_definition_name = "Storage Blob Data Owner" + principal_id = azurerm_user_assigned_identity.test.principal_id +} + +resource "azurerm_application_insights_workbook" "test" { + name = "0f498fab-2989-4395-b084-fc092d83a6b1" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + display_name = "acctest-amw-1" + source_id = lower(azurerm_resource_group.test.id) + category = "workbook1" + description = "description1" + storage_container_id = azurerm_storage_container.test.resource_manager_id + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + + data_json = jsonencode({ + "version" = "Notebook/1.0", + "items" = [ + { + "type" = 1, + "content" = { + "json" = "Test2021" + }, + "name" = "text - 0" + } + ], + "isLocked" = false, + "fallbackResourceIds" = [ + "Azure Monitor" + ] + }) + tags = { + env = "test" + } + + depends_on = [ + azurerm_role_assignment.test, + ] +} +`, template, data.RandomInteger, data.RandomString) +} + +func (r ApplicationInsightsWorkbookResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestUAI-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_storage_account" "test" { + name = "acctestsads%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" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_storage_account.test.id + role_definition_name = "Storage Blob Data Owner" + principal_id = azurerm_user_assigned_identity.test.principal_id +} + +resource "azurerm_application_insights_workbook" "test" { + name = "0f498fab-2989-4395-b084-fc092d83a6b1" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + display_name = "acctest-amw-2" + source_id = "azure monitor" + category = "workbook2" + description = "description2" + storage_container_id = azurerm_storage_container.test.resource_manager_id + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + + data_json = jsonencode({ + "version" = "Notebook/1.0", + "items" = [ + { + "type" = 1, + "content" = { + "json" = "Test2022" + }, + "name" = "text - 0" + } + ], + "isLocked" = false, + "fallbackResourceIds" = [ + "Azure Monitor" + ] + }) + tags = { + env = "test2" + } + + depends_on = [ + azurerm_role_assignment.test, + ] +} +`, template, data.RandomInteger, data.RandomString) +} diff --git a/internal/services/applicationinsights/application_insights_workbook_template_resource.go b/internal/services/applicationinsights/application_insights_workbook_template_resource.go index d984bd50a1cc..881d0254e2cb 100644 --- a/internal/services/applicationinsights/application_insights_workbook_template_resource.go +++ b/internal/services/applicationinsights/application_insights_workbook_template_resource.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/applicationinsights" + workbooktemplates "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/workbooktemplatesapis" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -49,7 +49,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) ModelObject() interface{} { } func (r ApplicationInsightsWorkbookTemplateResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { - return applicationinsights.ValidateWorkbookTemplateID + return workbooktemplates.ValidateWorkbookTemplateID } func (r ApplicationInsightsWorkbookTemplateResource) Arguments() map[string]*pluginsdk.Schema { @@ -150,7 +150,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Create() sdk.ResourceFunc { client := metadata.Client.AppInsights.WorkbookTemplateClient subscriptionId := metadata.Client.Account.SubscriptionId - id := applicationinsights.NewWorkbookTemplateID(subscriptionId, model.ResourceGroupName, model.Name) + id := workbooktemplates.NewWorkbookTemplateID(subscriptionId, model.ResourceGroupName, model.Name) existing, err := client.WorkbookTemplatesGet(ctx, id) if err != nil && !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing %s: %+v", id, err) @@ -166,9 +166,9 @@ func (r ApplicationInsightsWorkbookTemplateResource) Create() sdk.ResourceFunc { return err } - properties := &applicationinsights.WorkbookTemplate{ + properties := &workbooktemplates.WorkbookTemplate{ Location: location.Normalize(model.Location), - Properties: &applicationinsights.WorkbookTemplateProperties{ + Properties: &workbooktemplates.WorkbookTemplateProperties{ Priority: &model.Priority, TemplateData: templateDataValue, }, @@ -181,7 +181,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Create() sdk.ResourceFunc { } if model.Localized != "" { - var localizedValue map[string][]applicationinsights.WorkbookTemplateLocalizedGallery + var localizedValue map[string][]workbooktemplates.WorkbookTemplateLocalizedGallery if err := json.Unmarshal([]byte(model.Localized), &localizedValue); err != nil { return err } @@ -214,7 +214,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Update() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AppInsights.WorkbookTemplateClient - id, err := applicationinsights.ParseWorkbookTemplateID(metadata.ResourceData.Id()) + id, err := workbooktemplates.ParseWorkbookTemplateID(metadata.ResourceData.Id()) if err != nil { return err } @@ -264,7 +264,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Update() sdk.ResourceFunc { } if metadata.ResourceData.HasChange("localized") { - var localizedValue map[string][]applicationinsights.WorkbookTemplateLocalizedGallery + var localizedValue map[string][]workbooktemplates.WorkbookTemplateLocalizedGallery if err := json.Unmarshal([]byte(model.Localized), &localizedValue); err != nil { return err } @@ -291,7 +291,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Read() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AppInsights.WorkbookTemplateClient - id, err := applicationinsights.ParseWorkbookTemplateID(metadata.ResourceData.Id()) + id, err := workbooktemplates.ParseWorkbookTemplateID(metadata.ResourceData.Id()) if err != nil { return err } @@ -366,7 +366,7 @@ func (r ApplicationInsightsWorkbookTemplateResource) Delete() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AppInsights.WorkbookTemplateClient - id, err := applicationinsights.ParseWorkbookTemplateID(metadata.ResourceData.Id()) + id, err := workbooktemplates.ParseWorkbookTemplateID(metadata.ResourceData.Id()) if err != nil { return err } @@ -380,10 +380,10 @@ func (r ApplicationInsightsWorkbookTemplateResource) Delete() sdk.ResourceFunc { } } -func expandWorkbookTemplateGalleryModel(inputList []WorkbookTemplateGalleryModel) (*[]applicationinsights.WorkbookTemplateGallery, error) { - var outputList []applicationinsights.WorkbookTemplateGallery +func expandWorkbookTemplateGalleryModel(inputList []WorkbookTemplateGalleryModel) (*[]workbooktemplates.WorkbookTemplateGallery, error) { + var outputList []workbooktemplates.WorkbookTemplateGallery for _, input := range inputList { - output := applicationinsights.WorkbookTemplateGallery{ + output := workbooktemplates.WorkbookTemplateGallery{ Category: utils.String(input.Category), Name: utils.String(input.Name), Order: utils.Int64(input.Order), @@ -397,7 +397,7 @@ func expandWorkbookTemplateGalleryModel(inputList []WorkbookTemplateGalleryModel return &outputList, nil } -func flattenWorkbookTemplateGalleryModel(inputList *[]applicationinsights.WorkbookTemplateGallery) ([]WorkbookTemplateGalleryModel, error) { +func flattenWorkbookTemplateGalleryModel(inputList *[]workbooktemplates.WorkbookTemplateGallery) ([]WorkbookTemplateGalleryModel, error) { var outputList []WorkbookTemplateGalleryModel if inputList == nil { return outputList, nil diff --git a/internal/services/applicationinsights/application_insights_workbook_template_resource_test.go b/internal/services/applicationinsights/application_insights_workbook_template_resource_test.go index 64fef725c218..610f9aec96c4 100644 --- a/internal/services/applicationinsights/application_insights_workbook_template_resource_test.go +++ b/internal/services/applicationinsights/application_insights_workbook_template_resource_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/applicationinsights" + workbooktemplates "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/workbooktemplatesapis" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -80,7 +80,7 @@ func TestAccApplicationInsightsWorkbookTemplate_update(t *testing.T) { } func (r ApplicationInsightsWorkbookTemplateResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := applicationinsights.ParseWorkbookTemplateID(state.ID) + id, err := workbooktemplates.ParseWorkbookTemplateID(state.ID) if err != nil { return nil, err } diff --git a/internal/services/applicationinsights/client/client.go b/internal/services/applicationinsights/client/client.go index 8f69466b6aaa..0746f0d7e629 100644 --- a/internal/services/applicationinsights/client/client.go +++ b/internal/services/applicationinsights/client/client.go @@ -2,7 +2,8 @@ package client import ( "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2020-02-02/insights" - workbookTemplate "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/applicationinsights" + workbooktemplates "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2020-11-20/workbooktemplatesapis" + workbooks "github.com/hashicorp/go-azure-sdk/resource-manager/applicationinsights/2022-04-01/workbooksapis" "github.com/hashicorp/terraform-provider-azurerm/internal/common" "github.com/hashicorp/terraform-provider-azurerm/internal/services/applicationinsights/azuresdkhacks" ) @@ -14,7 +15,8 @@ type Client struct { WebTestsClient *azuresdkhacks.WebTestsClient BillingClient *insights.ComponentCurrentBillingFeaturesClient SmartDetectionRuleClient *insights.ProactiveDetectionConfigurationsClient - WorkbookTemplateClient *workbookTemplate.ApplicationInsightsClient + WorkbookClient *workbooks.WorkbooksAPIsClient + WorkbookTemplateClient *workbooktemplates.WorkbookTemplatesAPIsClient } func NewClient(o *common.ClientOptions) *Client { @@ -37,7 +39,10 @@ func NewClient(o *common.ClientOptions) *Client { smartDetectionRuleClient := insights.NewProactiveDetectionConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&smartDetectionRuleClient.Client, o.ResourceManagerAuthorizer) - workbookTemplateClient := workbookTemplate.NewApplicationInsightsClientWithBaseURI(o.ResourceManagerEndpoint) + workbookClient := workbooks.NewWorkbooksAPIsClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&workbookClient.Client, o.ResourceManagerAuthorizer) + + workbookTemplateClient := workbooktemplates.NewWorkbookTemplatesAPIsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&workbookTemplateClient.Client, o.ResourceManagerAuthorizer) return &Client{ @@ -47,6 +52,7 @@ func NewClient(o *common.ClientOptions) *Client { WebTestsClient: &webTestsWorkaroundClient, BillingClient: &billingClient, SmartDetectionRuleClient: &smartDetectionRuleClient, + WorkbookClient: &workbookClient, WorkbookTemplateClient: &workbookTemplateClient, } } diff --git a/internal/services/applicationinsights/registration.go b/internal/services/applicationinsights/registration.go index cc0f01206e8c..ff545e173a34 100644 --- a/internal/services/applicationinsights/registration.go +++ b/internal/services/applicationinsights/registration.go @@ -54,6 +54,7 @@ func (r Registration) DataSources() []sdk.DataSource { // Resources returns a list of Resources supported by this Service func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ + ApplicationInsightsWorkbookResource{}, ApplicationInsightsWorkbookTemplateResource{}, } } diff --git a/internal/services/applicationinsights/validate/workbook_name.go b/internal/services/applicationinsights/validate/workbook_name.go new file mode 100644 index 000000000000..68f1d61b4ffd --- /dev/null +++ b/internal/services/applicationinsights/validate/workbook_name.go @@ -0,0 +1,21 @@ +package validate + +import ( + "fmt" + "strings" +) + +func StringDoesNotContainUpperCaseLetter(input interface{}, k string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if strings.ToLower(v) != v { + errors = append(errors, fmt.Errorf("expected value of %s to not contain any uppercase letter", k)) + return + } + + return +} diff --git a/internal/services/applicationinsights/validate/workbook_tags.go b/internal/services/applicationinsights/validate/workbook_tags.go new file mode 100644 index 000000000000..3eccb45d3bc9 --- /dev/null +++ b/internal/services/applicationinsights/validate/workbook_tags.go @@ -0,0 +1,21 @@ +package validate + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" +) + +func WorkbookTags(i interface{}, k string) (warnings []string, errors []error) { + warnings, errors = tags.Validate(i, k) + if len(errors) > 0 { + return + } + + tagsMap := i.(map[string]interface{}) + if _, ok := tagsMap["hidden-title"]; ok { + errors = append(errors, fmt.Errorf("a tag with the key `hidden-title` should not be used to set the display name. Please Use `display_name` instead")) + } + + return +} diff --git a/internal/services/appservice/helpers/function_app_schema.go b/internal/services/appservice/helpers/function_app_schema.go index 333fe1d20814..848b1458b27f 100644 --- a/internal/services/appservice/helpers/function_app_schema.go +++ b/internal/services/appservice/helpers/function_app_schema.go @@ -1551,7 +1551,7 @@ func ExpandSiteConfigLinuxFunctionApp(siteConfig []SiteConfigLinuxFunctionApp, e if linuxAppStack.NodeVersion != "" { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "node", false) appSettings = updateOrAppendAppSettings(appSettings, "WEBSITE_NODE_DEFAULT_VERSION", linuxAppStack.NodeVersion, false) - expanded.LinuxFxVersion = utils.String(fmt.Sprintf("Node|%s", linuxAppStack.NodeVersion)) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("NODE|%s", linuxAppStack.NodeVersion)) } if linuxAppStack.PythonVersion != "" { @@ -1687,6 +1687,10 @@ func ExpandSiteConfigLinuxFunctionApp(siteConfig []SiteConfigLinuxFunctionApp, e expanded.MinimumElasticInstanceCount = utils.Int32(int32(linuxSiteConfig.ElasticInstanceMinimum)) } + if metadata.ResourceData.HasChange("site_config.0.runtime_scale_monitoring_enabled") { + expanded.FunctionsRuntimeScaleMonitoringEnabled = utils.Bool(linuxSiteConfig.RuntimeScaleMonitoring) + } + expanded.AppSettings = &appSettings return expanded, nil @@ -1798,36 +1802,36 @@ func ExpandSiteConfigWindowsFunctionApp(siteConfig []SiteConfigWindowsFunctionAp if windowsAppStack.DotNetVersion != "" { if windowsAppStack.DotNetIsolated { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "dotnet-isolated", false) - windowsSiteConfig.WindowsFxVersion = fmt.Sprintf("DOTNET-ISOLATED|%s", windowsAppStack.DotNetVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("DOTNET-ISOLATED|%s", windowsAppStack.DotNetVersion)) } else { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "dotnet", false) - windowsSiteConfig.WindowsFxVersion = fmt.Sprintf("DOTNET|%s", windowsAppStack.DotNetVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("DOTNET|%s", windowsAppStack.DotNetVersion)) } } if windowsAppStack.NodeVersion != "" { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "node", false) appSettings = updateOrAppendAppSettings(appSettings, "WEBSITE_NODE_DEFAULT_VERSION", windowsAppStack.NodeVersion, false) - windowsSiteConfig.WindowsFxVersion = fmt.Sprintf("Node|%s", windowsAppStack.NodeVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("Node|%s", windowsAppStack.NodeVersion)) } if windowsAppStack.JavaVersion != "" { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "java", false) - windowsSiteConfig.WindowsFxVersion = fmt.Sprintf("Java|%s", windowsAppStack.JavaVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("Java|%s", windowsAppStack.JavaVersion)) } if windowsAppStack.PowerShellCoreVersion != "" { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "powershell", false) - windowsSiteConfig.WindowsFxVersion = fmt.Sprintf("PowerShell|%s", windowsAppStack.PowerShellCoreVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("PowerShell|%s", windowsAppStack.PowerShellCoreVersion)) } if windowsAppStack.CustomHandler { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "custom", false) - windowsSiteConfig.WindowsFxVersion = "" // Custom needs an explicit empty string here + expanded.WindowsFxVersion = utils.String("") // Custom needs an explicit empty string here } } else { appSettings = updateOrAppendAppSettings(appSettings, "FUNCTIONS_WORKER_RUNTIME", "", true) - windowsSiteConfig.WindowsFxVersion = "" + expanded.WindowsFxVersion = utils.String("") } if metadata.ResourceData.HasChange("site_config.0.vnet_route_all_enabled") { @@ -1923,6 +1927,10 @@ func ExpandSiteConfigWindowsFunctionApp(siteConfig []SiteConfigWindowsFunctionAp expanded.MinimumElasticInstanceCount = utils.Int32(int32(windowsSiteConfig.ElasticInstanceMinimum)) } + if metadata.ResourceData.HasChange("site_config.0.runtime_scale_monitoring_enabled") { + expanded.FunctionsRuntimeScaleMonitoringEnabled = utils.Bool(windowsSiteConfig.RuntimeScaleMonitoring) + } + expanded.AppSettings = &appSettings return expanded, nil diff --git a/internal/services/appservice/helpers/function_app_slot_schema.go b/internal/services/appservice/helpers/function_app_slot_schema.go index dd213469737a..7c714e60ddd8 100644 --- a/internal/services/appservice/helpers/function_app_slot_schema.go +++ b/internal/services/appservice/helpers/function_app_slot_schema.go @@ -734,14 +734,14 @@ func ExpandSiteConfigWindowsFunctionAppSlot(siteConfig []SiteConfigWindowsFuncti Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String("dotnet-isolated"), }) - windowsSlotSiteConfig.WindowsFxVersion = fmt.Sprintf("DOTNET-ISOLATED|%s", windowsAppStack.DotNetVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("DOTNET-ISOLATED|%s", windowsAppStack.DotNetVersion)) } else { appSettings = append(appSettings, web.NameValuePair{ Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String("dotnet"), }) - windowsSlotSiteConfig.WindowsFxVersion = fmt.Sprintf("DOTNET|%s", windowsAppStack.DotNetVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("DOTNET|%s", windowsAppStack.DotNetVersion)) } } @@ -754,7 +754,7 @@ func ExpandSiteConfigWindowsFunctionAppSlot(siteConfig []SiteConfigWindowsFuncti Name: utils.String("WEBSITE_NODE_DEFAULT_VERSION"), Value: utils.String(windowsAppStack.NodeVersion), }) - windowsSlotSiteConfig.WindowsFxVersion = fmt.Sprintf("Node|%s", windowsAppStack.NodeVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("Node|%s", windowsAppStack.NodeVersion)) } if windowsAppStack.JavaVersion != "" { @@ -762,7 +762,7 @@ func ExpandSiteConfigWindowsFunctionAppSlot(siteConfig []SiteConfigWindowsFuncti Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String("java"), }) - windowsSlotSiteConfig.WindowsFxVersion = fmt.Sprintf("Java|%s", windowsAppStack.JavaVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("Java|%s", windowsAppStack.JavaVersion)) } if windowsAppStack.PowerShellCoreVersion != "" { @@ -770,7 +770,7 @@ func ExpandSiteConfigWindowsFunctionAppSlot(siteConfig []SiteConfigWindowsFuncti Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String("powershell"), }) - windowsSlotSiteConfig.WindowsFxVersion = fmt.Sprintf("PowerShell|%s", windowsAppStack.PowerShellCoreVersion) + expanded.WindowsFxVersion = utils.String(fmt.Sprintf("PowerShell|%s", windowsAppStack.PowerShellCoreVersion)) } if windowsAppStack.CustomHandler { @@ -778,14 +778,14 @@ func ExpandSiteConfigWindowsFunctionAppSlot(siteConfig []SiteConfigWindowsFuncti Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String("custom"), }) - windowsSlotSiteConfig.WindowsFxVersion = "" // Custom needs an explicit empty string here + expanded.WindowsFxVersion = utils.String("") // Custom needs an explicit empty string here } } else { appSettings = append(appSettings, web.NameValuePair{ Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), Value: utils.String(""), }) - windowsSlotSiteConfig.WindowsFxVersion = "" + expanded.WindowsFxVersion = utils.String("") } } @@ -951,6 +951,7 @@ func ExpandSiteConfigLinuxFunctionAppSlot(siteConfig []SiteConfigLinuxFunctionAp if len(siteConfig) == 0 { return nil, nil } + expanded := &web.SiteConfig{} if existing != nil { expanded = existing @@ -1029,92 +1030,91 @@ func ExpandSiteConfigLinuxFunctionAppSlot(siteConfig []SiteConfigLinuxFunctionAp expanded.AppCommandLine = utils.String(linuxSlotSiteConfig.AppCommandLine) } - if metadata.ResourceData.HasChange("site_config.0.application_stack") && len(linuxSlotSiteConfig.ApplicationStack) > 0 { - if len(linuxSlotSiteConfig.ApplicationStack) > 0 { - linuxAppStack := linuxSlotSiteConfig.ApplicationStack[0] - if linuxAppStack.DotNetVersion != "" { - if linuxAppStack.DotNetIsolated { - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("dotnet-isolated"), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("DOTNET-ISOLATED|%s", linuxAppStack.DotNetVersion) - } else { - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("dotnet"), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("DOTNET|%s", linuxAppStack.DotNetVersion) - } - } - - if linuxAppStack.NodeVersion != "" { + if len(linuxSlotSiteConfig.ApplicationStack) > 0 { + linuxAppStack := linuxSlotSiteConfig.ApplicationStack[0] + if linuxAppStack.DotNetVersion != "" { + if linuxAppStack.DotNetIsolated { appSettings = append(appSettings, web.NameValuePair{ Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("node"), + Value: utils.String("dotnet-isolated"), }) - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("WEBSITE_NODE_DEFAULT_VERSION"), - Value: utils.String(linuxAppStack.NodeVersion), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("Node|%s", linuxAppStack.NodeVersion) - } - - if linuxAppStack.PythonVersion != "" { + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("DOTNET-ISOLATED|%s", linuxAppStack.DotNetVersion)) + } else { appSettings = append(appSettings, web.NameValuePair{ Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("python"), + Value: utils.String("dotnet"), }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("Python|%s", linuxAppStack.PythonVersion) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("DOTNET|%s", linuxAppStack.DotNetVersion)) } + } - if linuxAppStack.JavaVersion != "" { - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("java"), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("Java|%s", linuxAppStack.JavaVersion) - } + if linuxAppStack.NodeVersion != "" { + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), + Value: utils.String("node"), + }) + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("WEBSITE_NODE_DEFAULT_VERSION"), + Value: utils.String(linuxAppStack.NodeVersion), + }) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("NODE|%s", linuxAppStack.NodeVersion)) + } - if linuxAppStack.PowerShellCoreVersion != "" { - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("powershell"), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("PowerShell|%s", linuxAppStack.PowerShellCoreVersion) - } + if linuxAppStack.PythonVersion != "" { + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), + Value: utils.String("python"), + }) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("Python|%s", linuxAppStack.PythonVersion)) + } - if linuxAppStack.CustomHandler { - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String("custom"), - }) - linuxSlotSiteConfig.LinuxFxVersion = "" // Custom needs an explicit empty string here - } + if linuxAppStack.JavaVersion != "" { + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), + Value: utils.String("java"), + }) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("Java|%s", linuxAppStack.JavaVersion)) + } - if linuxAppStack.Docker != nil && len(linuxAppStack.Docker) == 1 { - dockerConfig := linuxAppStack.Docker[0] - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("DOCKER_REGISTRY_SERVER_URL"), - Value: utils.String(dockerConfig.RegistryURL), - }) - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("DOCKER_REGISTRY_SERVER_USERNAME"), - Value: utils.String(dockerConfig.RegistryUsername), - }) - appSettings = append(appSettings, web.NameValuePair{ - Name: utils.String("DOCKER_REGISTRY_SERVER_PASSWORD"), - Value: utils.String(dockerConfig.RegistryPassword), - }) - linuxSlotSiteConfig.LinuxFxVersion = fmt.Sprintf("DOCKER|%s/%s:%s", dockerConfig.RegistryURL, dockerConfig.ImageName, dockerConfig.ImageTag) - } - } else { + if linuxAppStack.PowerShellCoreVersion != "" { appSettings = append(appSettings, web.NameValuePair{ Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), - Value: utils.String(""), + Value: utils.String("powershell"), + }) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("PowerShell|%s", linuxAppStack.PowerShellCoreVersion)) + } + + if linuxAppStack.CustomHandler { + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), + Value: utils.String("custom"), }) - linuxSlotSiteConfig.LinuxFxVersion = "" + expanded.LinuxFxVersion = utils.String("") // Custom needs an explicit empty string here } + + if linuxAppStack.Docker != nil && len(linuxAppStack.Docker) == 1 { + dockerConfig := linuxAppStack.Docker[0] + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("DOCKER_REGISTRY_SERVER_URL"), + Value: utils.String(dockerConfig.RegistryURL), + }) + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("DOCKER_REGISTRY_SERVER_USERNAME"), + Value: utils.String(dockerConfig.RegistryUsername), + }) + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("DOCKER_REGISTRY_SERVER_PASSWORD"), + Value: utils.String(dockerConfig.RegistryPassword), + }) + expanded.LinuxFxVersion = utils.String(fmt.Sprintf("DOCKER|%s/%s:%s", dockerConfig.RegistryURL, dockerConfig.ImageName, dockerConfig.ImageTag)) + } + + } else { + appSettings = append(appSettings, web.NameValuePair{ + Name: utils.String("FUNCTIONS_WORKER_RUNTIME"), + Value: utils.String(""), + }) + expanded.LinuxFxVersion = utils.String("") } expanded.AcrUseManagedIdentityCreds = utils.Bool(linuxSlotSiteConfig.UseManagedIdentityACR) diff --git a/internal/services/appservice/helpers/service_plan.go b/internal/services/appservice/helpers/service_plan.go index 1a8efd3ab991..2477ec4c4aa8 100644 --- a/internal/services/appservice/helpers/service_plan.go +++ b/internal/services/appservice/helpers/service_plan.go @@ -26,7 +26,6 @@ var appServicePlanSkus = []string{ var freeSkus = []string{ "F1", - "FREE", } var sharedSkus = []string{ diff --git a/internal/services/appservice/helpers/web_app_schema.go b/internal/services/appservice/helpers/web_app_schema.go index e26bf287988e..a4e51d19dc80 100644 --- a/internal/services/appservice/helpers/web_app_schema.go +++ b/internal/services/appservice/helpers/web_app_schema.go @@ -8,6 +8,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2021-02-01/web" "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" apimValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -867,6 +868,7 @@ type ApplicationStackWindows struct { } // Version information for the below validations was taken in part from - https://github.com/Azure/app-service-linux-docs/tree/master/Runtime_Support +// There is no 3.0 version of .netFramework, setting the property to this value causing app to be broke. func windowsApplicationStackSchema() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeList, @@ -878,12 +880,24 @@ func windowsApplicationStackSchema() *pluginsdk.Schema { "dotnet_version": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - "v3.0", - "v4.0", - "v5.0", - "v6.0", - }, false), + ValidateFunc: func() pluginsdk.SchemaValidateFunc { + if !features.FourPointOh() { + return validation.StringInSlice([]string{ + "v2.0", + "v3.0", + "core3.1", + "v4.0", + "v5.0", + "v6.0"}, false) + } + return validation.StringInSlice([]string{ + "v2.0", + "core3.1", + "v4.0", + "v5.0", + "v6.0", + }, false) + }(), AtLeastOneOf: []string{ "site_config.0.application_stack.0.docker_container_name", "site_config.0.application_stack.0.dotnet_version", @@ -2902,6 +2916,9 @@ func ExpandSiteConfigWindows(siteConfig []SiteConfigWindows, existing *web.SiteC if len(winSiteConfig.ApplicationStack) == 1 { winAppStack := winSiteConfig.ApplicationStack[0] expanded.NetFrameworkVersion = utils.String(winAppStack.NetFrameworkVersion) + if winAppStack.CurrentStack == "dotnetcore" { + expanded.NetFrameworkVersion = nil + } expanded.PhpVersion = utils.String(winAppStack.PhpVersion) expanded.NodeVersion = utils.String(winAppStack.NodeVersion) expanded.PythonVersion = utils.String(winAppStack.PythonVersion) diff --git a/internal/services/appservice/linux_function_app_data_source.go b/internal/services/appservice/linux_function_app_data_source.go index 89bcfbb5b33c..5bc87d017455 100644 --- a/internal/services/appservice/linux_function_app_data_source.go +++ b/internal/services/appservice/linux_function_app_data_source.go @@ -47,6 +47,7 @@ type LinuxFunctionAppDataSourceModel struct { SiteConfig []helpers.SiteConfigLinuxFunctionApp `tfschema:"site_config"` StickySettings []helpers.StickySettings `tfschema:"sticky_settings"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` DefaultHostname string `tfschema:"default_hostname"` @@ -219,6 +220,11 @@ func (d LinuxFunctionAppDataSource) Attributes() map[string]*pluginsdk.Schema { "site_credential": helpers.SiteCredentialSchema(), "sticky_settings": helpers.StickySettingsComputedSchema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, } } @@ -309,6 +315,16 @@ func (d LinuxFunctionAppDataSource) Read() sdk.ResourceFunc { DefaultHostname: utils.NormalizeNilableString(functionApp.DefaultHostName), } + if v := props.OutboundIPAddresses; v != nil { + state.OutboundIPAddresses = *v + state.OutboundIPAddressList = strings.Split(*v, ",") + } + + if v := props.PossibleOutboundIPAddresses; v != nil { + state.PossibleOutboundIPAddresses = *v + state.PossibleOutboundIPAddressList = strings.Split(*v, ",") + } + configResp, err := client.GetConfiguration(ctx, id.ResourceGroup, id.SiteName) if err != nil { return fmt.Errorf("making Read request on AzureRM Function App Configuration %q: %+v", id.SiteName, err) @@ -334,6 +350,7 @@ func (d LinuxFunctionAppDataSource) Read() sdk.ResourceFunc { state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + state.VirtualNetworkSubnetID = utils.NormalizeNilableString(functionApp.VirtualNetworkSubnetID) metadata.SetID(id) diff --git a/internal/services/appservice/linux_function_app_data_source_test.go b/internal/services/appservice/linux_function_app_data_source_test.go index aa29c992a841..6a751da77063 100644 --- a/internal/services/appservice/linux_function_app_data_source_test.go +++ b/internal/services/appservice/linux_function_app_data_source_test.go @@ -2,6 +2,7 @@ package appservice_test import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" @@ -14,11 +15,17 @@ func TestAccLinuxFunctionAppDataSource_standardComplete(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_linux_function_app", "test") d := LinuxFunctionAppDataSource{} + ipListRegex := regexp.MustCompile(`(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(,){0,1})+`) + data.DataSourceTest(t, []acceptance.TestStep{ { Config: d.standardComplete(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("location").HasValue(data.Locations.Primary), + check.That(data.ResourceName).Key("outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("possible_outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("possible_outbound_ip_address_list.#").Exists(), check.That(data.ResourceName).Key("default_hostname").HasValue(fmt.Sprintf("acctest-lfa-%d.azurewebsites.net", data.RandomInteger)), ), }, diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 7296966de840..3ea480a09c61 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -54,6 +55,7 @@ type LinuxFunctionAppModel struct { KeyVaultReferenceIdentityID string `tfschema:"key_vault_reference_identity_id"` SiteConfig []helpers.SiteConfigLinuxFunctionApp `tfschema:"site_config"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` @@ -243,6 +245,12 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -321,22 +329,12 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { return fmt.Errorf("reading %s: %+v", servicePlanId, err) } - sendContentSettings := !functionApp.ForceDisableContentShare - if planSku := servicePlan.Sku; planSku != nil && planSku.Tier != nil { - switch tier := *planSku.Tier; strings.ToLower(tier) { - case "dynamic": // Consumption Plan modifications to request - sendContentSettings = false - case "elastic": // ElasticPremium Plan modifications to request? - case "basic": // App Service Plan modifications to request? - sendContentSettings = false - case "standard": - sendContentSettings = false - case "premiumv2", "premiumv3": - sendContentSettings = false - } - } else { - return fmt.Errorf("determining plan type for Linux %s: %v", id, err) + var planSKU *string + if sku := servicePlan.Sku; sku != nil && sku.Name != nil { + planSKU = sku.Name } + // Only send for ElasticPremium + sendContentSettings := helpers.PlanIsElastic(planSKU) && !functionApp.ForceDisableContentShare existing, err := client.Get(ctx, id.ResourceGroup, id.SiteName) if err != nil && !utils.ResponseWasNotFound(existing.Response) { @@ -413,12 +411,18 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { if functionApp.AppSettings == nil { functionApp.AppSettings = make(map[string]string) } - suffix := uuid.New().String()[0:4] - if _, present := functionApp.AppSettings["WEBSITE_CONTENTSHARE"]; !present { - functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) - } - if _, present := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { - functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + if !functionApp.StorageUsesMSI { + suffix := uuid.New().String()[0:4] + if _, present := functionApp.AppSettings["WEBSITE_CONTENTSHARE"]; !present { + functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) + } + if _, present := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { + functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + } + } else { + if _, present := functionApp.AppSettings["AzureWebJobsStorage__accountName"]; !present { + functionApp.AppSettings["AzureWebJobsStorage__accountName"] = storageString + } } } @@ -450,6 +454,10 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(functionApp.KeyVaultReferenceIdentityID) } + if functionApp.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(functionApp.VirtualNetworkSubnetID) + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.SiteName, siteEnvelope) if err != nil { return fmt.Errorf("creating Linux %s: %+v", id, err) @@ -595,6 +603,16 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { DefaultHostname: utils.NormalizeNilableString(props.DefaultHostName), } + if v := props.OutboundIPAddresses; v != nil { + state.OutboundIPAddresses = *v + state.OutboundIPAddressList = strings.Split(*v, ",") + } + + if v := props.PossibleOutboundIPAddresses; v != nil { + state.PossibleOutboundIPAddresses = *v + state.PossibleOutboundIPAddressList = strings.Split(*v, ",") + } + configResp, err := client.GetConfiguration(ctx, id.ResourceGroup, id.SiteName) if err != nil { return fmt.Errorf("making Read request on AzureRM Function App Configuration %q: %+v", id.SiteName, err) @@ -621,6 +639,10 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + if err := metadata.Encode(&state); err != nil { return fmt.Errorf("encoding: %+v", err) } @@ -687,7 +709,7 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { } // Only send for ElasticPremium - sendContentSettings := helpers.PlanIsElastic(planSKU) + sendContentSettings := helpers.PlanIsElastic(planSKU) && !state.ForceDisableContentShare // Some service plan updates are allowed - see customiseDiff for exceptions if metadata.ResourceData.HasChange("service_plan_id") { @@ -702,6 +724,19 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { existing.SiteProperties.HTTPSOnly = utils.Bool(state.HttpsOnly) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetwork(ctx, id.ResourceGroup, id.SiteName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + if metadata.ResourceData.HasChange("client_certificate_enabled") { existing.SiteProperties.ClientCertEnabled = utils.Bool(state.ClientCertEnabled) } diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index c99013c45f94..312948a4ad52 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -42,6 +42,23 @@ func TestAccLinuxFunctionApp_basicBasicPlan(t *testing.T) { }) } +func TestAccLinuxFunctionApp_basicRuntimeCheck(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.runtimeScaleCheck(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.runtime_scale_monitoring_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) +} + func TestAccLinuxFunctionApp_basicConsumptionPlan(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -885,6 +902,7 @@ func TestAccLinuxFunctionApp_appStackNodeUpdate(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|12"), ), }, data.ImportStep(), @@ -893,6 +911,7 @@ func TestAccLinuxFunctionApp_appStackNodeUpdate(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|16"), ), }, data.ImportStep(), @@ -901,6 +920,16 @@ func TestAccLinuxFunctionApp_appStackNodeUpdate(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|14"), + ), + }, + data.ImportStep(), + { + Config: r.appStackNodeUpdateTags(data, SkuBasicPlan, "14"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|14"), ), }, data.ImportStep(), @@ -1096,7 +1125,22 @@ func TestAccLinuxFunctionApp_updateStorageAccount(t *testing.T) { }) } -func TestAccLinuxFunctionApp_msiStorageAccount(t *testing.T) { +func TestAccLinuxFunctionApp_msiStorageAccountElastic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + ), + }, + data.ImportStep(), + }) +} +func TestAccLinuxFunctionApp_msiStorageAccountStandard(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1168,6 +1212,21 @@ func TestAccLinuxFunctionApp_storageAccountKeyVaultSecretVersionless(t *testing. }) } +func TestAccLinuxFunctionAppASEv3_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // CustomDiff tests func TestAccLinuxFunctionApp_consumptionPlanBackupShouldError(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") @@ -1193,6 +1252,90 @@ func TestAccLinuxFunctionApp_basicPlanBackupShouldError(t *testing.T) { }) } +func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionApp_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +// Outputs + +func TestAccLinuxFunctionApp_basicOutputs(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + ipListRegex := regexp.MustCompile(`(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(,){0,1})+`) + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("possible_outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("possible_outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("default_hostname").MatchesRegex(regexp.MustCompile(`(.)+`)), + ), + }, + data.ImportStep(), + }) +} + // Configs func (r LinuxFunctionAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -1236,6 +1379,31 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger) } +func (r LinuxFunctionAppResource) runtimeScaleCheck(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + runtime_scale_monitoring_enabled = true + pre_warmed_instance_count = 1 + } +} +`, r.template(data, planSku), data.RandomInteger) +} + func (r LinuxFunctionAppResource) healthCheckPath(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -1941,6 +2109,36 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, nodeVersion) } +func (r LinuxFunctionAppResource) appStackNodeUpdateTags(data acceptance.TestData, planSku string, nodeVersion string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + application_stack { + node_version = "%s" + } + } + + tags = { + foo = "bar" + } +} +`, r.template(data, planSku), data.RandomInteger, nodeVersion) +} + func (r LinuxFunctionAppResource) appStackDocker(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -2800,7 +2998,11 @@ resource "azurerm_linux_function_app" "test" { type = "SystemAssigned" } - site_config {} + site_config { + application_stack { + python_version = "3.9" + } + } } `, r.template(data, planSku), data.RandomInteger) } @@ -3136,3 +3338,222 @@ resource "azurerm_user_assigned_identity" "test" { } `, r.template(data, planSku), data.RandomInteger) } + +func (r LinuxFunctionAppResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config {} + +} + + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config {} + +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config {} + +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppResource) withASEV3(data acceptance.TestData) string { + return fmt.Sprintf(` +%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_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +`, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) +} diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 6924a4338781..4920f56de734 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -47,6 +48,7 @@ type LinuxFunctionAppSlotModel struct { KeyVaultReferenceIdentityID string `tfschema:"key_vault_reference_identity_id"` SiteConfig []helpers.SiteConfigLinuxFunctionAppSlot `tfschema:"site_config"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` DefaultHostname string `tfschema:"default_hostname"` Kind string `tfschema:"kind"` @@ -224,6 +226,12 @@ func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { "site_config": helpers.SiteConfigSchemaLinuxFunctionAppSlot(), "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -317,22 +325,12 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { return fmt.Errorf("reading %s: %+v", servicePlanId, err) } - sendContentSettings := !functionAppSlot.ForceDisableContentShare - if planSku := servicePlan.Sku; planSku != nil && planSku.Tier != nil { - switch tier := *planSku.Tier; strings.ToLower(tier) { - case "dynamic": // Consumption Plan modifications to request - sendContentSettings = false - case "elastic": // ElasticPremium Plan modifications to request? - case "basic": // App Service Plan modifications to request? - sendContentSettings = false - case "standard": - sendContentSettings = false - case "premiumv2", "premiumv3": - sendContentSettings = false - } - } else { - return fmt.Errorf("determining plan type for Linux %s: %v", id, err) + var planSKU *string + if sku := servicePlan.Sku; sku != nil && sku.Name != nil { + planSKU = sku.Name } + // Only send for ElasticPremium + sendContentSettings := helpers.PlanIsElastic(planSKU) && !functionAppSlot.ForceDisableContentShare existing, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil && !utils.ResponseWasNotFound(existing.Response) { @@ -408,12 +406,18 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { if functionAppSlot.AppSettings == nil { functionAppSlot.AppSettings = make(map[string]string) } - suffix := uuid.New().String()[0:4] - if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"]; !present { - functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionAppSlot.Name), suffix) - } - if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { - functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + if !functionAppSlot.StorageUsesMSI { + suffix := uuid.New().String()[0:4] + if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"]; !present { + functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionAppSlot.Name), suffix) + } + if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { + functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + } + } else { + if _, present := functionAppSlot.AppSettings["AzureWebJobsStorage__accountName"]; !present { + functionAppSlot.AppSettings["AzureWebJobsStorage__accountName"] = storageString + } } } @@ -445,6 +449,10 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(functionAppSlot.KeyVaultReferenceIdentityID) } + if functionAppSlot.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(functionAppSlot.VirtualNetworkSubnetID) + } + future, err := client.CreateOrUpdateSlot(ctx, id.ResourceGroup, id.SiteName, siteEnvelope, id.SlotName) if err != nil { return fmt.Errorf("creating Linux %s: %+v", id, err) @@ -597,6 +605,10 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + if err := metadata.Encode(&state); err != nil { return fmt.Errorf("encoding: %+v", err) } @@ -662,7 +674,7 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { return err } - sendContentSettings := !helpers.PlanIsElastic(planSKU) + sendContentSettings := helpers.PlanIsElastic(planSKU) && !state.ForceDisableContentShare if metadata.ResourceData.HasChange("enabled") { existing.SiteProperties.Enabled = utils.Bool(state.Enabled) @@ -696,6 +708,19 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetworkSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { diff --git a/internal/services/appservice/linux_function_app_slot_resource_test.go b/internal/services/appservice/linux_function_app_slot_resource_test.go index 3c0d36d44b8f..4b2e56b6b118 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -715,6 +715,7 @@ func TestAccLinuxFunctionAppSlot_appStackNodeUpdate(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|12"), ), }, data.ImportStep(), @@ -723,6 +724,25 @@ func TestAccLinuxFunctionAppSlot_appStackNodeUpdate(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|14"), + ), + }, + data.ImportStep(), + { + Config: r.appStackNode(data, SkuStandardPlan, "16"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|16"), + ), + }, + data.ImportStep(), + { + Config: r.appStackNodeUpdateTags(data, SkuStandardPlan, "16"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + check.That(data.ResourceName).Key("site_config.0.linux_fx_version").HasValue("NODE|16"), ), }, data.ImportStep(), @@ -893,7 +913,23 @@ func TestAccLinuxFunctionAppSlot_identityKeyVaultIdentity(t *testing.T) { }) } -func TestAccLinuxFunctionAppSlot_msiStorageAccount(t *testing.T) { +func TestAccLinuxFunctionAppSlot_msiStorageAccountElastic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp,linux"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionAppSlot_msiStorageAccountStandard(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -941,6 +977,81 @@ func TestAccLinuxFunctionAppSlot_storageAccountKeyVaultSecretVersionless(t *test }) } +func TestAccLinuxFunctionAppSlot_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionAppSlotASEv3_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // Configs func (r LinuxFunctionAppSlotResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -1423,6 +1534,33 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, nodeVersion) } +func (r LinuxFunctionAppSlotResource) appStackNodeUpdateTags(data acceptance.TestData, planSku string, nodeVersion string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + application_stack { + node_version = "%s" + } + } + + tags = { + foo = "bar" + } +} +`, r.template(data, planSku), data.RandomInteger, nodeVersion) +} + func (r LinuxFunctionAppSlotResource) appStackDocker(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -2214,7 +2352,11 @@ resource "azurerm_linux_function_app_slot" "test" { type = "SystemAssigned" } - site_config {} + site_config { + application_stack { + python_version = "3.9" + } + } } `, r.template(data, planSku), data.RandomInteger) } @@ -2521,3 +2663,220 @@ resource "azurerm_user_assigned_identity" "test" { } `, r.template(data, planSku), data.RandomInteger) } + +func (r LinuxFunctionAppSlotResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config {} +} + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + virtual_network_subnet_id = azurerm_subnet.test1.id + + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + virtual_network_subnet_id = azurerm_subnet.test2.id + + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%[3]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%[3]d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +`, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) +} diff --git a/internal/services/appservice/linux_web_app_resource.go b/internal/services/appservice/linux_web_app_resource.go index f6f1917396d8..99a4c7d911b8 100644 --- a/internal/services/appservice/linux_web_app_resource.go +++ b/internal/services/appservice/linux_web_app_resource.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -130,9 +131,9 @@ func (r LinuxWebAppResource) Arguments() map[string]*pluginsdk.Schema { }, "virtual_network_subnet_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, }, "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), @@ -501,11 +502,14 @@ func (r LinuxWebAppResource) Read() sdk.ResourceFunc { KeyVaultReferenceIdentityID: utils.NormalizeNilableString(props.KeyVaultReferenceIdentity), Enabled: utils.NormaliseNilableBool(props.Enabled), HttpsOnly: utils.NormaliseNilableBool(props.HTTPSOnly), - VirtualNetworkSubnetID: utils.NormalizeNilableString(webApp.VirtualNetworkSubnetID), StickySettings: helpers.FlattenStickySettings(stickySettings.SlotConfigNames), Tags: tags.ToTypedObject(webApp.Tags), } + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + var healthCheckCount *int state.AppSettings, healthCheckCount = helpers.FlattenAppSettings(appSettings) @@ -665,6 +669,19 @@ func (r LinuxWebAppResource) Update() sdk.ResourceFunc { existing.SiteConfig = siteConfig } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetwork(ctx, id.ResourceGroup, id.SiteName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + updateFuture, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.SiteName, existing) if err != nil { return fmt.Errorf("updating Linux %s: %+v", id, err) diff --git a/internal/services/appservice/linux_web_app_resource_test.go b/internal/services/appservice/linux_web_app_resource_test.go index a276dfd801a2..64e7d94c5e79 100644 --- a/internal/services/appservice/linux_web_app_resource_test.go +++ b/internal/services/appservice/linux_web_app_resource_test.go @@ -1122,9 +1122,12 @@ func TestAccLinuxWebApp_vNetIntegration(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegrationWebApp_withSubnetId(data), + Config: r.vNetIntegrationWebApp_subnet1(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), ), }, data.ImportStep(), @@ -1144,9 +1147,22 @@ func TestAccLinuxWebApp_vNetIntegrationUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.vNetIntegrationWebApp_withSubnetId(data), + Config: r.vNetIntegrationWebApp_subnet1(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet2(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), ), }, data.ImportStep(), @@ -2913,8 +2929,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +resource "azurerm_subnet" "test1" { + name = "subnet1" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] @@ -2929,6 +2945,22 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_web_app" "test" { name = "acctestWA-%d" location = azurerm_resource_group.test.location @@ -2940,7 +2972,66 @@ resource "azurerm_linux_web_app" "test" { `, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) } -func (r LinuxWebAppResource) vNetIntegrationWebApp_withSubnetId(data acceptance.TestData) string { +func (r LinuxWebAppResource) vNetIntegrationWebApp_subnet1(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_web_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxWebAppResource) vNetIntegrationWebApp_subnet2(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2955,8 +3046,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +resource "azurerm_subnet" "test1" { + name = "subnet1" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] @@ -2971,12 +3062,28 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_web_app" "test" { name = "acctestWA-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name service_plan_id = azurerm_service_plan.test.id - virtual_network_subnet_id = azurerm_subnet.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id site_config {} } diff --git a/internal/services/appservice/linux_web_app_slot_resource.go b/internal/services/appservice/linux_web_app_slot_resource.go index ef686bce20a2..f0d164243ef4 100644 --- a/internal/services/appservice/linux_web_app_slot_resource.go +++ b/internal/services/appservice/linux_web_app_slot_resource.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -132,9 +133,9 @@ func (r LinuxWebAppSlotResource) Arguments() map[string]*pluginsdk.Schema { }, "virtual_network_subnet_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, }, "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), @@ -451,7 +452,10 @@ func (r LinuxWebAppSlotResource) Read() sdk.ResourceFunc { Enabled: utils.NormaliseNilableBool(props.Enabled), HttpsOnly: utils.NormaliseNilableBool(props.HTTPSOnly), Tags: tags.ToTypedObject(webApp.Tags), - VirtualNetworkSubnetID: utils.NormalizeNilableString(webApp.VirtualNetworkSubnetID), + } + + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId } var healthCheckCount *int @@ -588,6 +592,19 @@ func (r LinuxWebAppSlotResource) Update() sdk.ResourceFunc { existing.SiteConfig = siteConfig } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetworkSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + updateFuture, err := client.CreateOrUpdateSlot(ctx, id.ResourceGroup, id.SiteName, existing, id.SlotName) if err != nil { return fmt.Errorf("updating Linux %s: %+v", id, err) diff --git a/internal/services/appservice/linux_web_app_slot_resource_test.go b/internal/services/appservice/linux_web_app_slot_resource_test.go index 6d46b77a3da0..ae43c913cc13 100644 --- a/internal/services/appservice/linux_web_app_slot_resource_test.go +++ b/internal/services/appservice/linux_web_app_slot_resource_test.go @@ -898,10 +898,12 @@ func TestAccLinuxWebAppSlot_vNetIntegration(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegrationWebApp_withSubnetId(data), + Config: r.vNetIntegrationWebApp_subnet1(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("virtual_network_subnet_id").Exists(), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), ), }, data.ImportStep(), @@ -921,9 +923,22 @@ func TestAccLinuxWebAppSlot_vNetIntegrationUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.vNetIntegrationWebApp_withSubnetId(data), + Config: r.vNetIntegrationWebApp_subnet1(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet2(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), ), }, data.ImportStep(), @@ -2186,14 +2201,87 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +resource "azurerm_subnet" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_web_app_slot" "test" { + name = "acctestWAS-%[2]d" + app_service_id = azurerm_linux_web_app.test.id + + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxWebAppSlotResource) vNetIntegrationWebApp_subnet1(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] delegation { name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -2204,14 +2292,14 @@ resource "azurerm_subnet" "test" { resource "azurerm_linux_web_app_slot" "test" { name = "acctestWAS-%[2]d" app_service_id = azurerm_linux_web_app.test.id - virtual_network_subnet_id = azurerm_subnet.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id site_config {} } `, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) } -func (r LinuxWebAppSlotResource) vNetIntegrationWebApp_withSubnetId(data acceptance.TestData) string { +func (r LinuxWebAppSlotResource) vNetIntegrationWebApp_subnet2(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2226,8 +2314,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +resource "azurerm_subnet" "test1" { + name = "subnet1" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] @@ -2242,10 +2330,26 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_web_app_slot" "test" { name = "acctestWAS-%[2]d" app_service_id = azurerm_linux_web_app.test.id - virtual_network_subnet_id = azurerm_subnet.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id site_config {} } diff --git a/internal/services/appservice/service_plan_resource_test.go b/internal/services/appservice/service_plan_resource_test.go index 76b5bff434b0..f8e828986a77 100644 --- a/internal/services/appservice/service_plan_resource_test.go +++ b/internal/services/appservice/service_plan_resource_test.go @@ -441,3 +441,64 @@ resource "azurerm_service_plan" "test" { } `, data.RandomInteger, data.Locations.Primary) } + +func (r ServicePlanResource) aseV3Linux(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-appserviceplan-%[1]d" + location = "%[2]s" +} + +resource "azurerm_resource_group" "test2" { + name = "acctestRG2-ase-%[1]d" + location = "%[2]s" +} + + +resource "azurerm_virtual_network" "test" { + name = "acctest-vnet-%[1]d" + 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 = "acctest-subnet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "asedelegation" + service_delegation { + name = "Microsoft.Web/hostingEnvironments" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_app_service_environment_v3" "test" { + name = "acctest-ase-%[1]d" + resource_group_name = azurerm_resource_group.test.name + subnet_id = azurerm_subnet.test.id +} + +resource "azurerm_service_plan" "test" { + name = "acctest-SP-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + os_type = "Linux" + sku_name = "I1v2" + + app_service_environment_id = azurerm_app_service_environment_v3.test.id + + tags = { + environment = "AccTest" + Foo = "bar" + } +} +`, data.RandomInteger, data.Locations.Primary) +} diff --git a/internal/services/appservice/validate/web_app_name_test.go b/internal/services/appservice/validate/web_app_name_test.go new file mode 100644 index 000000000000..1273d241c86b --- /dev/null +++ b/internal/services/appservice/validate/web_app_name_test.go @@ -0,0 +1,50 @@ +package validate_test + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" +) + +func TestWebAppName(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + Input: "", + Valid: false, + }, + { + Input: "a", + Valid: true, + }, + { + Input: "-valid", + Valid: true, + }, + { + // len is 35 + Input: "ThisIsALongAndValidNameThatWillWork", + Valid: true, + }, + { + Input: "ThisNameIsTooLongThisNameIsTooLongThisNameIsTooLongThisNameIsTooLongThisNameIsTooLongThisNameIsTooLongThisNameIsTooLongThisNameIsTooLong", + Valid: false, + }, + { + // len is 60 and should show the warning message + Input: "012345678901234567890123456789012345678901234567890123456789", + Valid: true, + }, + } + + for _, tc := range cases { + _, errs := validate.WebAppName(tc.Input, "test") + valid := len(errs) == 0 + + if valid != tc.Valid { + t.Fatalf("expected %s to be %t, got %t", tc.Input, tc.Valid, valid) + } + } +} diff --git a/internal/services/appservice/windows_function_app_data_source.go b/internal/services/appservice/windows_function_app_data_source.go index b82be7e16eef..88eaf7125e64 100644 --- a/internal/services/appservice/windows_function_app_data_source.go +++ b/internal/services/appservice/windows_function_app_data_source.go @@ -47,6 +47,7 @@ type WindowsFunctionAppDataSourceModel struct { SiteConfig []helpers.SiteConfigWindowsFunctionApp `tfschema:"site_config"` StickySettings []helpers.StickySettings `tfschema:"sticky_settings"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetId string `tfschema:"virtual_network_subnet_id"` CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` DefaultHostname string `tfschema:"default_hostname"` @@ -216,6 +217,11 @@ func (d WindowsFunctionAppDataSource) Attributes() map[string]*pluginsdk.Schema "identity": commonschema.SystemAssignedUserAssignedIdentityComputed(), "tags": tags.SchemaDataSource(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, } } @@ -257,6 +263,17 @@ func (d WindowsFunctionAppDataSource) Read() sdk.ResourceFunc { functionApp.Kind = utils.NormalizeNilableString(existing.Kind) functionApp.CustomDomainVerificationId = utils.NormalizeNilableString(props.CustomDomainVerificationID) functionApp.DefaultHostname = utils.NormalizeNilableString(props.DefaultHostName) + functionApp.VirtualNetworkSubnetId = utils.NormalizeNilableString(props.VirtualNetworkSubnetID) + + if v := props.OutboundIPAddresses; v != nil { + functionApp.OutboundIPAddresses = *v + functionApp.OutboundIPAddressList = strings.Split(*v, ",") + } + + if v := props.PossibleOutboundIPAddresses; v != nil { + functionApp.PossibleOutboundIPAddresses = *v + functionApp.PossibleOutboundIPAddressList = strings.Split(*v, ",") + } appSettingsResp, err := client.ListApplicationSettings(ctx, id.ResourceGroup, id.SiteName) if err != nil { diff --git a/internal/services/appservice/windows_function_app_data_source_test.go b/internal/services/appservice/windows_function_app_data_source_test.go index b8ab54af0115..c14b492143a8 100644 --- a/internal/services/appservice/windows_function_app_data_source_test.go +++ b/internal/services/appservice/windows_function_app_data_source_test.go @@ -2,6 +2,7 @@ package appservice_test import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" @@ -14,11 +15,17 @@ func TestAccWindowsFunctionAppDataSource_complete(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_windows_function_app", "test") d := WindowsFunctionAppDataSource{} + ipListRegex := regexp.MustCompile(`(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(,){0,1})+`) + data.DataSourceTest(t, []acceptance.TestStep{ { Config: d.complete(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("location").HasValue(data.Locations.Primary), + check.That(data.ResourceName).Key("outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("possible_outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("possible_outbound_ip_address_list.#").Exists(), check.That(data.ResourceName).Key("default_hostname").HasValue(fmt.Sprintf("acctest-wfa-%d.azurewebsites.net", data.RandomInteger)), ), }, diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index cfd3422eca33..246ce171bde1 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -54,6 +55,7 @@ type WindowsFunctionAppModel struct { KeyVaultReferenceIdentityID string `tfschema:"key_vault_reference_identity_id"` SiteConfig []helpers.SiteConfigWindowsFunctionApp `tfschema:"site_config"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` @@ -243,6 +245,12 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -321,21 +329,12 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { return fmt.Errorf("reading %s: %+v", servicePlanId, err) } - sendContentSettings := !functionApp.ForceDisableContentShare - if planSku := servicePlan.Sku; planSku != nil && planSku.Tier != nil { - switch tier := *planSku.Tier; strings.ToLower(tier) { - case "dynamic": - case "elastic": - case "basic": - sendContentSettings = false - case "standard": - sendContentSettings = false - case "premiumv2", "premiumv3": - sendContentSettings = false - } - } else { - return fmt.Errorf("determining plan type for Windows %s: %v", id, err) + var planSKU *string + if sku := servicePlan.Sku; sku != nil && sku.Name != nil { + planSKU = sku.Name } + // Only send for Dynamic and ElasticPremium + sendContentSettings := (helpers.PlanIsConsumption(planSKU) || helpers.PlanIsElastic(planSKU)) && !functionApp.ForceDisableContentShare existing, err := client.Get(ctx, id.ResourceGroup, id.SiteName) if err != nil && !utils.ResponseWasNotFound(existing.Response) { @@ -411,12 +410,18 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { if functionApp.AppSettings == nil { functionApp.AppSettings = make(map[string]string) } - suffix := uuid.New().String()[0:4] - if _, present := functionApp.AppSettings["WEBSITE_CONTENTSHARE"]; !present { - functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) - } - if _, present := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { - functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + if !functionApp.StorageUsesMSI { + suffix := uuid.New().String()[0:4] + if _, present := functionApp.AppSettings["WEBSITE_CONTENTSHARE"]; !present { + functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) + } + if _, present := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { + functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + } + } else { + if _, present := functionApp.AppSettings["AzureWebJobsStorage__accountName"]; !present { + functionApp.AppSettings["AzureWebJobsStorage__accountName"] = storageString + } } } @@ -444,6 +449,10 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { }, } + if functionApp.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(functionApp.VirtualNetworkSubnetID) + } + if functionApp.KeyVaultReferenceIdentityID != "" { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(functionApp.KeyVaultReferenceIdentityID) } @@ -593,6 +602,16 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { DefaultHostname: utils.NormalizeNilableString(props.DefaultHostName), } + if v := props.OutboundIPAddresses; v != nil { + state.OutboundIPAddresses = *v + state.OutboundIPAddressList = strings.Split(*v, ",") + } + + if v := props.PossibleOutboundIPAddresses; v != nil { + state.PossibleOutboundIPAddresses = *v + state.PossibleOutboundIPAddressList = strings.Split(*v, ",") + } + configResp, err := client.GetConfiguration(ctx, id.ResourceGroup, id.SiteName) if err != nil { return fmt.Errorf("making Read request on AzureRM Function App Configuration %q: %+v", id.SiteName, err) @@ -619,6 +638,10 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + if subnetId := utils.NormalizeNilableString(functionApp.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + if err := metadata.Encode(&state); err != nil { return fmt.Errorf("encoding: %+v", err) } @@ -683,7 +706,9 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { if err != nil { return err } - sendContentSettings := !helpers.PlanIsAppPlan(planSKU) + + // Only send for Dynamic and ElasticPremium + sendContentSettings := (helpers.PlanIsConsumption(planSKU) || helpers.PlanIsElastic(planSKU)) && !state.ForceDisableContentShare // Some service plan updates are allowed - see customiseDiff for exceptions if metadata.ResourceData.HasChange("service_plan_id") { @@ -722,6 +747,19 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetwork(ctx, id.ResourceGroup, id.SiteName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index 1bc62718b313..25729c5ea3ff 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -3,6 +3,7 @@ package appservice_test import ( "context" "fmt" + "regexp" "strings" "testing" @@ -33,6 +34,23 @@ func TestAccWindowsFunctionApp_basicBasicPlan(t *testing.T) { }) } +func TestAccWindowsFunctionApp_basicRuntimeCheck(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.runtimeScaleCheck(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + check.That(data.ResourceName).Key("site_config.0.runtime_scale_monitoring_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) +} + func TestAccWindowsFunctionApp_basicConsumptionPlan(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} @@ -878,6 +896,32 @@ func TestAccWindowsFunctionApp_appStackNodeUpdate(t *testing.T) { }) } +func TestAccWindowsFunctionApp_appStackUpdateTags(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.appStackNode(data, SkuConsumptionPlan, "~14"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + check.That(data.ResourceName).Key("site_config.0.windows_fx_version").HasValue("Node|~14"), + ), + }, + data.ImportStep(), + { + Config: r.appStackNodeWithTags(data, SkuConsumptionPlan, "~14"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + check.That(data.ResourceName).Key("site_config.0.windows_fx_version").HasValue("Node|~14"), + ), + }, + data.ImportStep(), + }) +} + func TestAccWindowsFunctionApp_appStackJava(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} @@ -984,7 +1028,39 @@ func TestAccWindowsFunctionApp_updateStorageAccount(t *testing.T) { }) } -func TestAccWindowsFunctionApp_msiStorageAccount(t *testing.T) { +func TestAccWindowsFunctionApp_msiStorageAccountConsumption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuConsumptionPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionApp_msiStorageAccountElastic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionApp_msiStorageAccountStandard(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} @@ -1056,6 +1132,105 @@ func TestAccWindowsFunctionApp_storageAccountKeyVaultSecretVersionless(t *testin }) } +func TestAccWindowsFunctionApp_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionApp_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppASEv3_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +// Outputs + +func TestAccWindowsFunctionApp_basicOutputs(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + ipListRegex := regexp.MustCompile(`(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(,){0,1})+`) + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("possible_outbound_ip_addresses").MatchesRegex(ipListRegex), + check.That(data.ResourceName).Key("possible_outbound_ip_address_list.#").Exists(), + check.That(data.ResourceName).Key("default_hostname").MatchesRegex(regexp.MustCompile(`(.)+`)), + ), + }, + data.ImportStep(), + }) +} + // Exists func (r WindowsFunctionAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -1101,6 +1276,31 @@ resource "azurerm_windows_function_app" "test" { `, r.template(data, planSku), data.RandomInteger) } +func (r WindowsFunctionAppResource) runtimeScaleCheck(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + runtime_scale_monitoring_enabled = true + pre_warmed_instance_count = 1 + } +} +`, r.template(data, planSku), data.RandomInteger) +} + func (r WindowsFunctionAppResource) appSettings(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -2265,6 +2465,36 @@ resource "azurerm_windows_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, nodeVersion) } +func (r WindowsFunctionAppResource) appStackNodeWithTags(data acceptance.TestData, planSku string, nodeVersion string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + application_stack { + node_version = "%s" + } + } + + tags = { + env = "TestAcc" + } +} +`, r.template(data, planSku), data.RandomInteger, nodeVersion) +} + func (r WindowsFunctionAppResource) appStackJava(data acceptance.TestData, planSku string, javaVersion string) string { return fmt.Sprintf(` provider "azurerm" { @@ -2350,7 +2580,7 @@ provider "azurerm" { %s resource "azurerm_windows_function_app" "test" { - name = "acctest-LFA-%d" + name = "acctest-WFA-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name service_plan_id = azurerm_service_plan.test.id @@ -2506,7 +2736,11 @@ resource "azurerm_windows_function_app" "test" { type = "SystemAssigned" } - site_config {} + site_config { + application_stack { + dotnet_version = "6" + } + } } `, r.template(data, planSku), data.RandomInteger) } @@ -2717,7 +2951,7 @@ func (WindowsFunctionAppResource) template(data acceptance.TestData, planSku str } return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-LFA-%d" + name = "acctestRG-WFA-%d" location = "%s" } @@ -2846,3 +3080,197 @@ resource "azurerm_service_plan" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString, planSku) } + +func (r WindowsFunctionAppResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + site_config {} +} + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + site_config {} +} + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + site_config {} +} + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppResource) withASEV3(data acceptance.TestData) string { + return fmt.Sprintf(` +%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_windows_function_app" "test" { + name = "acctest-WFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +`, ServicePlanResource{}.aseV3(data), data.RandomString, data.RandomInteger) +} diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index 675eb013e537..3a47b06699e0 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -55,6 +56,7 @@ type WindowsFunctionAppSlotModel struct { PossibleOutboundIPAddresses string `tfschema:"possible_outbound_ip_addresses"` PossibleOutboundIPAddressList []string `tfschema:"possible_outbound_ip_address_list"` SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` } var _ sdk.ResourceWithUpdate = WindowsFunctionAppSlotResource{} @@ -224,6 +226,12 @@ func (r WindowsFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema "site_config": helpers.SiteConfigSchemaWindowsFunctionAppSlot(), "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -324,21 +332,14 @@ func (r WindowsFunctionAppSlotResource) Create() sdk.ResourceFunc { return fmt.Errorf("reading %s: %+v", servicePlanId, err) } - sendContentSettings := !functionAppSlot.ForceDisableContentShare - if planSku := servicePlan.Sku; planSku != nil && planSku.Tier != nil { - switch tier := *planSku.Tier; strings.ToLower(tier) { - case "dynamic": - case "elastic": - case "basic": - sendContentSettings = false - case "standard": - sendContentSettings = false - case "premiumv2", "premiumv3": - sendContentSettings = false - } + var planSKU *string + if sku := servicePlan.Sku; sku != nil && sku.Name != nil { + planSKU = sku.Name } else { - return fmt.Errorf("determining plan type for Windows %s: %v", id, err) + return fmt.Errorf("could not determine Service Plan SKU type") } + // Only send for Dynamic and ElasticPremium + sendContentSettings := (helpers.PlanIsConsumption(planSKU) || helpers.PlanIsElastic(planSKU)) && !functionAppSlot.ForceDisableContentShare existing, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil && !utils.ResponseWasNotFound(existing.Response) { @@ -414,12 +415,18 @@ func (r WindowsFunctionAppSlotResource) Create() sdk.ResourceFunc { if functionAppSlot.AppSettings == nil { functionAppSlot.AppSettings = make(map[string]string) } - suffix := uuid.New().String()[0:4] - if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"]; !present { - functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionAppSlot.Name), suffix) - } - if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { - functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + if !functionAppSlot.StorageUsesMSI { + suffix := uuid.New().String()[0:4] + if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"]; !present { + functionAppSlot.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionAppSlot.Name), suffix) + } + if _, present := functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !present { + functionAppSlot.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString + } + } else { + if _, present := functionAppSlot.AppSettings["AzureWebJobsStorage__accountName"]; !present { + functionAppSlot.AppSettings["AzureWebJobsStorage__accountName"] = storageString + } } } @@ -447,6 +454,10 @@ func (r WindowsFunctionAppSlotResource) Create() sdk.ResourceFunc { }, } + if functionAppSlot.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(functionAppSlot.VirtualNetworkSubnetID) + } + if functionAppSlot.KeyVaultReferenceIdentityID != "" { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(functionAppSlot.KeyVaultReferenceIdentityID) } @@ -511,18 +522,18 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { if err != nil { return err } - functionAppSlot, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) + functionApp, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { - if utils.ResponseWasNotFound(functionAppSlot.Response) { + if utils.ResponseWasNotFound(functionApp.Response) { return metadata.MarkAsGone(id) } return fmt.Errorf("reading Windows %s: %+v", id, err) } - if functionAppSlot.SiteProperties == nil { + if functionApp.SiteProperties == nil { return fmt.Errorf("reading properties of Windows %s", id) } - props := *functionAppSlot.SiteProperties + props := *functionApp.SiteProperties appSettingsResp, err := client.ListApplicationSettingsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { @@ -567,11 +578,11 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { state := WindowsFunctionAppSlotModel{ Name: id.SlotName, FunctionAppID: parse.NewFunctionAppID(id.SubscriptionId, id.ResourceGroup, id.SiteName).ID(), - Enabled: utils.NormaliseNilableBool(functionAppSlot.Enabled), - ClientCertMode: string(functionAppSlot.ClientCertMode), + Enabled: utils.NormaliseNilableBool(functionApp.Enabled), + ClientCertMode: string(functionApp.ClientCertMode), DailyMemoryTimeQuota: int(utils.NormaliseNilableInt32(props.DailyMemoryTimeQuota)), - Tags: tags.ToTypedObject(functionAppSlot.Tags), - Kind: utils.NormalizeNilableString(functionAppSlot.Kind), + Tags: tags.ToTypedObject(functionApp.Tags), + Kind: utils.NormalizeNilableString(functionApp.Kind), KeyVaultReferenceIdentityID: utils.NormalizeNilableString(props.KeyVaultReferenceIdentity), CustomDomainVerificationId: utils.NormalizeNilableString(props.CustomDomainVerificationID), DefaultHostname: utils.NormalizeNilableString(props.DefaultHostName), @@ -600,14 +611,18 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { state.SiteConfig[0].AppServiceLogs = helpers.FlattenFunctionAppAppServiceLogs(logs) - state.HttpsOnly = utils.NormaliseNilableBool(functionAppSlot.HTTPSOnly) - state.ClientCertEnabled = utils.NormaliseNilableBool(functionAppSlot.ClientCertEnabled) + state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) + state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } if err := metadata.Encode(&state); err != nil { return fmt.Errorf("encoding: %+v", err) } - flattenedIdentity, err := flattenIdentity(functionAppSlot.Identity) + flattenedIdentity, err := flattenIdentity(functionApp.Identity) if err != nil { return fmt.Errorf("flattening `identity`: %+v", err) } @@ -667,7 +682,9 @@ func (r WindowsFunctionAppSlotResource) Update() sdk.ResourceFunc { if err != nil { return err } - sendContentSettings := !helpers.PlanIsAppPlan(planSKU) + + // Only send for Dynamic and ElasticPremium + sendContentSettings := (helpers.PlanIsConsumption(planSKU) || helpers.PlanIsElastic(planSKU)) && !state.ForceDisableContentShare // Some service plan updates are allowed - see customiseDiff for exceptions if metadata.ResourceData.HasChange("enabled") { @@ -702,6 +719,19 @@ func (r WindowsFunctionAppSlotResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetworkSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { diff --git a/internal/services/appservice/windows_function_app_slot_resource_test.go b/internal/services/appservice/windows_function_app_slot_resource_test.go index c3cf61dba2dc..a8ba79aa2470 100644 --- a/internal/services/appservice/windows_function_app_slot_resource_test.go +++ b/internal/services/appservice/windows_function_app_slot_resource_test.go @@ -815,7 +815,39 @@ func TestAccWindowsFunctionAppSlot_updateStorageAccount(t *testing.T) { }) } -func TestAccWindowsFunctionAppSlot_msiStorageAccount(t *testing.T) { +func TestAccWindowsFunctionAppSlot_msiStorageAccountConsumption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuConsumptionPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlot_msiStorageAccountElastic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.msiStorageAccount(data, SkuElasticPremiumPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kind").HasValue("functionapp"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlot_msiStorageAccountStandard(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") r := WindowsFunctionAppSlotResource{} @@ -863,6 +895,81 @@ func TestAccWindowsFunctionAppSlot_storageAccountKeyVaultSecretVersionless(t *te }) } +func TestAccWindowsFunctionAppSlot_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlot_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlotASEv3_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // Exists func (r WindowsFunctionAppSlotResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -2001,7 +2108,11 @@ resource "azurerm_windows_function_app_slot" "test" { type = "SystemAssigned" } - site_config {} + site_config { + application_stack { + dotnet_version = "6" + } + } } `, r.template(data, planSku), data.RandomInteger) } @@ -2163,7 +2274,7 @@ func (WindowsFunctionAppSlotResource) template(data acceptance.TestData, planSku } return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-LFA-%[1]d" + name = "acctestRG-WFA-%[1]d" location = "%[2]s" } @@ -2316,3 +2427,189 @@ resource "azurerm_windows_function_app" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString, planSku) } + +func (r WindowsFunctionAppSlotResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_windows_function_app_slot" "test" { + name = "acctest-WFAS-%d" + function_app_id = azurerm_windows_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppSlotResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_windows_function_app_slot" "test" { + name = "acctest-WFAS-%d" + function_app_id = azurerm_windows_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + virtual_network_subnet_id = azurerm_subnet.test1.id + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppSlotResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} +resource "azurerm_windows_function_app_slot" "test" { + name = "acctest-WFAS-%d" + function_app_id = azurerm_windows_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + virtual_network_subnet_id = azurerm_subnet.test2.id + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_windows_function_app" "test" { + name = "acctest-WFA-%[3]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +resource "azurerm_windows_function_app_slot" "test" { + name = "acctest-WFAS-%[3]d" + function_app_id = azurerm_windows_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config { + vnet_route_all_enabled = true + } +} + +`, ServicePlanResource{}.aseV3(data), data.RandomString, data.RandomInteger) +} diff --git a/internal/services/appservice/windows_web_app_data_source.go b/internal/services/appservice/windows_web_app_data_source.go index 48a9d59ef9f2..f4641f72659f 100644 --- a/internal/services/appservice/windows_web_app_data_source.go +++ b/internal/services/appservice/windows_web_app_data_source.go @@ -46,6 +46,7 @@ type WindowsWebAppDataSourceModel struct { PossibleOutboundIPAddressList []string `tfschema:"possible_outbound_ip_address_list"` SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` } var _ sdk.DataSource = WindowsWebAppDataSource{} @@ -172,6 +173,11 @@ func (d WindowsWebAppDataSource) Attributes() map[string]*pluginsdk.Schema { "storage_account": helpers.StorageAccountSchemaComputed(), + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "tags": tags.SchemaDataSource(), } } diff --git a/internal/services/appservice/windows_web_app_resource.go b/internal/services/appservice/windows_web_app_resource.go index a730feb630a9..fbfa9bd52ce6 100644 --- a/internal/services/appservice/windows_web_app_resource.go +++ b/internal/services/appservice/windows_web_app_resource.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -52,6 +53,7 @@ type WindowsWebAppModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` ZipDeployFile string `tfschema:"zip_deploy_file"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` } var _ sdk.ResourceWithCustomImporter = WindowsWebAppResource{} @@ -151,6 +153,12 @@ func (r WindowsWebAppResource) Arguments() map[string]*pluginsdk.Schema { }, "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -313,6 +321,10 @@ func (r WindowsWebAppResource) Create() sdk.ResourceFunc { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(webApp.KeyVaultReferenceIdentityID) } + if webApp.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(webApp.VirtualNetworkSubnetID) + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.SiteName, siteEnvelope) if err != nil { return fmt.Errorf("creating Windows %s: %+v", id, err) @@ -510,6 +522,10 @@ func (r WindowsWebAppResource) Read() sdk.ResourceFunc { Tags: tags.ToTypedObject(webApp.Tags), } + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + var healthCheckCount *int state.AppSettings, healthCheckCount = helpers.FlattenAppSettings(appSettings) @@ -655,6 +671,19 @@ func (r WindowsWebAppResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetwork(ctx, id.ResourceGroup, id.SiteName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + currentStack := "" stateConfig := state.SiteConfig[0] if len(stateConfig.ApplicationStack) == 1 { diff --git a/internal/services/appservice/windows_web_app_resource_test.go b/internal/services/appservice/windows_web_app_resource_test.go index 4b0f031cc828..ee2fba96de3d 100644 --- a/internal/services/appservice/windows_web_app_resource_test.go +++ b/internal/services/appservice/windows_web_app_resource_test.go @@ -556,13 +556,13 @@ func TestAccWindowsWebApp_virtualDirectoriesUpdate(t *testing.T) { } // App Stacks -func TestAccWindowsWebApp_withDotNet3(t *testing.T) { +func TestAccWindowsWebApp_withDotNetCore(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_web_app", "test") r := WindowsWebAppResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.dotNetCore(data, "v3.0"), + Config: r.dotNetCore(data, "core3.1"), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -1055,6 +1055,66 @@ func TestAccWindowsWebAppASEV3_basic(t *testing.T) { }) } +func TestAccWindowsWebApp_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_web_app", "test") + r := WindowsWebAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegrationWebApp_subnet1(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsWebApp_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_web_app", "test") + r := WindowsWebAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegrationWebApp_basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet1(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet2(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r WindowsWebAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.WebAppID(state.ID) if err != nil { @@ -2828,3 +2888,158 @@ data "azurerm_storage_account_sas" "test" { } `, r.baseTemplate(data), data.RandomInteger, data.RandomString) } + +func (r WindowsWebAppResource) vNetIntegrationWebApp_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsWebAppResource) vNetIntegrationWebApp_subnet1(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsWebAppResource) vNetIntegrationWebApp_subnet2(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/appservice/windows_web_app_slot_resource.go b/internal/services/appservice/windows_web_app_slot_resource.go index 011c4e5a851c..de51b5def48d 100644 --- a/internal/services/appservice/windows_web_app_slot_resource.go +++ b/internal/services/appservice/windows_web_app_slot_resource.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -47,6 +48,7 @@ type WindowsWebAppSlotModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` ZipDeployFile string `tfschema:"zip_deploy_file"` Tags map[string]string `tfschema:"tags"` + VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` } var _ sdk.ResourceWithUpdate = WindowsWebAppSlotResource{} @@ -153,6 +155,12 @@ func (r WindowsWebAppSlotResource) Arguments() map[string]*pluginsdk.Schema { }, "tags": tags.Schema(), + + "virtual_network_subnet_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, + }, } } @@ -272,6 +280,10 @@ func (r WindowsWebAppSlotResource) Create() sdk.ResourceFunc { siteEnvelope.SiteProperties.KeyVaultReferenceIdentity = utils.String(webAppSlot.KeyVaultReferenceIdentityID) } + if webAppSlot.VirtualNetworkSubnetID != "" { + siteEnvelope.SiteProperties.VirtualNetworkSubnetID = utils.String(webAppSlot.VirtualNetworkSubnetID) + } + future, err := client.CreateOrUpdateSlot(ctx, id.ResourceGroup, id.SiteName, siteEnvelope, id.SlotName) if err != nil { return fmt.Errorf("creating Windows %s: %+v", id, err) @@ -450,6 +462,10 @@ func (r WindowsWebAppSlotResource) Read() sdk.ResourceFunc { Tags: tags.ToTypedObject(webApp.Tags), } + if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { + state.VirtualNetworkSubnetID = subnetId + } + var healthCheckCount *int state.AppSettings, healthCheckCount = helpers.FlattenAppSettings(appSettings) @@ -585,6 +601,19 @@ func (r WindowsWebAppSlotResource) Update() sdk.ResourceFunc { existing.SiteConfig = siteConfig } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetworkSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + updateFuture, err := client.CreateOrUpdateSlot(ctx, id.ResourceGroup, id.SiteName, existing, id.SlotName) if err != nil { return fmt.Errorf("updating Windows %s: %+v", id, err) diff --git a/internal/services/appservice/windows_web_app_slot_resource_test.go b/internal/services/appservice/windows_web_app_slot_resource_test.go index 9d58283baeae..813b82c2a20b 100644 --- a/internal/services/appservice/windows_web_app_slot_resource_test.go +++ b/internal/services/appservice/windows_web_app_slot_resource_test.go @@ -710,6 +710,66 @@ func TestAccWindowsWebAppSlot_zipDeploy(t *testing.T) { }) } +func TestAccWindowsWebAppSlot_vNetIntegration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_web_app_slot", "test") + r := WindowsWebAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegrationWebApp_subnet1(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsWebAppSlot_vNetIntegrationUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_web_app_slot", "test") + r := WindowsWebAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegrationWebApp_basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet1(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_subnet2(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegrationWebApp_basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // Exists func (r WindowsWebAppSlotResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -1853,3 +1913,155 @@ resource "azurerm_windows_web_app" "test" { } `, data.RandomInteger, data.Locations.Primary) } + +func (r WindowsWebAppSlotResource) vNetIntegrationWebApp_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app_slot" "test" { + name = "acctestWAS-%[2]d" + app_service_id = azurerm_windows_web_app.test.id + + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsWebAppSlotResource) vNetIntegrationWebApp_subnet1(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app_slot" "test" { + name = "acctestWAS-%[2]d" + app_service_id = azurerm_windows_web_app.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} + +func (r WindowsWebAppSlotResource) vNetIntegrationWebApp_subnet2(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_virtual_network" "test" { + name = "vnet-%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" "test1" { + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_windows_web_app_slot" "test" { + name = "acctestWAS-%[2]d" + app_service_id = azurerm_windows_web_app.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id + + site_config {} +} +`, r.baseTemplate(data), data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/automation/automation_account_data_source.go b/internal/services/automation/automation_account_data_source.go index e5646692b0dc..6e5ef6c19e2f 100644 --- a/internal/services/automation/automation_account_data_source.go +++ b/internal/services/automation/automation_account_data_source.go @@ -4,9 +4,10 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -40,6 +41,22 @@ func dataSourceAutomationAccount() *pluginsdk.Resource { Type: pluginsdk.TypeString, Computed: true, }, + "private_endpoint_connection": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, }, } } @@ -47,21 +64,23 @@ func dataSourceAutomationAccount() *pluginsdk.Resource { func dataSourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) error { iclient := meta.(*clients.Client).Automation.AgentRegistrationInfoClient client := meta.(*clients.Client).Automation.AccountClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewAutomationAccountID(client.SubscriptionID, d.Get("resource_group_name").(string), d.Get("name").(string)) + id := automationaccount.NewAutomationAccountID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } return fmt.Errorf("retreiving %s: %+v", id, err) } d.SetId(id.ID()) - iresp, err := iclient.Get(ctx, id.ResourceGroup, id.Name) + iresp, err := iclient.Get(ctx, id.ResourceGroupName, id.AutomationAccountName) if err != nil { if utils.ResponseWasNotFound(iresp.Response) { return fmt.Errorf("%q Account Registration Information was not found", id) @@ -73,5 +92,21 @@ func dataSourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{} d.Set("secondary_key", iresp.Keys.Secondary) } d.Set("endpoint", iresp.Endpoint) + if resp.Model != nil && resp.Model.Properties != nil { + d.Set("private_endpoint_connection", flattenPrivateEndpointConnections(resp.Model.Properties.PrivateEndpointConnections)) + } return nil } + +func flattenPrivateEndpointConnections(conns *[]automationaccount.PrivateEndpointConnection) (res []interface{}) { + if conns == nil || len(*conns) == 0 { + return + } + for _, con := range *conns { + res = append(res, map[string]interface{}{ + "id": con.Id, + "name": con.Name, + }) + } + return res +} diff --git a/internal/services/automation/automation_account_resource.go b/internal/services/automation/automation_account_resource.go index 18e665f53bc3..21483628e355 100644 --- a/internal/services/automation/automation_account_resource.go +++ b/internal/services/automation/automation_account_resource.go @@ -5,14 +5,18 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/automation/mgmt/2020-01-13-preview/automation" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/validate" + keyVaultParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -27,7 +31,7 @@ func resourceAutomationAccount() *pluginsdk.Resource { Update: resourceAutomationAccountUpdate, Delete: resourceAutomationAccountDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.AutomationAccountID(id) + _, err := automationaccount.ParseAutomationAccountID(id) return err }), @@ -51,16 +55,50 @@ func resourceAutomationAccount() *pluginsdk.Resource { "resource_group_name": commonschema.ResourceGroupName(), "sku_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(automation.SkuNameEnumBasic), - string(automation.SkuNameEnumFree), - }, false), + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(automationaccount.PossibleValuesForSkuNameEnum(), false), }, "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), + "encryption": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "user_assigned_identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + }, + + "key_source": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice( + automationaccount.PossibleValuesForEncryptionKeySourceType(), + false, + ), + }, + + "key_vault_key_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.NestedItemIdWithOptionalVersion, + }, + }, + }, + }, + + "local_authentication_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + "tags": tags.Schema(), "dsc_server_endpoint": { @@ -82,6 +120,22 @@ func resourceAutomationAccount() *pluginsdk.Resource { Optional: true, Default: true, }, + "private_endpoint_connection": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, }, } } @@ -92,35 +146,51 @@ func resourceAutomationAccountCreate(d *pluginsdk.ResourceData, meta interface{} ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewAutomationAccountID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + id := automationaccount.NewAutomationAccountID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_automation_account", id.ID()) } - identity, err := expandAutomationAccountIdentity(d.Get("identity").([]interface{}), true) + identityVal, err := identity.ExpandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) } - parameters := automation.AccountCreateOrUpdateParameters{ - AccountCreateOrUpdateProperties: &automation.AccountCreateOrUpdateProperties{ - Sku: &automation.Sku{ - Name: automation.SkuNameEnum(d.Get("sku_name").(string)), + parameters := automationaccount.AutomationAccountCreateOrUpdateParameters{ + Properties: &automationaccount.AutomationAccountCreateOrUpdateProperties{ + Sku: &automationaccount.Sku{ + Name: automationaccount.SkuNameEnum(d.Get("sku_name").(string)), }, PublicNetworkAccess: utils.Bool(d.Get("public_network_access_enabled").(bool)), }, Location: utils.String(location.Normalize(d.Get("location").(string))), - Identity: identity, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters); err != nil { + if localAuth := d.Get("local_authentication_enabled").(bool); localAuth == false { + parameters.Properties.DisableLocalAuth = utils.Bool(true) + } + if encryption := d.Get("encryption").([]interface{}); len(encryption) > 0 { + enc, err := expandEncryption(encryption[0].(map[string]interface{})) + if err != nil { + return fmt.Errorf("expanding `encryption`: %v", err) + } + parameters.Properties.Encryption = enc + } + // for create account do not set identity property (even TypeNone is not allowed), or api will response error + if identityVal.Type != identity.TypeNone { + parameters.Identity = identityVal + } + if tagsVal := expandTags(d.Get("tags").(map[string]interface{})); tagsVal != nil { + parameters.Tags = &tagsVal + } + + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } @@ -133,27 +203,42 @@ func resourceAutomationAccountUpdate(d *pluginsdk.ResourceData, meta interface{} ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AutomationAccountID(d.Id()) + id, err := automationaccount.ParseAutomationAccountID(d.Id()) if err != nil { return err } - identity, err := expandAutomationAccountIdentity(d.Get("identity").([]interface{}), false) + identity, err := identity.ExpandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) } - parameters := automation.AccountUpdateParameters{ - AccountUpdateProperties: &automation.AccountUpdateProperties{ - Sku: &automation.Sku{ - Name: automation.SkuNameEnum(d.Get("sku_name").(string)), + parameters := automationaccount.AutomationAccountUpdateParameters{ + Properties: &automationaccount.AutomationAccountUpdateProperties{ + Sku: &automationaccount.Sku{ + Name: automationaccount.SkuNameEnum(d.Get("sku_name").(string)), }, PublicNetworkAccess: utils.Bool(d.Get("public_network_access_enabled").(bool)), }, Location: utils.String(location.Normalize(d.Get("location").(string))), Identity: identity, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - if _, err := client.Update(ctx, id.ResourceGroup, id.Name, parameters); err != nil { + if localAuth := d.Get("local_authentication_enabled").(bool); localAuth == false { + parameters.Properties.DisableLocalAuth = utils.Bool(true) + } + + if encryption := d.Get("encryption").([]interface{}); len(encryption) > 0 { + enc, err := expandEncryption(encryption[0].(map[string]interface{})) + if err != nil { + return fmt.Errorf("expanding `encryption`: %v", err) + } + parameters.Properties.Encryption = enc + } + + if tagsVal := expandTags(d.Get("tags").(map[string]interface{})); tagsVal != nil { + parameters.Tags = &tagsVal + } + + if _, err := client.Update(ctx, *id, parameters); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } @@ -166,14 +251,14 @@ func resourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AutomationAccountID(d.Id()) + id, err := automationaccount.ParseAutomationAccountID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[DEBUG] %s was not found - removing from state!", *id) d.SetId("") return nil @@ -182,9 +267,9 @@ func resourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("retrieving %s: %+v", *id, err) } - keysResp, err := registrationClient.Get(ctx, id.ResourceGroup, id.Name) + keysResp, err := registrationClient.Get(ctx, id.ResourceGroupName, id.AutomationAccountName) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[DEBUG] Agent Registration Info for %s was not found - removing from state!", *id) d.SetId("") return nil @@ -193,27 +278,42 @@ func resourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("retrieving Registration Info for %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) + d.Set("name", id.AutomationAccountName) + d.Set("resource_group_name", id.ResourceGroupName) + + d.Set("location", location.NormalizeNilable(resp.Model.Location)) publicNetworkAccessEnabled := true - if resp.PublicNetworkAccess != nil { - publicNetworkAccessEnabled = *resp.PublicNetworkAccess + if resp.Model == nil || resp.Model.Properties == nil { + return fmt.Errorf("retrieving Automation Account got empty Model") + } + prop := resp.Model.Properties + if prop.PublicNetworkAccess != nil { + publicNetworkAccessEnabled = *prop.PublicNetworkAccess } d.Set("public_network_access_enabled", publicNetworkAccessEnabled) skuName := "" - if sku := resp.Sku; sku != nil { - skuName = string(resp.Sku.Name) + if sku := prop.Sku; sku != nil { + skuName = string(prop.Sku.Name) } d.Set("sku_name", skuName) + localAuthEnabled := true + if val := prop.DisableLocalAuth; val != nil && *val == true { + localAuthEnabled = false + } + d.Set("local_authentication_enabled", localAuthEnabled) + + if err := d.Set("encryption", flattenEncryption(prop.Encryption)); err != nil { + return fmt.Errorf("setting `encryption`: %+v", err) + } + d.Set("dsc_server_endpoint", keysResp.Endpoint) if keys := keysResp.Keys; keys != nil { d.Set("dsc_primary_access_key", keys.Primary) d.Set("dsc_secondary_access_key", keys.Secondary) } - identity, err := flattenAutomationAccountIdentity(resp.Identity) + identity, err := identity.FlattenSystemAndUserAssignedMap(resp.Model.Identity) if err != nil { return fmt.Errorf("flattening `identity`: %+v", err) } @@ -221,7 +321,14 @@ func resourceAutomationAccountRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("setting `identity`: %+v", err) } - return tags.FlattenAndSet(d, resp.Tags) + if resp.Model != nil && resp.Model.Properties != nil { + d.Set("private_endpoint_connection", flattenPrivateEndpointConnections(resp.Model.Properties.PrivateEndpointConnections)) + } + + if resp.Model.Tags != nil { + return flattenAndSetTags(d, *resp.Model.Tags) + } + return nil } func resourceAutomationAccountDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -229,14 +336,14 @@ func resourceAutomationAccountDelete(d *pluginsdk.ResourceData, meta interface{} ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AutomationAccountID(d.Id()) + id, err := automationaccount.ParseAutomationAccountID(d.Id()) if err != nil { return err } - resp, err := client.Delete(ctx, id.ResourceGroup, id.Name) + resp, err := client.Delete(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp) { + if response.WasNotFound(resp.HttpResponse) { return nil } @@ -246,57 +353,50 @@ func resourceAutomationAccountDelete(d *pluginsdk.ResourceData, meta interface{} return nil } -func expandAutomationAccountIdentity(input []interface{}, newResource bool) (*automation.Identity, error) { - expanded, err := identity.ExpandSystemAndUserAssignedMap(input) - if err != nil { - return nil, err +func expandEncryption(encMap map[string]interface{}) (*automationaccount.EncryptionProperties, error) { + var id interface{} + id, ok := encMap["user_assigned_identity_id"].(string) + if !ok { + return nil, fmt.Errorf("read encryption user identity id error") } - - if newResource && expanded.Type == identity.TypeNone { - return nil, nil + prop := &automationaccount.EncryptionProperties{ + Identity: &automationaccount.EncryptionPropertiesIdentity{ + UserAssignedIdentity: &id, + }, } - - out := automation.Identity{ - Type: automation.ResourceIdentityType(string(expanded.Type)), + if val, ok := encMap["key_source"].(string); ok && val != "" { + prop.KeySource = (*automationaccount.EncryptionKeySourceType)(&val) } - - if len(expanded.IdentityIds) > 0 { - ids := make(map[string]*automation.IdentityUserAssignedIdentitiesValue) - - for k := range expanded.IdentityIds { - ids[k] = &automation.IdentityUserAssignedIdentitiesValue{ - // intentionally empty - } + if keyIdStr := encMap["key_vault_key_id"].(string); keyIdStr != "" { + keyId, err := keyVaultParse.ParseOptionallyVersionedNestedItemID(keyIdStr) + if err != nil { + return nil, err + } + prop.KeyVaultProperties = &automationaccount.KeyVaultProperties{ + KeyName: utils.String(keyId.Name), + KeyVersion: utils.String(keyId.Version), + KeyvaultUri: utils.String(keyId.KeyVaultBaseUrl), } - - out.UserAssignedIdentities = ids } - - return &out, nil + return prop, nil } -func flattenAutomationAccountIdentity(input *automation.Identity) (*[]interface{}, error) { - var transformed *identity.SystemAndUserAssignedMap - if input != nil { - transformed = &identity.SystemAndUserAssignedMap{ - Type: identity.Type(string(input.Type)), - IdentityIds: make(map[string]identity.UserAssignedIdentityDetails), - } - if input.PrincipalID != nil { - transformed.PrincipalId = *input.PrincipalID - } - if input.TenantID != nil { - transformed.TenantId = *input.TenantID - } - if input.UserAssignedIdentities != nil { - for k, v := range input.UserAssignedIdentities { - transformed.IdentityIds[k] = identity.UserAssignedIdentityDetails{ - ClientId: v.ClientID, - PrincipalId: v.PrincipalID, - } - } +func flattenEncryption(encryption *automationaccount.EncryptionProperties) (res []interface{}) { + if encryption == nil { + return + } + item := map[string]interface{}{} + if encryption.KeySource != nil { + item["key_source"] = (string)(*encryption.KeySource) + } + if encryption.Identity != nil && encryption.Identity.UserAssignedIdentity != nil { + item["user_assigned_identity_id"] = (*encryption.Identity.UserAssignedIdentity).(string) + } + if keyProp := encryption.KeyVaultProperties; keyProp != nil { + keyVaultKeyId, err := keyVaultParse.NewNestedItemID(*keyProp.KeyvaultUri, "keys", *keyProp.KeyName, *keyProp.KeyVersion) + if err == nil { + item["key_vault_key_id"] = keyVaultKeyId.ID() } } - - return identity.FlattenSystemAndUserAssignedMap(transformed) + return []interface{}{item} } diff --git a/internal/services/automation/automation_account_resource_test.go b/internal/services/automation/automation_account_resource_test.go index baad6de2efb6..bbf395aefceb 100644 --- a/internal/services/automation/automation_account_resource_test.go +++ b/internal/services/automation/automation_account_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -69,6 +70,24 @@ func TestAccAutomationAccount_complete(t *testing.T) { }) } +func TestAccAutomationAccount_encryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_automation_account", "test") + r := AutomationAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.encryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku_name").HasValue("Basic"), + check.That(data.ResourceName).Key("local_authentication_enabled").HasValue("false"), + check.That(data.ResourceName).Key("encryption.0.key_source").HasValue("Microsoft.Keyvault"), + ), + }, + data.ImportStep(), + }) +} + func TestAccAutomationAccount_identityUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_automation_account", "test") r := AutomationAccountResource{} @@ -160,17 +179,17 @@ func TestAccAutomationAccount_userAssignedIdentity(t *testing.T) { } func (t AutomationAccountResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.AutomationAccountID(state.ID) + id, err := automationaccount.ParseAutomationAccountID(state.ID) if err != nil { return nil, err } - resp, err := clients.Automation.AccountClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.Automation.AccountClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving Automation Account %q (resource group: %q): %+v", id.Name, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving Automation Account %q (resource group: %q): %+v", id.AutomationAccountName, id.ResourceGroupName, err) } - return utils.Bool(resp.AccountProperties != nil), nil + return utils.Bool(resp.Model.Properties != nil), nil } func (AutomationAccountResource) basic(data acceptance.TestData) string { @@ -256,6 +275,123 @@ resource "azurerm_automation_account" "test" { `, data.RandomInteger, data.Locations.Primary) } +func (AutomationAccountResource) encryption(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-auto-%[1]d" + location = "%[2]s" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestUAI-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_key_vault" "test" { + name = "vault%[1]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 = "standard" + soft_delete_retention_days = 7 + purge_protection_enabled = true + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + certificate_permissions = [ + "ManageContacts", + ] + + key_permissions = [ + "Create", + "Get", + "List", + "Delete", + "Purge", + ] + + secret_permissions = [ + "Set", + ] + } + + access_policy { + tenant_id = azurerm_user_assigned_identity.test.tenant_id + object_id = azurerm_user_assigned_identity.test.principal_id + + certificate_permissions = [] + + key_permissions = [ + "Get", + "Recover", + "WrapKey", + "UnwrapKey", + ] + + secret_permissions = [] + } +} + +data "azurerm_key_vault" "test" { + name = azurerm_key_vault.test.name + resource_group_name = azurerm_key_vault.test.resource_group_name +} + +resource "azurerm_key_vault_key" "test" { + name = "acckvkey-%[1]d" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_automation_account" "test" { + name = "acctest-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Basic" + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + + local_authentication_enabled = false + + encryption { + key_source = "Microsoft.Keyvault" + user_assigned_identity_id = azurerm_user_assigned_identity.test.id + key_vault_key_id = azurerm_key_vault_key.test.id + } +} +`, data.RandomInteger, data.Locations.Primary) +} + func (AutomationAccountResource) userAssignedIdentity(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/automation/automation_connection_type_resource.go b/internal/services/automation/automation_connection_type_resource.go new file mode 100644 index 000000000000..347b082d1b91 --- /dev/null +++ b/internal/services/automation/automation_connection_type_resource.go @@ -0,0 +1,211 @@ +package automation + +import ( + "context" + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/automation/mgmt/2020-01-13-preview/automation" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type Field struct { + Name string `tfschema:"name"` + IsOptional bool `tfschema:"is_optional"` + IsEncrypted bool `tfschema:"is_encrypted"` + Type string `tfschema:"type"` +} + +type AutomationConnectionTypeModel struct { + ResourceGrup string `json:"resource_grup" tfschema:"resource_group_name"` + AutomationAccountName string `json:"automation_account_name" tfschema:"automation_account_name"` + Name string `json:"name" tfschema:"name"` + IsGlobal bool `json:"is_global" tfschema:"is_global"` + Field []Field `json:"field" tfschema:"field"` +} + +type AutomationConnectionTypeResource struct{} + +var _ sdk.Resource = (*AutomationConnectionTypeResource)(nil) + +func (m AutomationConnectionTypeResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "resource_group_name": azure.SchemaResourceGroupName(), + + "automation_account_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.AutomationAccount(), + }, + + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ConnectionTypeName, + }, + + "is_global": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + + "field": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + "is_encrypted": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + "is_optional": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + "type": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + }, + } +} + +func (m AutomationConnectionTypeResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (m AutomationConnectionTypeResource) ModelObject() interface{} { + return &AutomationConnectionTypeModel{} +} + +func (m AutomationConnectionTypeResource) ResourceType() string { + return "azurerm_automation_connection_type" +} + +func (m AutomationConnectionTypeResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + client := meta.Client.Automation.ConnectionTypeClient + connClient := meta.Client.Automation.AccountClient + + var model AutomationConnectionTypeModel + if err := meta.Decode(&model); err != nil { + return err + } + subscriptionID := meta.Client.Account.SubscriptionId + + accountID := automationaccount.NewAutomationAccountID(subscriptionID, model.ResourceGrup, model.AutomationAccountName) + account, err := connClient.Get(ctx, accountID) + if err != nil { + return fmt.Errorf("retrieving automation account %q: %+v", accountID, err) + } + if response.WasNotFound(account.HttpResponse) { + return fmt.Errorf("automation account %q was not found", accountID) + } + + id := parse.NewConnectionTypeID(accountID.SubscriptionId, model.ResourceGrup, model.AutomationAccountName, model.Name) + existing, err := client.Get(ctx, id.ResourceGroup, model.AutomationAccountName, model.Name) + if !utils.ResponseWasNotFound(existing.Response) { + if err != nil { + return fmt.Errorf("retreiving %s: %v", id, err) + } + return meta.ResourceRequiresImport(m.ResourceType(), id) + } + param := automation.ConnectionTypeCreateOrUpdateParameters{ + Name: utils.String(model.Name), + ConnectionTypeCreateOrUpdateProperties: &automation.ConnectionTypeCreateOrUpdateProperties{ + IsGlobal: utils.Bool(model.IsGlobal), + FieldDefinitions: map[string]*automation.FieldDefinition{}, + }, + } + for _, field := range model.Field { + param.ConnectionTypeCreateOrUpdateProperties.FieldDefinitions[field.Name] = &automation.FieldDefinition{ + IsEncrypted: utils.Bool(field.IsEncrypted), + IsOptional: utils.Bool(field.IsOptional), + Type: utils.String(field.Type), + } + } + _, err = client.CreateOrUpdate(ctx, id.ResourceGroup, id.AutomationAccountName, id.Name, param) + if err != nil { + return fmt.Errorf("creating %s: %v", id, err) + } + + meta.SetID(id) + return nil + }, + } +} + +func (m AutomationConnectionTypeResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + id, err := parse.ConnectionTypeID(meta.ResourceData.Id()) + if err != nil { + return err + } + + client := meta.Client.Automation.ConnectionTypeClient + result, err := client.Get(ctx, id.ResourceGroup, id.AutomationAccountName, id.Name) + if err != nil { + return err + } + + var output AutomationConnectionTypeModel + output.IsGlobal = utils.NormaliseNilableBool(result.IsGlobal) + output.Name = utils.NormalizeNilableString(result.Name) + output.AutomationAccountName = id.AutomationAccountName + output.ResourceGrup = id.ResourceGroup + for name, prop := range result.FieldDefinitions { + output.Field = append(output.Field, Field{ + Name: name, + Type: utils.NormalizeNilableString(prop.Type), + IsEncrypted: utils.NormaliseNilableBool(prop.IsEncrypted), + IsOptional: utils.NormaliseNilableBool(prop.IsOptional), + }) + } + + return meta.Encode(&output) + }, + } +} + +func (m AutomationConnectionTypeResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 10 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + id, err := parse.ConnectionTypeID(meta.ResourceData.Id()) + if err != nil { + return err + } + + client := meta.Client.Automation.ConnectionTypeClient + if _, err = client.Delete(ctx, id.ResourceGroup, id.AutomationAccountName, id.Name); err != nil { + return fmt.Errorf("deleting %s: %v", id, err) + } + return nil + }, + } +} + +func (m AutomationConnectionTypeResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ConnectionTypeID +} diff --git a/internal/services/automation/automation_connection_type_resource_test.go b/internal/services/automation/automation_connection_type_resource_test.go new file mode 100644 index 000000000000..cd9dc3b16b17 --- /dev/null +++ b/internal/services/automation/automation_connection_type_resource_test.go @@ -0,0 +1,83 @@ +package automation_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type AutomationConnectionTypeResource struct{} + +func (a AutomationConnectionTypeResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.ConnectionTypeID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.Automation.ConnectionTypeClient.Get(ctx, id.ResourceGroup, id.AutomationAccountName, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Automation Connection Type %s: %+v", id, err) + } + return utils.Bool(resp.ConnectionTypeProperties != nil), nil +} + +func (a AutomationConnectionTypeResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-auto-%[1]d" + location = "%[2]s" +} + +resource "azurerm_automation_account" "test" { + name = "acctest-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Basic" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (a AutomationConnectionTypeResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` + + +%s + +resource "azurerm_automation_connection_type" "test" { + name = "acctest-%[2]d" + resource_group_name = azurerm_resource_group.test.name + automation_account_name = azurerm_automation_account.test.name + is_global = false + field { + name = "my_def" + type = "string" + } +} +`, a.template(data), data.RandomInteger) +} + +func TestAccAutomationConnectionType_basic(t *testing.T) { + data := acceptance.BuildTestData(t, automation.AutomationConnectionTypeResource{}.ResourceType(), "test") + r := AutomationConnectionTypeResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("is_global").HasValue("false"), + ), + }, + data.ImportStep(), + }) +} diff --git a/internal/services/automation/automation_hybrid_runbook_worker_group.go b/internal/services/automation/automation_hybrid_runbook_worker_group.go new file mode 100644 index 000000000000..3bc5c1ea142b --- /dev/null +++ b/internal/services/automation/automation_hybrid_runbook_worker_group.go @@ -0,0 +1,180 @@ +package automation + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/hybridrunbookworkergroup" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type HybridRunbookWorkerGroupModel struct { + ResourceGroupName string `tfschema:"resource_group_name"` + AutomationAccountName string `tfschema:"automation_account_name"` + Name string `tfschema:"name"` + CredentialName string `tfschema:"credential_name"` +} + +type HybridRunbookWorkerGroupResource struct{} + +var _ sdk.Resource = (*HybridRunbookWorkerGroupResource)(nil) + +func (m HybridRunbookWorkerGroupResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "resource_group_name": commonschema.ResourceGroupName(), // end if common + "automation_account_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "credential_name": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + } +} + +func (m HybridRunbookWorkerGroupResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (m HybridRunbookWorkerGroupResource) ModelObject() interface{} { + return &HybridRunbookWorkerGroupModel{} +} + +func (m HybridRunbookWorkerGroupResource) ResourceType() string { + return "azurerm_automation_hybrid_runbook_worker_group" +} + +func (m HybridRunbookWorkerGroupResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + client := meta.Client.Automation.RunBookWgClient + + var model HybridRunbookWorkerGroupModel + if err := meta.Decode(&model); err != nil { + return err + } + + subscriptionID := meta.Client.Account.SubscriptionId + id := hybridrunbookworkergroup.NewHybridRunbookWorkerGroupID(subscriptionID, model.ResourceGroupName, + model.AutomationAccountName, model.Name) + existing, err := client.Get(ctx, id) + if !response.WasNotFound(existing.HttpResponse) { + if err != nil { + return fmt.Errorf("retreiving %s: %v", id, err) + } + return meta.ResourceRequiresImport(m.ResourceType(), id) + } + req := hybridrunbookworkergroup.HybridRunbookWorkerGroupCreateOrUpdateParameters{} + if model.CredentialName != "" { + req.Credential = &hybridrunbookworkergroup.RunAsCredentialAssociationProperty{ + Name: utils.String(model.CredentialName), + } + } + // return 201 cause err in autorest sdk + future, err := client.Create(ctx, id, req) + if err != nil && !response.WasStatusCode(future.HttpResponse, http.StatusCreated) { + return fmt.Errorf("creating %s: %v", id, err) + } + + meta.SetID(id) + return nil + }, + } +} + +func (m HybridRunbookWorkerGroupResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + id, err := hybridrunbookworkergroup.ParseHybridRunbookWorkerGroupID(meta.ResourceData.Id()) + if err != nil { + return err + } + client := meta.Client.Automation.RunBookWgClient + result, err := client.Get(ctx, *id) + if err != nil { + return err + } + if result.Model == nil { + return fmt.Errorf("retrieving %s got nil model", id) + } + var output HybridRunbookWorkerGroupModel + + output.Name = utils.NormalizeNilableString(result.Model.Name) + output.AutomationAccountName = id.AutomationAccountName + if c := result.Model.Credential; c != nil { + output.CredentialName = utils.NormalizeNilableString(c.Name) + } + output.ResourceGroupName = id.ResourceGroupName + return meta.Encode(&output) + }, + } +} + +func (m HybridRunbookWorkerGroupResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 10 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) (err error) { + client := meta.Client.Automation.RunBookWgClient + id, err := hybridrunbookworkergroup.ParseHybridRunbookWorkerGroupID(meta.ResourceData.Id()) + if err != nil { + return err + } + + var model HybridRunbookWorkerGroupModel + if err = meta.Decode(&model); err != nil { + return fmt.Errorf("decoding err: %+v", err) + } + + var upd hybridrunbookworkergroup.HybridRunbookWorkerGroupCreateOrUpdateParameters + if meta.ResourceData.HasChange("credential_name") { + upd.Credential = &hybridrunbookworkergroup.RunAsCredentialAssociationProperty{ + Name: utils.String(model.CredentialName), + } + } + if _, err = client.Update(ctx, *id, upd); err != nil { + return fmt.Errorf("updating %s: %v", id, err) + } + + return nil + }, + } +} + +func (m HybridRunbookWorkerGroupResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 10 * time.Minute, + Func: func(ctx context.Context, meta sdk.ResourceMetaData) error { + id, err := hybridrunbookworkergroup.ParseHybridRunbookWorkerGroupID(meta.ResourceData.Id()) + if err != nil { + return err + } + meta.Logger.Infof("deleting %s", id) + client := meta.Client.Automation.RunBookWgClient + if _, err = client.Delete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %v", id, err) + } + return nil + }, + } +} + +func (m HybridRunbookWorkerGroupResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return hybridrunbookworkergroup.ValidateHybridRunbookWorkerGroupID +} diff --git a/internal/services/automation/automation_hybrid_runbook_worker_group_test.go b/internal/services/automation/automation_hybrid_runbook_worker_group_test.go new file mode 100644 index 000000000000..956b849ef8ab --- /dev/null +++ b/internal/services/automation/automation_hybrid_runbook_worker_group_test.go @@ -0,0 +1,129 @@ +package automation_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/hybridrunbookworkergroup" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type HybridRunbookWorkerGroupResource struct{} + +func (a HybridRunbookWorkerGroupResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := hybridrunbookworkergroup.ParseHybridRunbookWorkerGroupID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.Automation.RunBookWgClient.Get(ctx, *id) + if err != nil { + return nil, fmt.Errorf("retrieving HybridRunbookWorkerGroup %s: %+v", id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (a HybridRunbookWorkerGroupResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-auto-%[1]d" + location = "%[2]s" +} + +resource "azurerm_automation_account" "test" { + name = "acctestAA-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Basic" +} + +resource "azurerm_automation_credential" "test" { + name = "acctest-%[1]d" + resource_group_name = azurerm_resource_group.test.name + automation_account_name = azurerm_automation_account.test.name + username = "test_user" + password = "test_pwd" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (a HybridRunbookWorkerGroupResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` + + +%s + +resource "azurerm_automation_hybrid_runbook_worker_group" "test" { + resource_group_name = azurerm_resource_group.test.name + automation_account_name = azurerm_automation_account.test.name + name = "acctest-%[2]d" + credential_name = azurerm_automation_credential.test.name +} +`, a.template(data), data.RandomInteger, data.Locations.Primary) +} + +func (a HybridRunbookWorkerGroupResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` + + +%s + +resource "azurerm_automation_credential" "test2" { + name = "acctest2-%d" + resource_group_name = azurerm_resource_group.test.name + automation_account_name = azurerm_automation_account.test.name + username = "test_user" + password = "test_pwd" +} + +resource "azurerm_automation_hybrid_runbook_worker_group" "test" { + resource_group_name = azurerm_resource_group.test.name + automation_account_name = azurerm_automation_account.test.name + name = "acctest-%[2]d" + credential_name = azurerm_automation_credential.test2.name +} +`, a.template(data), data.RandomInteger) +} + +func TestAccHybridRunbookWorkerGroup_basic(t *testing.T) { + data := acceptance.BuildTestData(t, automation.HybridRunbookWorkerGroupResource{}.ResourceType(), "test") + r := HybridRunbookWorkerGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + }) +} + +func TestAccHybridRunbookWorkerGroup_update(t *testing.T) { + data := acceptance.BuildTestData(t, automation.HybridRunbookWorkerGroupResource{}.ResourceType(), "test") + r := HybridRunbookWorkerGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} diff --git a/internal/services/automation/client/client.go b/internal/services/automation/client/client.go index ab9d59911c7c..689c2a94654e 100644 --- a/internal/services/automation/client/client.go +++ b/internal/services/automation/client/client.go @@ -2,11 +2,13 @@ package client import ( "github.com/Azure/azure-sdk-for-go/services/preview/automation/mgmt/2020-01-13-preview/automation" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" + "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/hybridrunbookworkergroup" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - AccountClient *automation.AccountClient + AccountClient *automationaccount.AutomationAccountClient AgentRegistrationInfoClient *automation.AgentRegistrationInformationClient CertificateClient *automation.CertificateClient ConnectionClient *automation.ConnectionClient @@ -18,13 +20,14 @@ type Client struct { ModuleClient *automation.ModuleClient RunbookClient *automation.RunbookClient RunbookDraftClient *automation.RunbookDraftClient + RunBookWgClient *hybridrunbookworkergroup.HybridRunbookWorkerGroupClient ScheduleClient *automation.ScheduleClient VariableClient *automation.VariableClient WebhookClient *automation.WebhookClient } func NewClient(o *common.ClientOptions) *Client { - accountClient := automation.NewAccountClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + accountClient := automationaccount.NewAutomationAccountClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&accountClient.Client, o.ResourceManagerAuthorizer) agentRegistrationInfoClient := automation.NewAgentRegistrationInformationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) @@ -60,6 +63,9 @@ func NewClient(o *common.ClientOptions) *Client { runbookDraftClient := automation.NewRunbookDraftClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&runbookDraftClient.Client, o.ResourceManagerAuthorizer) + runbookWgClient := hybridrunbookworkergroup.NewHybridRunbookWorkerGroupClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&runbookWgClient.Client, o.ResourceManagerAuthorizer) + scheduleClient := automation.NewScheduleClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&scheduleClient.Client, o.ResourceManagerAuthorizer) @@ -82,6 +88,7 @@ func NewClient(o *common.ClientOptions) *Client { ModuleClient: &moduleClient, RunbookClient: &runbookClient, RunbookDraftClient: &runbookDraftClient, + RunBookWgClient: &runbookWgClient, ScheduleClient: &scheduleClient, VariableClient: &variableClient, WebhookClient: &webhookClient, diff --git a/internal/services/automation/parse/connection_type.go b/internal/services/automation/parse/connection_type.go new file mode 100644 index 000000000000..15d87fe68785 --- /dev/null +++ b/internal/services/automation/parse/connection_type.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type ConnectionTypeId struct { + SubscriptionId string + ResourceGroup string + AutomationAccountName string + Name string +} + +func NewConnectionTypeID(subscriptionId, resourceGroup, automationAccountName, name string) ConnectionTypeId { + return ConnectionTypeId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + AutomationAccountName: automationAccountName, + Name: name, + } +} + +func (id ConnectionTypeId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + fmt.Sprintf("Automation Account Name %q", id.AutomationAccountName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Connection Type", segmentsStr) +} + +func (id ConnectionTypeId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Automation/automationAccounts/%s/connectionTypes/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.AutomationAccountName, id.Name) +} + +// ConnectionTypeID parses a ConnectionType ID into an ConnectionTypeId struct +func ConnectionTypeID(input string) (*ConnectionTypeId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ConnectionTypeId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.AutomationAccountName, err = id.PopSegment("automationAccounts"); err != nil { + return nil, err + } + if resourceId.Name, err = id.PopSegment("connectionTypes"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/automation/parse/connection_type_test.go b/internal/services/automation/parse/connection_type_test.go new file mode 100644 index 000000000000..6efb7ac7047a --- /dev/null +++ b/internal/services/automation/parse/connection_type_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = ConnectionTypeId{} + +func TestConnectionTypeIDFormatter(t *testing.T) { + actual := NewConnectionTypeID("12345678-1234-9876-4563-123456789012", "group1", "account1", "type1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/type1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestConnectionTypeID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ConnectionTypeId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing AutomationAccountName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/", + Error: true, + }, + + { + // missing value for AutomationAccountName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/type1", + Expected: &ConnectionTypeId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "group1", + AutomationAccountName: "account1", + Name: "type1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/ACCOUNT1/CONNECTIONTYPES/TYPE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ConnectionTypeID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.AutomationAccountName != v.Expected.AutomationAccountName { + t.Fatalf("Expected %q but got %q for AutomationAccountName", v.Expected.AutomationAccountName, actual.AutomationAccountName) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/automation/registration.go b/internal/services/automation/registration.go index aade13c936ed..b3d3cc41487b 100644 --- a/internal/services/automation/registration.go +++ b/internal/services/automation/registration.go @@ -8,6 +8,18 @@ import ( type Registration struct{} var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +var _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{} + +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{} +} + +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + AutomationConnectionTypeResource{}, + HybridRunbookWorkerGroupResource{}, + } +} func (r Registration) AssociatedGitHubLabel() string { return "service/automation" diff --git a/internal/services/automation/resourceids.go b/internal/services/automation/resourceids.go index e73f570e098b..55b7f0fdd235 100644 --- a/internal/services/automation/resourceids.go +++ b/internal/services/automation/resourceids.go @@ -1,6 +1,7 @@ package automation //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Connection -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connections/connection1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ConnectionType -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/type1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AutomationAccount -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Certificate -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/certificates/cert1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Credential -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/credentials/cred1 diff --git a/internal/services/automation/transition.go b/internal/services/automation/transition.go new file mode 100644 index 000000000000..abffe49b9038 --- /dev/null +++ b/internal/services/automation/transition.go @@ -0,0 +1,50 @@ +package automation + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +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) + } +} + +func expandTags(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 +} + +func flattenTags(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 { + output[i] = v + } + + return output +} + +func flattenAndSetTags(d *pluginsdk.ResourceData, tagMap map[string]string) error { + flattened := flattenTags(tagMap) + if err := d.Set("tags", flattened); err != nil { + return fmt.Errorf("setting `tags`: %s", err) + } + + return nil +} diff --git a/internal/services/automation/validate/connection_type_id.go b/internal/services/automation/validate/connection_type_id.go new file mode 100644 index 000000000000..bc9a00066983 --- /dev/null +++ b/internal/services/automation/validate/connection_type_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/automation/parse" +) + +func ConnectionTypeID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ConnectionTypeID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/automation/validate/connection_type_id_test.go b/internal/services/automation/validate/connection_type_id_test.go new file mode 100644 index 000000000000..ba083425d8d5 --- /dev/null +++ b/internal/services/automation/validate/connection_type_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestConnectionTypeID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing AutomationAccountName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/", + Valid: false, + }, + + { + // missing value for AutomationAccountName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Automation/automationAccounts/account1/connectionTypes/type1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.AUTOMATION/AUTOMATIONACCOUNTS/ACCOUNT1/CONNECTIONTYPES/TYPE1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ConnectionTypeID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/automation/validate/connection_type_name.go b/internal/services/automation/validate/connection_type_name.go new file mode 100644 index 000000000000..8a02e48ddceb --- /dev/null +++ b/internal/services/automation/validate/connection_type_name.go @@ -0,0 +1,19 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func ConnectionTypeName(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, append(errors, fmt.Errorf("expected type of %s to be string", k)) + } + + if !regexp.MustCompile(`^[\w\-]{1,128}$`).MatchString(v) { + errors = append(errors, fmt.Errorf("%s contain only letters, numbers hyphens and underscore. The value must be between 1 and 128 characters long", k)) + } + + return nil, errors +} diff --git a/internal/services/batch/batch_account_data_source_test.go b/internal/services/batch/batch_account_data_source_test.go index e42cd7cba39a..deb30a1e0c4b 100644 --- a/internal/services/batch/batch_account_data_source_test.go +++ b/internal/services/batch/batch_account_data_source_test.go @@ -125,11 +125,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" tags = { env = "test" @@ -246,11 +247,12 @@ resource "azurerm_user_assigned_identity" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" identity { type = "UserAssigned" identity_ids = [azurerm_user_assigned_identity.test.id] diff --git a/internal/services/batch/batch_account_resource.go b/internal/services/batch/batch_account_resource.go index 5aedcd47bfda..e2d2df2a78b8 100644 --- a/internal/services/batch/batch_account_resource.go +++ b/internal/services/batch/batch_account_resource.go @@ -3,10 +3,12 @@ package batch import ( "fmt" "log" + "strings" "time" "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2022-01-01/batch" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" @@ -58,6 +60,38 @@ func resourceBatchAccount() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ValidateFunc: storageValidate.StorageAccountID, + RequiredWith: []string{"storage_account_authentication_mode"}, + }, + + "storage_account_authentication_mode": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(batch.AutoStorageAuthenticationModeStorageKeys), + string(batch.AutoStorageAuthenticationModeBatchAccountManagedIdentity), + }, false), + RequiredWith: []string{"storage_account_id"}, + }, + + "storage_account_node_identity": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + RequiredWith: []string{"storage_account_id"}, + }, + + "allowed_authentication_modes": { + Type: pluginsdk.TypeSet, + Optional: true, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(batch.AuthenticationModeSharedKey), + string(batch.AuthenticationModeAAD), + string(batch.AuthenticationModeTaskAuthenticationToken), + }, false), + }, }, "pool_allocation_mode": { @@ -174,9 +208,10 @@ func resourceBatchAccountCreate(d *pluginsdk.ResourceData, meta interface{}) err parameters := batch.AccountCreateParameters{ Location: &location, AccountCreateProperties: &batch.AccountCreateProperties{ - PoolAllocationMode: batch.PoolAllocationMode(poolAllocationMode), - PublicNetworkAccess: batch.PublicNetworkAccessTypeEnabled, - Encryption: encryption, + PoolAllocationMode: batch.PoolAllocationMode(poolAllocationMode), + PublicNetworkAccess: batch.PublicNetworkAccessTypeEnabled, + Encryption: encryption, + AllowedAuthenticationModes: expandAllowedAuthenticationModes(d.Get("allowed_authentication_modes").(*pluginsdk.Set).List()), }, Identity: identity, Tags: tags.Expand(t), @@ -199,11 +234,37 @@ func resourceBatchAccountCreate(d *pluginsdk.ResourceData, meta interface{}) err } parameters.KeyVaultReference = keyVaultReference + + if v, ok := d.GetOk("allowed_authentication_modes"); ok { + authModes := v.(*pluginsdk.Set).List() + for _, mode := range authModes { + if batch.AuthenticationMode(mode.(string)) == batch.AuthenticationModeSharedKey { + return fmt.Errorf("creating %s: When setting pool allocation mode to UserSubscription, `allowed_authentication_modes=[StorageKeys]` is not allowed. ", id) + } + } + } + } + + authMode := d.Get("storage_account_authentication_mode").(string) + if batch.AutoStorageAuthenticationMode(authMode) == batch.AutoStorageAuthenticationModeBatchAccountManagedIdentity && + identity.Type == batch.ResourceIdentityTypeNone { + return fmt.Errorf(" storage_account_authentication_mode=`BatchAccountManagedIdentity` can only be set when identity.type is `SystemAssigned` or `UserAssigned`") } if storageAccountId != "" { + if authMode == "" { + return fmt.Errorf("`storage_account_authentication_mode` is required when `storage_account_id` ") + } parameters.AccountCreateProperties.AutoStorage = &batch.AutoStorageBaseProperties{ - StorageAccountID: &storageAccountId, + StorageAccountID: &storageAccountId, + AuthenticationMode: batch.AutoStorageAuthenticationMode(authMode), + } + } + + nodeIdentity := d.Get("storage_account_node_identity").(string) + if nodeIdentity != "" { + parameters.AccountCreateProperties.AutoStorage.NodeIdentityReference = &batch.ComputeNodeIdentityReference{ + ResourceID: utils.String(nodeIdentity), } } @@ -258,11 +319,17 @@ func resourceBatchAccountRead(d *pluginsdk.ResourceData, meta interface{}) error if props := resp.AccountProperties; props != nil { d.Set("account_endpoint", props.AccountEndpoint) - accountID := "" if autoStorage := props.AutoStorage; autoStorage != nil { - accountID = *autoStorage.StorageAccountID + d.Set("storage_account_id", *autoStorage.StorageAccountID) + d.Set("storage_account_authentication_mode", autoStorage.AuthenticationMode) + + if autoStorage.NodeIdentityReference != nil { + d.Set("storage_account_node_identity", autoStorage.NodeIdentityReference.ResourceID) + } + } else { + d.Set("storage_account_authentication_mode", "") + d.Set("storage_account_id", "") } - d.Set("storage_account_id", accountID) if props.PublicNetworkAccess != "" { d.Set("public_network_access_enabled", props.PublicNetworkAccess == batch.PublicNetworkAccessTypeEnabled) @@ -273,9 +340,14 @@ func resourceBatchAccountRead(d *pluginsdk.ResourceData, meta interface{}) error if err := d.Set("encryption", flattenEncryption(props.Encryption)); err != nil { return fmt.Errorf("setting `encryption`: %+v", err) } + + if err := d.Set("allowed_authentication_modes", flattenAllowedAuthenticationModes(props.AllowedAuthenticationModes)); err != nil { + return fmt.Errorf("setting `allowed_authentication_modes`: %+v", err) + } } - if d.Get("pool_allocation_mode").(string) == string(batch.PoolAllocationModeBatchService) { + if d.Get("pool_allocation_mode").(string) == string(batch.PoolAllocationModeBatchService) && + isShardKeyAllowed(d.Get("allowed_authentication_modes").(*pluginsdk.Set).List()) { keys, err := client.GetKeys(ctx, id.ResourceGroup, id.BatchAccountName) if err != nil { return fmt.Errorf("Cannot read keys for Batch account %q (resource group %q): %v", id.BatchAccountName, id.ResourceGroup, err) @@ -299,6 +371,7 @@ func resourceBatchAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) err if err != nil { return err } + t := d.Get("tags").(map[string]interface{}) identity, err := expandBatchAccountIdentity(d.Get("identity").([]interface{})) @@ -317,6 +390,16 @@ func resourceBatchAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) err Tags: tags.Expand(t), } + if d.HasChange("allowed_authentication_modes") { + allowedAuthModes := d.Get("allowed_authentication_modes").(*pluginsdk.Set).List() + if len(allowedAuthModes) == 0 { + parameters.AllowedAuthenticationModes = &[]batch.AuthenticationMode{} // remove all modes need explicit set it to empty array not nil + } else { + parameters.AllowedAuthenticationModes = expandAllowedAuthenticationModes(d.Get("allowed_authentication_modes").(*pluginsdk.Set).List()) + } + + } + if d.HasChange("storage_account_id") { if v, ok := d.GetOk("storage_account_id"); ok { parameters.AccountUpdateProperties.AutoStorage = &batch.AutoStorageBaseProperties{ @@ -330,6 +413,27 @@ func resourceBatchAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) err } } + authMode := d.Get("storage_account_authentication_mode").(string) + if batch.AutoStorageAuthenticationMode(authMode) == batch.AutoStorageAuthenticationModeBatchAccountManagedIdentity && + identity.Type == batch.ResourceIdentityTypeNone { + return fmt.Errorf(" storage_account_authentication_mode=`BatchAccountManagedIdentity` can only be set when identity.type is `SystemAssigned` or `UserAssigned`") + } + + storageAccountId := d.Get("storage_account_id").(string) + if storageAccountId != "" { + parameters.AutoStorage = &batch.AutoStorageBaseProperties{ + StorageAccountID: &storageAccountId, + AuthenticationMode: batch.AutoStorageAuthenticationMode(authMode), + } + } + + nodeIdentity := d.Get("storage_account_node_identity").(string) + if nodeIdentity != "" { + parameters.AutoStorage.NodeIdentityReference = &batch.ComputeNodeIdentityReference{ + ResourceID: utils.String(nodeIdentity), + } + } + if _, err = client.Update(ctx, id.ResourceGroup, id.BatchAccountName, parameters); err != nil { return fmt.Errorf("updating Batch account %q (Resource Group %q): %+v", id.BatchAccountName, id.ResourceGroup, err) } @@ -429,6 +533,30 @@ func expandEncryption(e []interface{}) *batch.EncryptionProperties { return &encryptionProperty } +func expandAllowedAuthenticationModes(input []interface{}) *[]batch.AuthenticationMode { + if len(input) == 0 { + return nil + } + + allowedAuthModes := make([]batch.AuthenticationMode, 0) + for _, mode := range input { + allowedAuthModes = append(allowedAuthModes, batch.AuthenticationMode(mode.(string))) + } + return &allowedAuthModes +} + +func flattenAllowedAuthenticationModes(input *[]batch.AuthenticationMode) []string { + if input == nil || len(*input) == 0 { + return []string{} + } + + allowedAuthModes := make([]string, 0) + for _, mode := range *input { + allowedAuthModes = append(allowedAuthModes, string(mode)) + } + return allowedAuthModes +} + func flattenEncryption(encryptionProperties *batch.EncryptionProperties) []interface{} { if encryptionProperties == nil || encryptionProperties.KeySource == batch.KeySourceMicrosoftBatch { return []interface{}{} @@ -440,3 +568,15 @@ func flattenEncryption(encryptionProperties *batch.EncryptionProperties) []inter }, } } + +func isShardKeyAllowed(input []interface{}) bool { + if len(input) == 0 { + return false + } + for _, authMod := range input { + if strings.EqualFold(authMod.(string), string(batch.AuthenticationModeSharedKey)) { + return true + } + } + return false +} diff --git a/internal/services/batch/batch_account_resource_test.go b/internal/services/batch/batch_account_resource_test.go index 8cbdce1b035c..bb9637f1acd0 100644 --- a/internal/services/batch/batch_account_resource_test.go +++ b/internal/services/batch/batch_account_resource_test.go @@ -174,6 +174,72 @@ func TestAccBatchAccount_cmk(t *testing.T) { }) } +func TestAccBatchAccount_authenticationModes(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_batch_account", "test") + r := BatchAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.authenticationModes(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("allowed_authentication_modes.#").HasValue("3"), + ), + }, + }) +} + +func TestAccBatchAccount_authenticationModesUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_batch_account", "test") + r := BatchAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.authenticationModes(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("allowed_authentication_modes.#").HasValue("3"), + ), + }, + { + Config: r.authenticationModesUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("allowed_authentication_modes.#").HasValue("0")), + }, + }) +} + +func TestAccBatchAccount_autoStorage(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_batch_account", "test") + r := BatchAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoStorage(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("storage_account_authentication_mode").HasValue("StorageKeys"), + ), + }, + }) +} + +func TestAccBatchAccount_autoStorageBatchAuthMode(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_batch_account", "test") + r := BatchAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoStorageBatchAuthMode(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("storage_account_authentication_mode").HasValue("BatchAccountManagedIdentity"), + ), + }, + }) +} + func TestAccBatchAccount_removeStorageAccount(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_batch_account", "test") r := BatchAccountResource{} @@ -303,11 +369,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" public_network_access_enabled = false @@ -338,11 +405,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" public_network_access_enabled = false @@ -461,11 +529,12 @@ resource "azurerm_user_assigned_identity" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" identity { type = "UserAssigned" identity_ids = [azurerm_user_assigned_identity.test.id] @@ -508,11 +577,12 @@ resource "azurerm_user_assigned_identity" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" identity { type = "UserAssigned" identity_ids = [azurerm_user_assigned_identity.test.id] @@ -604,3 +674,148 @@ resource "azurerm_batch_account" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString) } + +func (BatchAccountResource) authenticationModes(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-batch-%d" + location = "%s" +} + +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 = "BatchService" + allowed_authentication_modes = [ + "AAD", + "SharedKey", + "TaskAuthenticationToken" + ] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + +func (BatchAccountResource) authenticationModesUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-batch-%d" + location = "%s" +} + +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 = "BatchService" + allowed_authentication_modes = [] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + +func (BatchAccountResource) autoStorage(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-batch-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "testaccsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctest%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_batch_account" "test" { + name = "testaccbatch%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + storage_account_id = azurerm_storage_account.test.id + storage_account_authentication_mode = "StorageKeys" + storage_account_node_identity = azurerm_user_assigned_identity.test.id + pool_allocation_mode = "BatchService" + allowed_authentication_modes = [ + "AAD", + "SharedKey", + "TaskAuthenticationToken" + ] +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, data.RandomString, data.RandomString) +} + +func (BatchAccountResource) autoStorageBatchAuthMode(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "testaccRG-batch-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "testaccsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctest%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_batch_account" "test" { + name = "testaccbatch%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + storage_account_id = azurerm_storage_account.test.id + storage_account_authentication_mode = "BatchAccountManagedIdentity" + pool_allocation_mode = "BatchService" + allowed_authentication_modes = [ + "AAD", + "SharedKey", + "TaskAuthenticationToken" + ] + + identity { + type = "SystemAssigned" + } +} +`, data.RandomInteger, data.Locations.Secondary, data.RandomString, data.RandomString, data.RandomString) +} diff --git a/internal/services/batch/batch_application_data_source_test.go b/internal/services/batch/batch_application_data_source_test.go index 13515dbfe111..014aa6919938 100644 --- a/internal/services/batch/batch_application_data_source_test.go +++ b/internal/services/batch/batch_application_data_source_test.go @@ -44,11 +44,12 @@ resource "azurerm_storage_account" "test" { } 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 + 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 + storage_account_authentication_mode = "StorageKeys" } resource "azurerm_batch_application" "test" { diff --git a/internal/services/batch/batch_application_resource_test.go b/internal/services/batch/batch_application_resource_test.go index e89e11f169d7..883d03c6eac3 100644 --- a/internal/services/batch/batch_application_resource_test.go +++ b/internal/services/batch/batch_application_resource_test.go @@ -101,11 +101,12 @@ resource "azurerm_storage_account" "test" { } 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 + 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 + storage_account_authentication_mode = "StorageKeys" } resource "azurerm_batch_application" "test" { @@ -137,11 +138,12 @@ resource "azurerm_storage_account" "test" { } resource "azurerm_batch_account" "test" { - name = "acctestba%[3]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 + name = "acctestba%[3]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 + storage_account_authentication_mode = "StorageKeys" } resource "azurerm_batch_application" "test" { diff --git a/internal/services/batch/batch_pool.go b/internal/services/batch/batch_pool.go index 85faf6ee25e1..f4b9784d5973 100644 --- a/internal/services/batch/batch_pool.go +++ b/internal/services/batch/batch_pool.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2022-01-01/batch" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -252,15 +253,13 @@ func flattenBatchPoolContainerRegistry(d *pluginsdk.ResourceData, armContainerRe } if userName := armContainerRegistry.UserName; userName != nil { result["user_name"] = *userName + // Locate the password only if user_name is defined + result["password"] = findBatchPoolContainerRegistryPassword(d, result["registry_server"].(string), result["user_name"].(string)) } - - // 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 + if identity := armContainerRegistry.IdentityReference; identity != nil { + result["user_assigned_identity_id"] = identity.ResourceID } - result["password"] = findBatchPoolContainerRegistryPassword(d, result["registry_server"].(string), result["user_name"].(string)) - return result } @@ -363,11 +362,23 @@ func expandBatchPoolContainerRegistry(ref map[string]interface{}) (*batch.Contai 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)), + containerRegistry := batch.ContainerRegistry{} + + if v := ref["registry_server"]; v != nil && v != "" { + containerRegistry.RegistryServer = pointer.FromString(v.(string)) + } + if v := ref["user_name"]; v != nil && v != "" { + containerRegistry.UserName = pointer.FromString(v.(string)) } + if v := ref["password"]; v != nil && v != "" { + containerRegistry.Password = pointer.FromString(v.(string)) + } + if v := ref["user_assigned_identity_id"]; v != nil && v != "" { + containerRegistry.IdentityReference = &batch.ComputeNodeIdentityReference{ + ResourceID: pointer.FromString(v.(string)), + } + } + return &containerRegistry, nil } diff --git a/internal/services/batch/batch_pool_data_source.go b/internal/services/batch/batch_pool_data_source.go index 35f859e26c07..a72e7eba4851 100644 --- a/internal/services/batch/batch_pool_data_source.go +++ b/internal/services/batch/batch_pool_data_source.go @@ -144,6 +144,10 @@ func dataSourceBatchPool() *pluginsdk.Resource { Type: pluginsdk.TypeString, Computed: true, }, + "user_assigned_identity_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, "user_name": { Type: pluginsdk.TypeString, Computed: true, diff --git a/internal/services/batch/batch_pool_data_source_test.go b/internal/services/batch/batch_pool_data_source_test.go index 7416b0450d24..d0b8e19c8be8 100644 --- a/internal/services/batch/batch_pool_data_source_test.go +++ b/internal/services/batch/batch_pool_data_source_test.go @@ -94,11 +94,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" tags = { env = "test" diff --git a/internal/services/batch/batch_pool_resource.go b/internal/services/batch/batch_pool_resource.go index 3094dabea019..fe71ed78677c 100644 --- a/internal/services/batch/batch_pool_resource.go +++ b/internal/services/batch/batch_pool_resource.go @@ -10,6 +10,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2022-01-01/batch" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" @@ -159,15 +160,22 @@ func resourceBatchPool() *pluginsdk.Resource { ForceNew: true, ValidateFunc: validation.StringIsNotEmpty, }, + "user_assigned_identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + Description: "The User Assigned Identity to use for Container Registry access.", + }, "user_name": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validation.StringIsNotEmpty, }, "password": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ForceNew: true, Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, diff --git a/internal/services/batch/batch_pool_resource_test.go b/internal/services/batch/batch_pool_resource_test.go index 07b51ccd63f1..893911d382c0 100644 --- a/internal/services/batch/batch_pool_resource_test.go +++ b/internal/services/batch/batch_pool_resource_test.go @@ -334,13 +334,13 @@ func TestAccBatchPool_validateResourceFileWithoutSource(t *testing.T) { }) } -func TestAccBatchPool_container(t *testing.T) { +func TestAccBatchPool_containerWithUser(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_batch_pool", "test") r := BatchPoolResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.containerConfiguration(data), + Config: r.containerConfigurationWithRegistryUser(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("container_configuration.0.type").HasValue("DockerCompatible"), @@ -349,6 +349,7 @@ func TestAccBatchPool_container(t *testing.T) { check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.registry_server").HasValue("myContainerRegistry.azurecr.io"), check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.user_name").HasValue("myUserName"), check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.password").HasValue("myPassword"), + check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.user_assigned_identity_id").IsEmpty(), ), }, data.ImportStep( @@ -358,6 +359,29 @@ func TestAccBatchPool_container(t *testing.T) { }) } +func TestAccBatchPool_containerWithUAMI(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_batch_pool", "test") + r := BatchPoolResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.containerConfigurationWithRegistryUAMI(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("container_configuration.0.type").HasValue("DockerCompatible"), + check.That(data.ResourceName).Key("container_configuration.0.container_image_names.#").HasValue("1"), + check.That(data.ResourceName).Key("container_configuration.0.container_registries.#").HasValue("1"), + check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.registry_server").HasValue("myContainerRegistry.azurecr.io"), + check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.user_name").IsEmpty(), + check.That(data.ResourceName).Key("container_configuration.0.container_registries.0.user_assigned_identity_id").IsSet(), + ), + }, + data.ImportStep( + "stop_pending_resize_operation", + ), + }) +} + func TestAccBatchPool_validateResourceFileWithMultipleSources(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_batch_pool", "test") r := BatchPoolResource{} @@ -502,11 +526,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" tags = { env = "test" @@ -562,11 +587,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" tags = { env = "test" @@ -620,11 +646,12 @@ resource "azurerm_storage_account" "test" { } 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 = "BatchService" - storage_account_id = azurerm_storage_account.test.id + name = "testaccbatch%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 + storage_account_authentication_mode = "StorageKeys" tags = { env = "test" @@ -1268,7 +1295,7 @@ resource "azurerm_batch_pool" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) } -func (BatchPoolResource) containerConfiguration(data acceptance.TestData) string { +func (BatchPoolResource) containerConfigurationWithRegistryUser(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -1323,6 +1350,73 @@ resource "azurerm_batch_pool" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomString) } +func (BatchPoolResource) containerConfigurationWithRegistryUAMI(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "testaccbatch%d" + location = "%s" +} + +resource "azurerm_user_assigned_identity" "test" { + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + name = "testaccuami%d" +} + +resource "azurerm_container_registry" "test" { + name = "testregistry%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Basic" + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } +} + +resource "azurerm_batch_account" "test" { + name = "testaccbatch%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_batch_pool" "test" { + name = "testaccpool%s" + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_batch_account.test.name + node_agent_sku_id = "batch.node.ubuntu 20.04" + vm_size = "Standard_A1" + + fixed_scale { + target_dedicated_nodes = 1 + } + + storage_image_reference { + publisher = "microsoft-azure-batch" + offer = "ubuntu-server-container" + sku = "20-04-lts" + version = "latest" + } + + container_configuration { + type = "DockerCompatible" + container_image_names = ["centos7"] + container_registries { + registry_server = "myContainerRegistry.azurecr.io" + user_assigned_identity_id = azurerm_user_assigned_identity.test.id + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomString, data.RandomString, data.RandomString) +} + func (BatchPoolResource) customImageConfiguration(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/blueprints/blueprint_definition_data_source.go b/internal/services/blueprints/blueprint_definition_data_source.go index e8285ed16c8b..d76986f3bbbc 100644 --- a/internal/services/blueprints/blueprint_definition_data_source.go +++ b/internal/services/blueprints/blueprint_definition_data_source.go @@ -77,6 +77,7 @@ func dataSourceBlueprintDefinition() *pluginsdk.Resource { func dataSourceBlueprintDefinitionRead(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Blueprints.BlueprintsClient + publishedClient := meta.(*clients.Client).Blueprints.PublishedBlueprintsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -112,9 +113,20 @@ func dataSourceBlueprintDefinitionRead(d *pluginsdk.ResourceData, meta interface d.Set("target_scope", resp.TargetScope) - if resp.Versions != nil { - d.Set("versions", resp.Versions) + versionList := make([]string, 0) + versions, err := publishedClient.List(ctx, scope, name) + if err != nil { + return fmt.Errorf("listing blue print versions for %s error: %+v", *resp.ID, err) } + for _, version := range versions.Values() { + if version.PublishedBlueprintProperties == nil || version.Name == nil { + continue + } + versionList = append(versionList, *version.Name) + } + + d.Set("versions", versionList) + return nil } diff --git a/internal/services/blueprints/blueprint_definition_data_source_test.go b/internal/services/blueprints/blueprint_definition_data_source_test.go index 30e640a4cb78..4f7f836a8a06 100644 --- a/internal/services/blueprints/blueprint_definition_data_source_test.go +++ b/internal/services/blueprints/blueprint_definition_data_source_test.go @@ -21,6 +21,7 @@ func TestAccBlueprintDefinitionDataSource_basic(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("description").HasValue("Acceptance Test stub for Blueprints at Subscription"), check.That(data.ResourceName).Key("name").HasValue("testAcc_basicSubscription"), + check.That(data.ResourceName).Key("versions.#").HasValue("2"), check.That(data.ResourceName).Key("last_modified").Exists(), check.That(data.ResourceName).Key("target_scope").HasValue("subscription"), check.That(data.ResourceName).Key("time_created").Exists(), diff --git a/internal/services/bot/bot_channel_test.go b/internal/services/bot/bot_channel_test.go index 5c1afa88746a..e9fb39e20b71 100644 --- a/internal/services/bot/bot_channel_test.go +++ b/internal/services/bot/bot_channel_test.go @@ -17,10 +17,11 @@ func TestAccBotChannelsRegistration(t *testing.T) { "streamingEndpointEnabled": testAccBotChannelsRegistration_streamingEndpointEnabled, }, "bot": { - "basic": testAccBotServiceAzureBot_basic, - "completeUpdate": testAccBotServiceAzureBot_completeUpdate, - "msaAppType": testAccBotServiceAzureBot_msaAppType, - "requiresImport": testAccBotServiceAzureBot_requiresImport, + "basic": testAccBotServiceAzureBot_basic, + "completeUpdate": testAccBotServiceAzureBot_completeUpdate, + "msaAppType": testAccBotServiceAzureBot_msaAppType, + "requiresImport": testAccBotServiceAzureBot_requiresImport, + "streamingEndpointEnabled": testAccBotServiceAzureBot_streamingEndpointEnabled, }, "connection": { "basic": testAccBotConnection_basic, diff --git a/internal/services/bot/bot_service_azure_bot_resource_test.go b/internal/services/bot/bot_service_azure_bot_resource_test.go index 3196a0ca4926..23d3f710857b 100644 --- a/internal/services/bot/bot_service_azure_bot_resource_test.go +++ b/internal/services/bot/bot_service_azure_bot_resource_test.go @@ -88,6 +88,28 @@ func testAccBotServiceAzureBot_msaAppType(t *testing.T) { }) } +func testAccBotServiceAzureBot_streamingEndpointEnabled(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_bot_service_azure_bot", "test") + r := BotServiceAzureBotResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.steamingEndpointEnabled(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.steamingEndpointEnabled(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (t BotServiceAzureBotResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.BotServiceID(state.ID) if err != nil { @@ -223,3 +245,28 @@ resource "azurerm_bot_service_azure_bot" "test" { } `, data.RandomInteger, data.Locations.Primary) } + +func (BotServiceAzureBotResource) steamingEndpointEnabled(data acceptance.TestData, streamingEndpointEnabled bool) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_bot_service_azure_bot" "test" { + name = "acctestdf%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = "global" + sku = "F0" + microsoft_app_id = data.azurerm_client_config.current.client_id + streaming_endpoint_enabled = %[3]t +} +`, data.RandomInteger, data.Locations.Primary, streamingEndpointEnabled) +} diff --git a/internal/services/bot/bot_service_base_resource.go b/internal/services/bot/bot_service_base_resource.go index e8eaaa3ba8f7..1ad13f335327 100644 --- a/internal/services/bot/bot_service_base_resource.go +++ b/internal/services/bot/bot_service_base_resource.go @@ -120,6 +120,12 @@ func (br botBaseResource) arguments(fields map[string]*pluginsdk.Schema) map[str ValidateFunc: validation.StringIsNotEmpty, }, + "streaming_endpoint_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "tags": tags.Schema(), } @@ -173,6 +179,7 @@ func (br botBaseResource) createFunc(resourceName, botKind string) sdk.ResourceF DeveloperAppInsightsApplicationID: utils.String(metadata.ResourceData.Get("developer_app_insights_application_id").(string)), LuisAppIds: utils.ExpandStringSlice(metadata.ResourceData.Get("luis_app_ids").([]interface{})), LuisKey: utils.String(metadata.ResourceData.Get("luis_key").(string)), + IsStreamingSupported: utils.Bool(metadata.ResourceData.Get("streaming_endpoint_enabled").(bool)), }, Tags: tags.Expand(metadata.ResourceData.Get("tags").(map[string]interface{})), } @@ -290,6 +297,12 @@ func (br botBaseResource) readFunc() sdk.ResourceFunc { luisAppIds = *v } metadata.ResourceData.Set("luis_app_ids", utils.FlattenStringSlice(&luisAppIds)) + + streamingEndpointEnabled := false + if v := props.IsStreamingSupported; v != nil { + streamingEndpointEnabled = *v + } + metadata.ResourceData.Set("streaming_endpoint_enabled", streamingEndpointEnabled) } return nil @@ -359,6 +372,10 @@ func (br botBaseResource) updateFunc() sdk.ResourceFunc { existing.Properties.LuisKey = utils.String(metadata.ResourceData.Get("luis_key").(string)) } + if metadata.ResourceData.HasChange("streaming_endpoint_enabled") { + existing.Properties.IsStreamingSupported = utils.Bool(metadata.ResourceData.Get("streaming_endpoint_enabled").(bool)) + } + if _, err := client.Update(ctx, id.ResourceGroup, id.Name, existing); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } diff --git a/internal/services/cdn/cdn_endpoint_custom_domain_resource.go b/internal/services/cdn/cdn_endpoint_custom_domain_resource.go index 58a5a87119bc..3dc453544d50 100644 --- a/internal/services/cdn/cdn_endpoint_custom_domain_resource.go +++ b/internal/services/cdn/cdn_endpoint_custom_domain_resource.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2020-09-01/cdn" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" keyvaultClient "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/client" @@ -21,6 +22,112 @@ import ( ) func resourceArmCdnEndpointCustomDomain() *pluginsdk.Resource { + schema := map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.CdnEndpointCustomDomainName(), + }, + + "cdn_endpoint_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.EndpointID, + }, + + "host_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "cdn_managed_https": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "certificate_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.CertificateTypeShared), + string(cdn.CertificateTypeDedicated), + }, false), + }, + "protocol_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.ProtocolTypeServerNameIndication), + string(cdn.ProtocolTypeIPBased), + }, false), + }, + "tls_version": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.MinimumTLSVersionTLS10), + string(cdn.MinimumTLSVersionTLS12), + string(cdn.MinimumTLSVersionNone), + }, false), + Default: string(cdn.MinimumTLSVersionTLS12), + }, + }, + }, + ConflictsWith: []string{"user_managed_https"}, + }, + + "user_managed_https": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "tls_version": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.MinimumTLSVersionTLS10), + string(cdn.MinimumTLSVersionTLS12), + string(cdn.MinimumTLSVersionNone), + }, false), + Default: string(cdn.MinimumTLSVersionTLS12), + }, + }, + }, + ConflictsWith: []string{"cdn_managed_https"}, + }, + } + if !features.FourPointOhBeta() { + schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_certificate_id"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion, + ExactlyOneOf: []string{"user_managed_https.0.key_vault_certificate_id", "user_managed_https.0.key_vault_secret_id"}, + Deprecated: "This is deprecated in favor of `key_vault_secret_id` as the service is actually looking for a secret, not a certificate", + } + + schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_secret_id"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion, + ExactlyOneOf: []string{"user_managed_https.0.key_vault_certificate_id", "user_managed_https.0.key_vault_secret_id"}, + } + } else { + schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_secret_id"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion, + } + } + return &pluginsdk.Resource{ Create: resourceArmCdnEndpointCustomDomainCreate, Read: resourceArmCdnEndpointCustomDomainRead, @@ -39,92 +146,7 @@ func resourceArmCdnEndpointCustomDomain() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(12 * time.Hour), }, - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.CdnEndpointCustomDomainName(), - }, - - "cdn_endpoint_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.EndpointID, - }, - - "host_name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "cdn_managed_https": { - Type: pluginsdk.TypeList, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "certificate_type": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(cdn.CertificateTypeShared), - string(cdn.CertificateTypeDedicated), - }, false), - }, - "protocol_type": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(cdn.ProtocolTypeServerNameIndication), - string(cdn.ProtocolTypeIPBased), - }, false), - }, - "tls_version": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - string(cdn.MinimumTLSVersionTLS10), - string(cdn.MinimumTLSVersionTLS12), - string(cdn.MinimumTLSVersionNone), - }, false), - Default: string(cdn.MinimumTLSVersionTLS12), - }, - }, - }, - ConflictsWith: []string{"user_managed_https"}, - }, - - "user_managed_https": { - Type: pluginsdk.TypeList, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "key_vault_certificate_id": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion, - }, - "tls_version": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - string(cdn.MinimumTLSVersionTLS10), - string(cdn.MinimumTLSVersionTLS12), - string(cdn.MinimumTLSVersionNone), - }, false), - Default: string(cdn.MinimumTLSVersionTLS12), - }, - }, - }, - ConflictsWith: []string{"cdn_managed_https"}, - }, - }, + Schema: schema, } } @@ -351,12 +373,21 @@ func resourceArmCdnEndpointCustomDomainRead(d *pluginsdk.ResourceData, meta inte case cdn.UserManagedHTTPSParameters: var isVersioned bool if b := d.Get("user_managed_https").([]interface{}); len(b) == 1 { - if certIdRaw := b[0].(map[string]interface{})["key_vault_certificate_id"].(string); certIdRaw != "" { - certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(certIdRaw) + b := b[0].(map[string]interface{}) + + secretIdRaw := b["key_vault_secret_id"].(string) + if !features.FourPointOhBeta() { + if secretIdRaw == "" { + secretIdRaw = b["key_vault_certificate_id"].(string) + } + } + + if secretIdRaw != "" { + id, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(secretIdRaw) if err != nil { - return fmt.Errorf("parsing Key Vault Certificate Id %q: %v", certIdRaw, err) + return fmt.Errorf("parsing Key Vault Secret Id %q: %v", secretIdRaw, err) } - isVersioned = certId.Version != "" + isVersioned = id.Version != "" } } settings, err := flattenArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx, params, keyVaultsClient, isVersioned) @@ -422,17 +453,24 @@ func expandArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Contex raw := input[0].(map[string]interface{}) - keyVaultCertId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(raw["key_vault_certificate_id"].(string)) + idLiteral := raw["key_vault_secret_id"].(string) + if !features.FourPointOhBeta() { + if idLiteral == "" { + idLiteral = raw["key_vault_certificate_id"].(string) + } + } + + keyVaultSecretId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(idLiteral) if err != nil { return nil, err } - keyVaultIdRaw, err := clients.KeyVault.KeyVaultIDFromBaseUrl(ctx, clients.Resource, keyVaultCertId.KeyVaultBaseUrl) + keyVaultIdRaw, err := clients.KeyVault.KeyVaultIDFromBaseUrl(ctx, clients.Resource, keyVaultSecretId.KeyVaultBaseUrl) if err != nil { - return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultCertId.KeyVaultBaseUrl, err) + return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultSecretId.KeyVaultBaseUrl, err) } if keyVaultIdRaw == nil { - return nil, fmt.Errorf("unexpected nil Key Vault ID retrieved at URL %q", keyVaultCertId.KeyVaultBaseUrl) + return nil, fmt.Errorf("unexpected nil Key Vault ID retrieved at URL %q", keyVaultSecretId.KeyVaultBaseUrl) } keyVaultId, err := keyvaultParse.VaultID(*keyVaultIdRaw) if err != nil { @@ -445,8 +483,8 @@ func expandArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Contex SubscriptionID: &keyVaultId.SubscriptionId, ResourceGroupName: &keyVaultId.ResourceGroup, VaultName: &keyVaultId.Name, - SecretName: &keyVaultCertId.Name, - SecretVersion: &keyVaultCertId.Version, + SecretName: &keyVaultSecretId.Name, + SecretVersion: &keyVaultSecretId.Version, UpdateRule: utils.String("NoAction"), DeleteRule: utils.String("NoAction"), }, @@ -507,31 +545,50 @@ func flattenArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Conte keyVaultId := keyvaultParse.NewVaultID(subscriptionId, resourceGroupName, vaultName) keyVaultBaseUrl, err := keyVaultsClient.BaseUriForKeyVault(ctx, keyVaultId) if err != nil { - return nil, fmt.Errorf("looking up Key Vault Certificate %q vault url from id %q: %+v", vaultName, keyVaultId, err) + return nil, fmt.Errorf("looking up Key Vault Secret %q vault url from id %q: %+v", vaultName, keyVaultId, err) } - cert, err := keyVaultsClient.ManagementClient.GetCertificate(ctx, *keyVaultBaseUrl, secretName, secretVersion) + + secret, err := keyVaultsClient.ManagementClient.GetSecret(ctx, *keyVaultBaseUrl, secretName, secretVersion) if err != nil { return nil, err } - if cert.ID == nil { - return nil, fmt.Errorf("unexpected null Key Vault Certificate retrieved for Key Vault %s / Secret Name %s / Secret Version %s", keyVaultId, secretName, secretVersion) + if secret.ID == nil { + return nil, fmt.Errorf("unexpected null Key Vault Secret retrieved for Key Vault %s / Secret Name %s / Secret Version %s", keyVaultId, secretName, secretVersion) } - certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*cert.ID) + secretId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*secret.ID) if err != nil { return nil, err } - certIdLiteral := certId.ID() + secretIdLiteral := secretId.ID() if !isVersioned { - certIdLiteral = certId.VersionlessID() + secretIdLiteral = secretId.VersionlessID() } - return []interface{}{ - map[string]interface{}{ - "key_vault_certificate_id": certIdLiteral, - "tls_version": string(input.MinimumTLSVersion), - }, - }, nil + m := map[string]interface{}{ + "key_vault_secret_id": secretIdLiteral, + "tls_version": string(input.MinimumTLSVersion), + } + + if features.FourPointOhBeta() { + return []interface{}{m}, nil + } + + // We try to retrieve the certificate with the given secret name and version. If it returns error, then we tolerate the error and simply setting empty string for the certificate id. + // As in this case, the users might be using a secret rather than a certificate. + var certIdLiteral string + cert, err := keyVaultsClient.ManagementClient.GetCertificate(ctx, *keyVaultBaseUrl, secretName, secretVersion) + if err == nil && cert.ID != nil { + certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*cert.ID) + if err == nil { + } + certIdLiteral = certId.ID() + if !isVersioned { + certIdLiteral = certId.VersionlessID() + } + } + m["key_vault_certificate_id"] = certIdLiteral + return []interface{}{m}, nil } func enableArmCdnEndpointCustomDomainHttps(ctx context.Context, client *cdn.CustomDomainsClient, id parse.CustomDomainId, params cdn.BasicCustomDomainHTTPSParameters) error { diff --git a/internal/services/cdn/cdn_endpoint_custom_domain_resource_test.go b/internal/services/cdn/cdn_endpoint_custom_domain_resource_test.go index 67d4e9e17fdd..cd85001600ef 100644 --- a/internal/services/cdn/cdn_endpoint_custom_domain_resource_test.go +++ b/internal/services/cdn/cdn_endpoint_custom_domain_resource_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -85,7 +86,7 @@ func TestAccCdnEndpointCustomDomain_httpsCdn(t *testing.T) { }) } -func TestAccCdnEndpointCustomDomain_httpsUserManaged(t *testing.T) { +func TestAccCdnEndpointCustomDomain_httpsUserManagedCertificate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_cdn_endpoint_custom_domain", "test") r := NewCdnEndpointCustomDomainResource(os.Getenv("ARM_TEST_DNS_ZONE_RESOURCE_GROUP_NAME"), os.Getenv("ARM_TEST_DNS_ZONE_NAME")) @@ -95,15 +96,56 @@ func TestAccCdnEndpointCustomDomain_httpsUserManaged(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.httpsUserManaged(data), + Config: r.httpsUserManagedCertificate(data), Check: resource.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - // The "key_vault_certificate_id" is skipped here since during import, there is no knowledge about whether users want - // versioned or versionless certificate id. That means the imported "key_vault_certificate_id" is what it is at the + // The "key_vault_secret_id" is skipped here since during import, there is no knowledge about whether users want + // versioned or versionless certificate id. That means the imported "key_vault_secret_id" is what it is at the // remote API representation, which might be different than it as defined in the configuration. - data.ImportStep("user_managed_https.0.key_vault_certificate_id"), + data.ImportStep("user_managed_https.0.key_vault_secret_id", "user_managed_https.0.key_vault_certificate_id"), + }) +} + +func TestAccCdnEndpointCustomDomain_httpsUserManagedCertificateDeprecated(t *testing.T) { + if features.FourPointOhBeta() { + t.Skipf("This test is skipped since v4.0") + } + data := acceptance.BuildTestData(t, "azurerm_cdn_endpoint_custom_domain", "test") + + r := NewCdnEndpointCustomDomainResource(os.Getenv("ARM_TEST_DNS_ZONE_RESOURCE_GROUP_NAME"), os.Getenv("ARM_TEST_DNS_ZONE_NAME")) + r.CertificateP12 = os.Getenv("ARM_TEST_DNS_CERTIFICATE") + r.SubDomainName = os.Getenv("ARM_TEST_DNS_SUBDOMAIN_NAME") + r.preCheckUserManagedCertificate(t) + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.httpsUserManagedCertificateDeprecated(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_managed_https.0.key_vault_secret_id", "user_managed_https.0.key_vault_certificate_id"), + }) +} + +func TestAccCdnEndpointCustomDomain_httpsUserManagedSecret(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_endpoint_custom_domain", "test") + + r := NewCdnEndpointCustomDomainResource(os.Getenv("ARM_TEST_DNS_ZONE_RESOURCE_GROUP_NAME"), os.Getenv("ARM_TEST_DNS_ZONE_NAME")) + r.CertificateP12 = os.Getenv("ARM_TEST_DNS_CERTIFICATE") + r.SubDomainName = os.Getenv("ARM_TEST_DNS_SUBDOMAIN_NAME") + r.preCheckUserManagedCertificate(t) + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.httpsUserManagedSecret(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_managed_https.0.key_vault_secret_id", "user_managed_https.0.key_vault_certificate_id"), }) } @@ -131,12 +173,12 @@ func TestAccCdnEndpointCustomDomain_httpsUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.httpsUserManaged(data), + Config: r.httpsUserManagedCertificate(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("user_managed_https.0.key_vault_certificate_id"), + data.ImportStep("user_managed_https.0.key_vault_secret_id", "user_managed_https.0.key_vault_certificate_id"), { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( @@ -241,7 +283,7 @@ resource "azurerm_cdn_endpoint_custom_domain" "test" { `, template, data.RandomIntOfLength(8)) } -func (r CdnEndpointCustomDomainResource) httpsUserManaged(data acceptance.TestData) string { +func (r CdnEndpointCustomDomainResource) httpsUserManagedBase(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %[1]s @@ -275,6 +317,8 @@ resource "azurerm_key_vault" "test" { secret_permissions = [ "Get", "Set", + "Delete", + "Purge", ] } access_policy { @@ -290,6 +334,52 @@ resource "azurerm_key_vault" "test" { ] } } +`, template, data.RandomIntOfLength(8)) +} + +func (r CdnEndpointCustomDomainResource) httpsUserManagedCertificate(data acceptance.TestData) string { + template := r.httpsUserManagedBase(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_key_vault_certificate" "test" { + name = "testkeyvaultcert-%[2]d" + key_vault_id = azurerm_key_vault.test.id + certificate { + contents = file("%[3]s") + password = "" + } + 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_cdn_endpoint_custom_domain" "test" { + name = "testcustomdomain-%[2]d" + cdn_endpoint_id = azurerm_cdn_endpoint.test.id + host_name = "${azurerm_dns_cname_record.test.name}.${data.azurerm_dns_zone.test.name}" + user_managed_https { + key_vault_secret_id = azurerm_key_vault_certificate.test.secret_id + } +} +`, template, data.RandomIntOfLength(8), r.CertificateP12) +} + +func (r CdnEndpointCustomDomainResource) httpsUserManagedCertificateDeprecated(data acceptance.TestData) string { + template := r.httpsUserManagedBase(data) + return fmt.Sprintf(` +%[1]s + resource "azurerm_key_vault_certificate" "test" { name = "testkeyvaultcert-%[2]d" key_vault_id = azurerm_key_vault.test.id @@ -323,6 +413,28 @@ resource "azurerm_cdn_endpoint_custom_domain" "test" { `, template, data.RandomIntOfLength(8), r.CertificateP12) } +func (r CdnEndpointCustomDomainResource) httpsUserManagedSecret(data acceptance.TestData) string { + template := r.httpsUserManagedBase(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_key_vault_secret" "test" { + name = "testkeyvaultsecret-%[2]d" + key_vault_id = azurerm_key_vault.test.id + content_type = "application/x-pkcs12" + value = file("%[3]s") +} +resource "azurerm_cdn_endpoint_custom_domain" "test" { + name = "testcustomdomain-%[2]d" + cdn_endpoint_id = azurerm_cdn_endpoint.test.id + host_name = "${azurerm_dns_cname_record.test.name}.${data.azurerm_dns_zone.test.name}" + user_managed_https { + key_vault_secret_id = azurerm_key_vault_secret.test.id + } +} +`, template, data.RandomIntOfLength(8), r.CertificateP12) +} + func (r CdnEndpointCustomDomainResource) template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/cdn/cdn_frontdoor_endpoint_resource_test.go b/internal/services/cdn/cdn_frontdoor_endpoint_resource_test.go index 1a46e9230f8b..eb4ef334b578 100644 --- a/internal/services/cdn/cdn_frontdoor_endpoint_resource_test.go +++ b/internal/services/cdn/cdn_frontdoor_endpoint_resource_test.go @@ -175,8 +175,8 @@ resource "azurerm_resource_group" "test" { resource "azurerm_cdn_frontdoor_profile" "test" { name = "acctest-cdnfdprofile-%d" - sku_name = "Standard_AzureFrontDoor" resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard_AzureFrontDoor" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go new file mode 100644 index 000000000000..7dad3bf00b0b --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource.go @@ -0,0 +1,1094 @@ +package cdn + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/frontdoor/mgmt/2020-11-01/frontdoor" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceCdnFrontDoorFirewallPolicy() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceCdnFrontDoorFirewallPolicyCreate, + Read: resourceCdnFrontDoorFirewallPolicyRead, + Update: resourceCdnFrontDoorFirewallPolicyUpdate, + Delete: resourceCdnFrontDoorFirewallPolicyDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FrontDoorFirewallPolicyID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorFirewallPolicyName, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "sku_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.SkuNameStandardAzureFrontDoor), + string(frontdoor.SkuNamePremiumAzureFrontDoor), + }, false), + }, + + "mode": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.PolicyModeDetection), + string(frontdoor.PolicyModePrevention), + }, false), + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "redirect_url": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.IsURLWithScheme([]string{"http", "https"}), + }, + + "custom_block_response_status_code": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntInSlice([]int{ + 200, + 403, + 405, + 406, + 429, + }), + }, + + "custom_block_response_body": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsBase64, + }, + + "custom_rule": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "priority": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 1, + }, + + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.RuleTypeMatchRule), + string(frontdoor.RuleTypeRateLimitRule), + }, false), + }, + + "rate_limit_duration_in_minutes": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 1, + }, + + "rate_limit_threshold": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 10, + }, + + "action": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ActionTypeAllow), + string(frontdoor.ActionTypeBlock), + string(frontdoor.ActionTypeLog), + string(frontdoor.ActionTypeRedirect), + }, false), + }, + + "match_condition": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 10, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "match_variable": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.MatchVariableCookies), + string(frontdoor.MatchVariablePostArgs), + string(frontdoor.MatchVariableQueryString), + string(frontdoor.MatchVariableRemoteAddr), + string(frontdoor.MatchVariableRequestBody), + string(frontdoor.MatchVariableRequestHeader), + string(frontdoor.MatchVariableRequestMethod), + string(frontdoor.MatchVariableRequestURI), + string(frontdoor.MatchVariableSocketAddr), + }, false), + }, + + "match_values": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 600, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + }, + + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.OperatorAny), + string(frontdoor.OperatorBeginsWith), + string(frontdoor.OperatorContains), + string(frontdoor.OperatorEndsWith), + string(frontdoor.OperatorEqual), + string(frontdoor.OperatorGeoMatch), + string(frontdoor.OperatorGreaterThan), + string(frontdoor.OperatorGreaterThanOrEqual), + string(frontdoor.OperatorIPMatch), + string(frontdoor.OperatorLessThan), + string(frontdoor.OperatorLessThanOrEqual), + string(frontdoor.OperatorRegEx), + }, false), + }, + + "selector": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "negation_condition": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "transforms": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 5, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.TransformTypeLowercase), + string(frontdoor.TransformTypeRemoveNulls), + string(frontdoor.TransformTypeTrim), + string(frontdoor.TransformTypeUppercase), + string(frontdoor.TransformTypeURLDecode), + string(frontdoor.TransformTypeURLEncode), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + + "managed_rule": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "version": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "action": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ActionTypeAllow), + string(frontdoor.ActionTypeLog), + string(frontdoor.ActionTypeBlock), + string(frontdoor.ActionTypeRedirect), + }, false), + }, + + "exclusion": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "match_variable": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionMatchVariableQueryStringArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestBodyPostArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestCookieNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestHeaderNames), + }, false), + }, + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorContains), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEndsWith), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEquals), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEqualsAny), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorStartsWith), + }, false), + }, + "selector": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "override": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "rule_group_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "exclusion": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "match_variable": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionMatchVariableQueryStringArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestBodyPostArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestCookieNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestHeaderNames), + }, false), + }, + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorContains), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEndsWith), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEquals), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEqualsAny), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorStartsWith), + }, false), + }, + "selector": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "rule": { + Type: pluginsdk.TypeList, + MaxItems: 1000, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "rule_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "exclusion": { + Type: pluginsdk.TypeList, + MaxItems: 100, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "match_variable": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionMatchVariableQueryStringArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestBodyPostArgNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestCookieNames), + string(frontdoor.ManagedRuleExclusionMatchVariableRequestHeaderNames), + }, false), + }, + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorContains), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEndsWith), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEquals), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorEqualsAny), + string(frontdoor.ManagedRuleExclusionSelectorMatchOperatorStartsWith), + }, false), + }, + "selector": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "action": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(frontdoor.ActionTypeAllow), + string(frontdoor.ActionTypeLog), + string(frontdoor.ActionTypeBlock), + string(frontdoor.ActionTypeRedirect), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + + "frontend_endpoint_ids": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "tags": commonschema.Tags(), + }, + } +} + +func resourceCdnFrontDoorFirewallPolicyCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorLegacyFirewallPoliciesClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + log.Printf("[INFO] preparing args for Cdn Frontdoor %q Firewall Policy(Resource Group: %q)", name, resourceGroup) + id := parse.NewFrontDoorFirewallPolicyID(subscriptionId, resourceGroup, name) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_frontdoor_firewall_policy", id.ID()) + } + } + + enabled := frontdoor.PolicyEnabledStateDisabled + + if d.Get("enabled").(bool) { + enabled = frontdoor.PolicyEnabledStateEnabled + } + + sku := d.Get("sku_name").(string) + mode := frontdoor.PolicyMode(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 := expandCdnFrontDoorFirewallManagedRules(d.Get("managed_rule").([]interface{})) + + if sku != string(frontdoor.SkuNamePremiumAzureFrontDoor) && managedRules != nil { + return fmt.Errorf("the `managed_rule` field is only supported with the %q sku, got %q", frontdoor.SkuNamePremiumAzureFrontDoor, sku) + } + + t := d.Get("tags").(map[string]interface{}) + + payload := frontdoor.WebApplicationFirewallPolicy{ + Location: utils.String(location.Normalize("Global")), + Sku: &frontdoor.Sku{ + Name: frontdoor.SkuName(sku), + }, + WebApplicationFirewallPolicyProperties: &frontdoor.WebApplicationFirewallPolicyProperties{ + PolicySettings: &frontdoor.PolicySettings{ + EnabledState: enabled, + Mode: mode, + }, + CustomRules: expandCdnFrontDoorFirewallCustomRules(customRules), + }, + Tags: expandFrontDoorTags(tags.Expand(t)), + } + + if managedRules != nil { + payload.WebApplicationFirewallPolicyProperties.ManagedRules = managedRules + } + + if redirectUrl != "" { + payload.WebApplicationFirewallPolicyProperties.PolicySettings.RedirectURL = utils.String(redirectUrl) + } + + if customBlockResponseBody != "" { + payload.WebApplicationFirewallPolicyProperties.PolicySettings.CustomBlockResponseBody = utils.String(customBlockResponseBody) + } + + if customBlockResponseStatusCode > 0 { + payload.WebApplicationFirewallPolicyProperties.PolicySettings.CustomBlockResponseStatusCode = utils.Int32(int32(customBlockResponseStatusCode)) + } + + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName, payload) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceCdnFrontDoorFirewallPolicyRead(d, meta) +} + +func resourceCdnFrontDoorFirewallPolicyUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorLegacyFirewallPoliciesClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorFirewallPolicyID(d.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + if existing.Sku == nil { + return fmt.Errorf("retrieving %s: `sku` was nil", *id) + } + + if existing.WebApplicationFirewallPolicyProperties == nil { + return fmt.Errorf("retrieving %s: `properties` was nil", *id) + } + + props := *existing.WebApplicationFirewallPolicyProperties + + if d.HasChanges("custom_block_response_body", "custom_block_response_status_code", "enabled", "mode", "redirect_url") { + enabled := frontdoor.PolicyEnabledStateDisabled + if d.Get("enabled").(bool) { + enabled = frontdoor.PolicyEnabledStateEnabled + } + props.PolicySettings = &frontdoor.PolicySettings{ + EnabledState: enabled, + Mode: frontdoor.PolicyMode(d.Get("mode").(string)), + } + + if redirectUrl := d.Get("redirect_url").(string); redirectUrl != "" { + props.PolicySettings.RedirectURL = utils.String(redirectUrl) + } + + if body := d.Get("custom_block_response_body").(string); body != "" { + props.PolicySettings.CustomBlockResponseBody = utils.String(body) + } + + if statusCode := d.Get("custom_block_response_status_code").(int); statusCode > 0 { + props.PolicySettings.CustomBlockResponseStatusCode = utils.Int32(int32(statusCode)) + } + } + + if d.HasChange("custom_rule") { + props.CustomRules = expandCdnFrontDoorFirewallCustomRules(d.Get("custom_rule").([]interface{})) + } + + if d.HasChange("managed_rule") { + managedRules := expandCdnFrontDoorFirewallManagedRules(d.Get("managed_rule").([]interface{})) + if existing.Sku.Name != frontdoor.SkuNamePremiumAzureFrontDoor && managedRules != nil { + return fmt.Errorf("the `managed_rule` field is only supported when using the sku %q, got %q", frontdoor.SkuNamePremiumAzureFrontDoor, existing.Sku.Name) + } + if managedRules != nil { + props.ManagedRules = managedRules + } + } + + if d.HasChange("tags") { + t := d.Get("tags").(map[string]interface{}) + existing.Tags = expandFrontDoorTags(tags.Expand(t)) + } + + existing.WebApplicationFirewallPolicyProperties = &props + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName, existing) + if err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the update of %s: %+v", *id, err) + } + + return resourceCdnFrontDoorFirewallPolicyRead(d, meta) +} + +func resourceCdnFrontDoorFirewallPolicyRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorLegacyFirewallPoliciesClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorFirewallPolicyID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Cdn Frontdoor Firewall Policy %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.FrontDoorWebApplicationFirewallPolicyName) + d.Set("resource_group_name", id.ResourceGroup) + + skuName := "" + if sku := resp.Sku; sku != nil { + skuName = string(sku.Name) + } + d.Set("sku_name", skuName) + + 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", flattenCdnFrontDoorFirewallCustomRules(properties.CustomRules)); err != nil { + return fmt.Errorf("flattening `custom_rule`: %+v", err) + } + + if err := d.Set("frontend_endpoint_ids", flattenFrontendEndpointLinkSlice(properties.FrontendEndpointLinks)); err != nil { + return fmt.Errorf("flattening `frontend_endpoint_ids`: %+v", err) + } + + if err := d.Set("managed_rule", flattenCdnFrontDoorFirewallManagedRules(properties.ManagedRules)); err != nil { + return fmt.Errorf("flattening `managed_rule`: %+v", err) + } + } + + if err := tags.FlattenAndSet(d, flattenFrontDoorTags(resp.Tags)); err != nil { + return err + } + + return nil +} + +func resourceCdnFrontDoorFirewallPolicyDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorLegacyFirewallPoliciesClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorFirewallPolicyID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) + } + + return nil +} + +func expandCdnFrontDoorFirewallCustomRules(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 := expandCdnFrontDoorFirewallMatchConditions(custom["match_condition"].([]interface{})) + action := custom["action"].(string) + + output = append(output, frontdoor.CustomRule{ + Name: utils.String(name), + Priority: &priority, + EnabledState: enabled, + RuleType: frontdoor.RuleType(ruleType), + RateLimitDurationInMinutes: utils.Int32(rateLimitDurationInMinutes), + RateLimitThreshold: utils.Int32(rateLimitThreshold), + MatchConditions: &matchConditions, + Action: frontdoor.ActionType(action), + }) + } + + return &frontdoor.CustomRuleList{ + Rules: &output, + } +} + +func expandCdnFrontDoorFirewallMatchConditions(input []interface{}) []frontdoor.MatchCondition { + result := make([]frontdoor.MatchCondition, 0) + if len(input) == 0 { + return nil + } + + 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: expandCdnFrontDoorFirewallTransforms(transforms), + } + + if matchVariable != "" { + matchCondition.MatchVariable = frontdoor.MatchVariable(matchVariable) + } + if selector != "" { + matchCondition.Selector = utils.String(selector) + } + + result = append(result, matchCondition) + } + + return result +} + +func expandCdnFrontDoorFirewallTransforms(input []interface{}) *[]frontdoor.TransformType { + result := make([]frontdoor.TransformType, 0) + if len(input) == 0 { + return nil + } + + for _, v := range input { + result = append(result, frontdoor.TransformType(v.(string))) + } + + return &result +} + +func expandCdnFrontDoorFirewallManagedRules(input []interface{}) *frontdoor.ManagedRuleSetList { + if len(input) == 0 { + return nil + } + + result := make([]frontdoor.ManagedRuleSet, 0) + for _, mr := range input { + managedRule := mr.(map[string]interface{}) + + ruleType := managedRule["type"].(string) + version := managedRule["version"].(string) + action := managedRule["action"].(string) + overrides := managedRule["override"].([]interface{}) + exclusions := expandCdnFrontDoorFirewallManagedRuleGroupExclusion(managedRule["exclusion"].([]interface{})) + ruleGroupOverrides := expandCdnFrontDoorFirewallManagedRuleGroupOverride(overrides) + + managedRuleSet := frontdoor.ManagedRuleSet{ + Exclusions: exclusions, + RuleSetVersion: &version, + RuleGroupOverrides: ruleGroupOverrides, + RuleSetType: &ruleType, + } + + if action != "" { + managedRuleSet.RuleSetAction = frontdoor.ManagedRuleSetActionType(action) + } + + result = append(result, managedRuleSet) + } + + return &frontdoor.ManagedRuleSetList{ + ManagedRuleSets: &result, + } +} + +func expandCdnFrontDoorFirewallManagedRuleGroupExclusion(input []interface{}) *[]frontdoor.ManagedRuleExclusion { + results := make([]frontdoor.ManagedRuleExclusion, 0) + if len(input) == 0 { + return nil + } + + for _, v := range input { + exclusion := v.(map[string]interface{}) + + matchVariable := exclusion["match_variable"].(string) + operator := exclusion["operator"].(string) + selector := exclusion["selector"].(string) + + results = append(results, frontdoor.ManagedRuleExclusion{ + MatchVariable: frontdoor.ManagedRuleExclusionMatchVariable(matchVariable), + SelectorMatchOperator: frontdoor.ManagedRuleExclusionSelectorMatchOperator(operator), + Selector: &selector, + }) + } + + return &results +} + +func expandCdnFrontDoorFirewallManagedRuleGroupOverride(input []interface{}) *[]frontdoor.ManagedRuleGroupOverride { + result := make([]frontdoor.ManagedRuleGroupOverride, 0) + if len(input) == 0 { + return nil + } + + for _, v := range input { + override := v.(map[string]interface{}) + + exclusions := expandCdnFrontDoorFirewallManagedRuleGroupExclusion(override["exclusion"].([]interface{})) + ruleGroupName := override["rule_group_name"].(string) + rules := expandCdnFrontDoorFirewallRuleOverride(override["rule"].([]interface{})) + + result = append(result, frontdoor.ManagedRuleGroupOverride{ + Exclusions: exclusions, + RuleGroupName: &ruleGroupName, + Rules: rules, + }) + } + + return &result +} + +func expandCdnFrontDoorFirewallRuleOverride(input []interface{}) *[]frontdoor.ManagedRuleOverride { + result := make([]frontdoor.ManagedRuleOverride, 0) + if len(input) == 0 { + return nil + } + + 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 := frontdoor.ActionType(rule["action"].(string)) + exclusions := expandCdnFrontDoorFirewallManagedRuleGroupExclusion(rule["exclusion"].([]interface{})) + + result = append(result, frontdoor.ManagedRuleOverride{ + RuleID: &ruleId, + EnabledState: enabled, + Action: action, + Exclusions: exclusions, + }) + } + + return &result +} + +func flattenCdnFrontDoorFirewallCustomRules(input *frontdoor.CustomRuleList) []interface{} { + if input == nil || input.Rules == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, v := range *input.Rules { + action := "" + if v.Action != "" { + action = string(v.Action) + } + + enabled := false + if v.EnabledState != "" { + enabled = v.EnabledState == frontdoor.CustomRuleEnabledStateEnabled + } + + name := "" + if v.Name != nil { + name = *v.Name + } + + priority := 0 + if v.Priority != nil { + priority = int(*v.Priority) + } + + rateLimitDurationInMinutes := 0 + if v.RateLimitDurationInMinutes != nil { + rateLimitDurationInMinutes = int(*v.RateLimitDurationInMinutes) + } + + rateLimitThreshold := 0 + if v.RateLimitThreshold != nil { + rateLimitThreshold = int(*v.RateLimitThreshold) + } + + ruleType := "" + if v.RuleType != "" { + ruleType = string(v.RuleType) + } + + results = append(results, map[string]interface{}{ + "action": action, + "enabled": enabled, + "match_condition": flattenCdnFrontDoorFirewallMatchConditions(v.MatchConditions), + "rate_limit_duration_in_minutes": rateLimitDurationInMinutes, + "rate_limit_threshold": rateLimitThreshold, + "priority": priority, + "name": name, + "type": ruleType, + }) + } + + return results +} + +func flattenCdnFrontDoorFirewallMatchConditions(input *[]frontdoor.MatchCondition) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, v := range *input { + selector := "" + if v.Selector != nil { + selector = *v.Selector + } + + negateCondition := false + if v.NegateCondition != nil { + negateCondition = *v.NegateCondition + } + + results = append(results, map[string]interface{}{ + "match_variable": string(v.MatchVariable), + "match_values": v.MatchValue, + "negation_condition": negateCondition, + "operator": string(v.Operator), + "selector": selector, + "transforms": flattenTransformSlice(v.Transforms), + }) + } + + return results +} + +func flattenCdnFrontDoorFirewallManagedRules(input *frontdoor.ManagedRuleSetList) []interface{} { + if input == nil || input.ManagedRuleSets == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, r := range *input.ManagedRuleSets { + ruleSetType := "" + if r.RuleSetType != nil { + ruleSetType = *r.RuleSetType + } + + ruleSetVersion := "" + if r.RuleSetVersion != nil { + ruleSetVersion = *r.RuleSetVersion + } + + ruleSetAction := "" + if r.RuleSetAction != "" { + ruleSetAction = string(r.RuleSetAction) + } + + results = append(results, map[string]interface{}{ + "exclusion": flattenCdnFrontDoorFirewallExclusions(r.Exclusions), + "override": flattenCdnFrontDoorFirewallOverrides(r.RuleGroupOverrides), + "type": ruleSetType, + "version": ruleSetVersion, + "action": ruleSetAction, + }) + } + + return results +} + +func flattenCdnFrontDoorFirewallExclusions(input *[]frontdoor.ManagedRuleExclusion) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, v := range *input { + matchVariable := "" + if v.MatchVariable != "" { + matchVariable = string(v.MatchVariable) + } + + operator := "" + if v.SelectorMatchOperator != "" { + operator = string(v.SelectorMatchOperator) + } + + selector := "" + if v.Selector != nil { + selector = *v.Selector + } + + results = append(results, map[string]interface{}{ + "match_variable": matchVariable, + "operator": operator, + "selector": selector, + }) + } + + return results +} + +func flattenCdnFrontDoorFirewallOverrides(input *[]frontdoor.ManagedRuleGroupOverride) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, v := range *input { + ruleGroupName := "" + if v.RuleGroupName != nil { + ruleGroupName = *v.RuleGroupName + } + + results = append(results, map[string]interface{}{ + "rule_group_name": ruleGroupName, + "exclusion": flattenCdnFrontDoorFirewallExclusions(v.Exclusions), + "rule": flattenCdnFrontDoorFirewallRules(v.Rules), + }) + } + + return results +} + +func flattenCdnFrontDoorFirewallRules(input *[]frontdoor.ManagedRuleOverride) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, v := range *input { + action := "" + if v.Action != "" { + action = string(v.Action) + } + + enabled := false + if v.EnabledState != "" { + enabled = v.EnabledState == frontdoor.ManagedRuleEnabledStateEnabled + } + + ruleId := "" + if v.RuleID != nil { + ruleId = *v.RuleID + } + + results = append(results, map[string]interface{}{ + "action": action, + "enabled": enabled, + "exclusion": flattenCdnFrontDoorFirewallExclusions(v.Exclusions), + "rule_id": ruleId, + }) + } + + return results +} diff --git a/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go new file mode 100644 index 000000000000..662635fcc8e7 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_firewall_policy_resource_test.go @@ -0,0 +1,341 @@ +package cdn_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CdnFrontDoorFirewallPolicyResource struct{} + +func TestAccCdnFrontDoorFirewallPolicy_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorFirewallPolicy_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCdnFrontDoorFirewallPolicy_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc(), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorFirewallPolicy_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_firewall_policy", "test") + r := CdnFrontDoorFirewallPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (CdnFrontDoorFirewallPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.FrontDoorFirewallPolicyIDInsensitively(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Cdn.FrontDoorLegacyFirewallPoliciesClient.Get(ctx, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (CdnFrontDoorFirewallPolicyResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cdn-afdx-%d" + location = "%s" +} + +resource "azurerm_cdn_frontdoor_profile" "test" { + name = "accTestProfile-%[1]d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "Premium_AzureFrontDoor" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r CdnFrontDoorFirewallPolicyResource) basic(data acceptance.TestData) string { + tmp := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestWAF%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_name + mode = "Prevention" +} +`, tmp, data.RandomInteger) +} + +func (r CdnFrontDoorFirewallPolicyResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_firewall_policy" "import" { + name = azurerm_cdn_frontdoor_firewall_policy.test.name + resource_group_name = azurerm_cdn_frontdoor_firewall_policy.test.resource_group_name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_name + mode = "Prevention" +} +`, r.basic(data)) +} + +func (r CdnFrontDoorFirewallPolicyResource) update(data acceptance.TestData) string { + tmp := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestWAF%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_name + enabled = true + mode = "Detection" + 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"] + } + } + + managed_rule { + type = "DefaultRuleSet" + version = "preview-0.1" + action = "Block" + + override { + rule_group_name = "PHP" + + rule { + rule_id = "933111" + enabled = false + action = "Block" + } + } + } + + managed_rule { + type = "BotProtection" + version = "preview-0.1" + action = "Log" + } +} +`, tmp, data.RandomInteger) +} + +func (r CdnFrontDoorFirewallPolicyResource) complete(data acceptance.TestData) string { + tmp := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestWAF%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_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"] + } + } + + 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"] + } + } + + custom_rule { + name = "Rule3" + enabled = true + priority = 3 + rate_limit_duration_in_minutes = 1 + rate_limit_threshold = 10 + type = "MatchRule" + action = "Block" + + match_condition { + match_variable = "SocketAddr" + 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"] + } + } + + managed_rule { + type = "DefaultRuleSet" + version = "1.0" + action = "Block" + + exclusion { + match_variable = "QueryStringArgNames" + operator = "Equals" + selector = "not_suspicious" + } + + override { + rule_group_name = "PHP" + + rule { + rule_id = "933100" + enabled = false + action = "Block" + } + } + + override { + rule_group_name = "SQLI" + + exclusion { + match_variable = "QueryStringArgNames" + operator = "Equals" + selector = "really_not_suspicious" + } + + rule { + rule_id = "942200" + action = "Block" + + exclusion { + match_variable = "QueryStringArgNames" + operator = "Equals" + selector = "innocent" + } + } + } + } + + managed_rule { + type = "Microsoft_BotManagerRuleSet" + version = "1.0" + action = "Block" + } +} +`, tmp, data.RandomInteger) +} diff --git a/internal/services/cdn/cdn_frontdoor_helpers.go b/internal/services/cdn/cdn_frontdoor_helpers.go index 1f8b655f0513..ef922d70f694 100644 --- a/internal/services/cdn/cdn_frontdoor_helpers.go +++ b/internal/services/cdn/cdn_frontdoor_helpers.go @@ -1,6 +1,9 @@ package cdn -import "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" +import ( + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/Azure/azure-sdk-for-go/services/frontdoor/mgmt/2020-11-01/frontdoor" +) func expandEnabledBool(isEnabled bool) cdn.EnabledState { if isEnabled { @@ -13,3 +16,59 @@ func expandEnabledBool(isEnabled bool) cdn.EnabledState { func flattenEnabledBool(input cdn.EnabledState) bool { return input == cdn.EnabledStateEnabled } + +func expandFrontDoorTags(tagMap *map[string]string) map[string]*string { + t := make(map[string]*string) + + if tagMap != nil { + for k, v := range *tagMap { + tagKey := k + tagValue := v + t[tagKey] = &tagValue + } + } + + return t +} + +func flattenFrontDoorTags(tagMap map[string]*string) *map[string]string { + t := make(map[string]string) + + for k, v := range tagMap { + tagKey := k + tagValue := v + if tagValue == nil { + continue + } + t[tagKey] = *tagValue + } + + return &t +} + +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 { + if item.ID == nil { + continue + } + + result = append(result, *item.ID) + } + } + + return result +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_group_data_source.go b/internal/services/cdn/cdn_frontdoor_origin_group_data_source.go new file mode 100644 index 000000000000..2e5b07e9dee1 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_group_data_source.go @@ -0,0 +1,144 @@ +package cdn + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func dataSourceCdnFrontDoorOriginGroup() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceCdnFrontDoorOriginGroupRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.FrontDoorOriginGroupName, + }, + + "profile_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.FrontDoorName, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + // Computed + "cdn_frontdoor_profile_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "health_probe": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "interval_in_seconds": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + "path": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "protocol": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "request_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "load_balancing": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "additional_latency_in_milliseconds": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "sample_size": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "successful_samples_required": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + }, + }, + }, + + "restore_traffic_time_to_healed_or_new_endpoint_in_minutes": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "session_affinity_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + }, + } +} + +func dataSourceCdnFrontDoorOriginGroupRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginGroupsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id := parse.NewFrontDoorOriginGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("profile_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("%s was not found", id) + } + + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.SetId(id.ID()) + d.Set("name", id.OriginGroupName) + d.Set("profile_name", id.ProfileName) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("cdn_frontdoor_profile_id", parse.NewFrontDoorProfileID(id.SubscriptionId, id.ResourceGroup, id.ProfileName).ID()) + + if props := resp.AFDOriginGroupProperties; props != nil { + if err := d.Set("health_probe", flattenCdnFrontDoorOriginGroupHealthProbeParameters(props.HealthProbeSettings)); err != nil { + return fmt.Errorf("setting `health_probe`: %+v", err) + } + + if err := d.Set("load_balancing", flattenCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(props.LoadBalancingSettings)); err != nil { + return fmt.Errorf("setting `load_balancing`: %+v", err) + } + + d.Set("session_affinity_enabled", flattenEnabledBool(props.SessionAffinityState)) + d.Set("restore_traffic_time_to_healed_or_new_endpoint_in_minutes", props.TrafficRestorationTimeToHealedOrNewEndpointsInMinutes) + } + + return nil +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_group_data_source_test.go b/internal/services/cdn/cdn_frontdoor_origin_group_data_source_test.go new file mode 100644 index 000000000000..8b13006a5e0d --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_group_data_source_test.go @@ -0,0 +1,37 @@ +package cdn_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type CdnFrontDoorOriginGroupDataSource struct{} + +func TestAccCdnFrontDoorOriginGroupDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_cdn_frontdoor_origin_group", "test") + d := CdnFrontDoorOriginGroupDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("cdn_frontdoor_profile_id").MatchesOtherKey(check.That("azurerm_cdn_frontdoor_profile.test").Key("id")), + ), + }, + }) +} + +func (CdnFrontDoorOriginGroupDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_cdn_frontdoor_origin_group" "test" { + name = azurerm_cdn_frontdoor_origin_group.test.name + profile_name = azurerm_cdn_frontdoor_profile.test.name + resource_group_name = azurerm_cdn_frontdoor_profile.test.resource_group_name +} +`, CdnFrontDoorOriginGroupResource{}.complete(data)) +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_group_resource.go b/internal/services/cdn/cdn_frontdoor_origin_group_resource.go new file mode 100644 index 000000000000..4d38f2b1bada --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_group_resource.go @@ -0,0 +1,381 @@ +package cdn + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceCdnFrontDoorOriginGroup() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceCdnFrontDoorOriginGroupCreate, + Read: resourceCdnFrontDoorOriginGroupRead, + Update: resourceCdnFrontDoorOriginGroupUpdate, + Delete: resourceCdnFrontDoorOriginGroupDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FrontDoorOriginGroupID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorOriginGroupName, + }, + + "cdn_frontdoor_profile_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorProfileID, + }, + + "load_balancing": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "additional_latency_in_milliseconds": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 50, + ValidateFunc: validation.IntBetween(0, 1000), + }, + + "sample_size": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 4, + ValidateFunc: validation.IntBetween(0, 255), + }, + + "successful_samples_required": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 3, + ValidateFunc: validation.IntBetween(0, 255), + }, + }, + }, + }, + + // Optional + "health_probe": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "protocol": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.ProbeProtocolHTTP), + string(cdn.ProbeProtocolHTTPS), + }, false), + }, + + "request_type": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(cdn.HealthProbeRequestTypeHEAD), + ValidateFunc: validation.StringInSlice([]string{ + string(cdn.HealthProbeRequestTypeGET), + string(cdn.HealthProbeRequestTypeHEAD), + }, false), + }, + + "interval_in_seconds": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(5, 31536000), + }, + + "path": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "/", + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "session_affinity_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "restore_traffic_time_to_healed_or_new_endpoint_in_minutes": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(0, 50), + }, + }, + } +} + +func resourceCdnFrontDoorOriginGroupCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginGroupsClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + profileId, err := parse.FrontDoorProfileID(d.Get("cdn_frontdoor_profile_id").(string)) + if err != nil { + return err + } + + id := parse.NewFrontDoorOriginGroupID(profileId.SubscriptionId, profileId.ResourceGroup, profileId.ProfileName, d.Get("name").(string)) + existing, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_frontdoor_origin_group", id.ID()) + } + + props := cdn.AFDOriginGroup{ + AFDOriginGroupProperties: &cdn.AFDOriginGroupProperties{ + HealthProbeSettings: expandCdnFrontDoorOriginGroupHealthProbeParameters(d.Get("health_probe").([]interface{})), + LoadBalancingSettings: expandCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(d.Get("load_balancing").([]interface{})), + SessionAffinityState: expandEnabledBool(d.Get("session_affinity_enabled").(bool)), + TrafficRestorationTimeToHealedOrNewEndpointsInMinutes: utils.Int32(int32(d.Get("restore_traffic_time_to_healed_or_new_endpoint_in_minutes").(int))), + }, + } + + future, err := client.Create(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, props) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceCdnFrontDoorOriginGroupRead(d, meta) +} + +func resourceCdnFrontDoorOriginGroupRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginGroupsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginGroupID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.Set("name", id.OriginGroupName) + d.Set("cdn_frontdoor_profile_id", parse.NewFrontDoorProfileID(id.SubscriptionId, id.ResourceGroup, id.ProfileName).ID()) + + if props := resp.AFDOriginGroupProperties; props != nil { + if err := d.Set("health_probe", flattenCdnFrontDoorOriginGroupHealthProbeParameters(props.HealthProbeSettings)); err != nil { + return fmt.Errorf("setting `health_probe`: %+v", err) + } + + if err := d.Set("load_balancing", flattenCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(props.LoadBalancingSettings)); err != nil { + return fmt.Errorf("setting `load_balancing`: %+v", err) + } + + d.Set("session_affinity_enabled", flattenEnabledBool(props.SessionAffinityState)) + d.Set("restore_traffic_time_to_healed_or_new_endpoint_in_minutes", props.TrafficRestorationTimeToHealedOrNewEndpointsInMinutes) + } + + return nil +} + +func resourceCdnFrontDoorOriginGroupUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginGroupsClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginGroupID(d.Id()) + if err != nil { + return err + } + + params := &cdn.AFDOriginGroupUpdatePropertiesParameters{} + + if d.HasChange("health_probe") { + params.HealthProbeSettings = expandCdnFrontDoorOriginGroupHealthProbeParameters(d.Get("health_probe").([]interface{})) + } + + if d.HasChange("load_balancing") { + params.LoadBalancingSettings = expandCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(d.Get("load_balancing").([]interface{})) + } + + if d.HasChange("restore_traffic_time_to_healed_or_new_endpoint_in_minutes") { + params.TrafficRestorationTimeToHealedOrNewEndpointsInMinutes = utils.Int32(int32(d.Get("restore_traffic_time_to_healed_or_new_endpoint_in_minutes").(int))) + } + + if d.HasChange("session_affinity_enabled") { + params.SessionAffinityState = expandEnabledBool(d.Get("session_affinity_enabled").(bool)) + } + + payload := cdn.AFDOriginGroupUpdateParameters{ + AFDOriginGroupUpdatePropertiesParameters: params, + } + + future, err := client.Update(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, payload) + if err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the update of %s: %+v", *id, err) + } + + return resourceCdnFrontDoorOriginGroupRead(d, meta) +} + +func resourceCdnFrontDoorOriginGroupDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginGroupsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginGroupID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) + } + return nil +} + +func expandCdnFrontDoorOriginGroupHealthProbeParameters(input []interface{}) *cdn.HealthProbeParameters { + if len(input) == 0 || input[0] == nil { + return nil + } + + v := input[0].(map[string]interface{}) + + probeProtocolValue := cdn.ProbeProtocol(v["protocol"].(string)) + probeRequestTypeValue := cdn.HealthProbeRequestType(v["request_type"].(string)) + return &cdn.HealthProbeParameters{ + ProbeIntervalInSeconds: utils.Int32(int32(v["interval_in_seconds"].(int))), + ProbePath: utils.String(v["path"].(string)), + ProbeProtocol: probeProtocolValue, + ProbeRequestType: probeRequestTypeValue, + } +} + +func expandCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(input []interface{}) *cdn.LoadBalancingSettingsParameters { + if len(input) == 0 || input[0] == nil { + return nil + } + + v := input[0].(map[string]interface{}) + + return &cdn.LoadBalancingSettingsParameters{ + AdditionalLatencyInMilliseconds: utils.Int32(int32(v["additional_latency_in_milliseconds"].(int))), + SampleSize: utils.Int32(int32(v["sample_size"].(int))), + SuccessfulSamplesRequired: utils.Int32(int32(v["successful_samples_required"].(int))), + } +} + +func flattenCdnFrontDoorOriginGroupLoadBalancingSettingsParameters(input *cdn.LoadBalancingSettingsParameters) []interface{} { + if input == nil { + return []interface{}{} + } + + additionalLatencyInMilliseconds := 0 + if input.AdditionalLatencyInMilliseconds != nil { + additionalLatencyInMilliseconds = int(*input.AdditionalLatencyInMilliseconds) + } + + sampleSize := 0 + if input.SampleSize != nil { + sampleSize = int(*input.SampleSize) + } + + successfulSamplesRequired := 0 + if input.SuccessfulSamplesRequired != nil { + successfulSamplesRequired = int(*input.SuccessfulSamplesRequired) + } + return []interface{}{ + map[string]interface{}{ + "additional_latency_in_milliseconds": additionalLatencyInMilliseconds, + "sample_size": sampleSize, + "successful_samples_required": successfulSamplesRequired, + }, + } +} + +func flattenCdnFrontDoorOriginGroupHealthProbeParameters(input *cdn.HealthProbeParameters) []interface{} { + if input == nil { + return []interface{}{} + } + + intervalInSeconds := 0 + if input.ProbeIntervalInSeconds != nil { + intervalInSeconds = int(*input.ProbeIntervalInSeconds) + } + + path := "" + if input.ProbePath != nil { + path = *input.ProbePath + } + + protocol := "" + if input.ProbeProtocol != "" { + protocol = string(input.ProbeProtocol) + } + + requestType := "" + if input.ProbeRequestType != "" { + requestType = string(input.ProbeRequestType) + } + + return []interface{}{ + map[string]interface{}{ + "interval_in_seconds": intervalInSeconds, + "path": path, + "protocol": protocol, + "request_type": requestType, + }, + } +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_group_resource_test.go b/internal/services/cdn/cdn_frontdoor_origin_group_resource_test.go new file mode 100644 index 000000000000..3952cb60f74a --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_group_resource_test.go @@ -0,0 +1,212 @@ +package cdn_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CdnFrontDoorOriginGroupResource struct{} + +func TestAccCdnFrontDoorOriginGroup_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin_group", "test") + r := CdnFrontDoorOriginGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOriginGroup_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin_group", "test") + r := CdnFrontDoorOriginGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCdnFrontDoorOriginGroup_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin_group", "test") + r := CdnFrontDoorOriginGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOriginGroup_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin_group", "test") + r := CdnFrontDoorOriginGroupResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r CdnFrontDoorOriginGroupResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.FrontDoorOriginGroupID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Cdn.FrontDoorOriginGroupsClient + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(true), nil +} + +func (r CdnFrontDoorOriginGroupResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin_group" "test" { + name = "acctest-origingroup-%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + load_balancing { + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginGroupResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_origin_group" "import" { + name = azurerm_cdn_frontdoor_origin_group.test.name + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_origin_group.test.cdn_frontdoor_profile_id + + load_balancing { + additional_latency_in_milliseconds = 0 + sample_size = 16 + successful_samples_required = 3 + } +} +`, config) +} + +func (r CdnFrontDoorOriginGroupResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin_group" "test" { + name = "acctest-origingroup-%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + session_affinity_enabled = true + + restore_traffic_time_to_healed_or_new_endpoint_in_minutes = 10 + + health_probe { + interval_in_seconds = 240 + path = "/" + protocol = "Https" + request_type = "GET" + } + + load_balancing { + additional_latency_in_milliseconds = 0 + sample_size = 16 + successful_samples_required = 3 + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginGroupResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin_group" "test" { + name = "acctest-origingroup-%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + session_affinity_enabled = false + + restore_traffic_time_to_healed_or_new_endpoint_in_minutes = 15 + + health_probe { + interval_in_seconds = 120 + path = "/healthProbe" + protocol = "Http" + request_type = "HEAD" + } + + load_balancing { + additional_latency_in_milliseconds = 32 + sample_size = 32 + successful_samples_required = 5 + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginGroupResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-cdn-afdx-%d" + location = "%s" +} + +resource "azurerm_cdn_frontdoor_profile" "test" { + name = "acctest-cdnfdprofile-%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard_AzureFrontDoor" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_resource.go b/internal/services/cdn/cdn_frontdoor_origin_resource.go new file mode 100644 index 000000000000..57488160ad27 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_resource.go @@ -0,0 +1,455 @@ +package cdn + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" + privateLinkServiceParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceCdnFrontDoorOrigin() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceCdnFrontDoorOriginCreate, + Read: resourceCdnFrontDoorOriginRead, + Update: resourceCdnFrontDoorOriginUpdate, + Delete: resourceCdnFrontDoorOriginDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FrontDoorOriginID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorOriginName, + }, + + "cdn_frontdoor_origin_group_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorOriginGroupID, + }, + + "host_name": { + Type: pluginsdk.TypeString, + // HostName cannot be null or empty. + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "certificate_name_check_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + + "health_probes_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "http_port": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 80, + ValidateFunc: validation.IntBetween(1, 65535), + }, + + "https_port": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 443, + ValidateFunc: validation.IntBetween(1, 65535), + }, + + // Must be a valid domain name, IPv4 or IPv6 IP Address + "origin_host_header": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.Any(validation.IsIPv6Address, validation.IsIPv4Address, validation.StringIsNotEmpty), + }, + + "priority": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 1, + ValidateFunc: validation.IntBetween(1, 5), + }, + + "private_link": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "location": commonschema.Location(), + + "private_link_target_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "request_message": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "Access request for CDN FrontDoor Private Link Origin", + ValidateFunc: validation.StringLenBetween(1, 140), + }, + + "target_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "blob", + "blob_secondary", + "sites", + "web", + }, false), + }, + }, + }, + }, + + "weight": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 500, + ValidateFunc: validation.IntBetween(1, 1000), + }, + }, + } +} + +func resourceCdnFrontDoorOriginCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginsClient + profileClient := meta.(*clients.Client).Cdn.FrontDoorProfileClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + originGroupId, err := parse.FrontDoorOriginGroupID(d.Get("cdn_frontdoor_origin_group_id").(string)) + if err != nil { + return err + } + + id := parse.NewFrontDoorOriginID(originGroupId.SubscriptionId, originGroupId.ResourceGroup, originGroupId.ProfileName, originGroupId.OriginGroupName, d.Get("name").(string)) + existing, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_frontdoor_origin", id.ID()) + } + + // I need to get the profile SKU so I know if it is valid or not to define a private link as + // private links are only allowed in the premium sku... + profileId := parse.NewFrontDoorProfileID(id.SubscriptionId, id.ResourceGroup, id.ProfileName) + + profile, err := profileClient.Get(ctx, profileId.ResourceGroup, profileId.ProfileName) + if err != nil { + if utils.ResponseWasNotFound(profile.Response) { + return fmt.Errorf("retrieving parent %s: not found", profileId) + } + + return fmt.Errorf("retrieving parent %s: %+v", profileId, err) + } + + if profile.Sku == nil { + return fmt.Errorf("retrieving parent %s: `sku` was nil", profileId) + } + skuName := profile.Sku.Name + + enableCertNameCheck := d.Get("certificate_name_check_enabled").(bool) + props := &cdn.AFDOriginProperties{ + EnabledState: expandEnabledBool(d.Get("health_probes_enabled").(bool)), + EnforceCertificateNameCheck: utils.Bool(enableCertNameCheck), + HostName: utils.String(d.Get("host_name").(string)), + HTTPPort: utils.Int32(int32(d.Get("http_port").(int))), + HTTPSPort: utils.Int32(int32(d.Get("https_port").(int))), + Priority: utils.Int32(int32(d.Get("priority").(int))), + Weight: utils.Int32(int32(d.Get("weight").(int))), + } + + if originHostHeader := d.Get("origin_host_header").(string); originHostHeader != "" { + props.OriginHostHeader = utils.String(originHostHeader) + } + + expanded, err := expandPrivateLinkSettings(d.Get("private_link").([]interface{}), skuName, enableCertNameCheck) + if err != nil { + return err + } + props.SharedPrivateLinkResource = expanded + + payload := cdn.AFDOrigin{ + AFDOriginProperties: props, + } + + future, err := client.Create(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName, payload) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceCdnFrontDoorOriginRead(d, meta) +} + +func resourceCdnFrontDoorOriginRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.OriginName) + d.Set("cdn_frontdoor_origin_group_id", parse.NewFrontDoorOriginGroupID(id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.OriginGroupName).ID()) + + if props := resp.AFDOriginProperties; props != nil { + if err := d.Set("private_link", flattenPrivateLinkSettings(props.SharedPrivateLinkResource)); err != nil { + return fmt.Errorf("setting `private_link`: %+v", err) + } + + d.Set("certificate_name_check_enabled", props.EnforceCertificateNameCheck) + d.Set("health_probes_enabled", flattenEnabledBool(props.EnabledState)) + d.Set("host_name", props.HostName) + d.Set("http_port", props.HTTPPort) + d.Set("https_port", props.HTTPSPort) + d.Set("origin_host_header", props.OriginHostHeader) + d.Set("priority", props.Priority) + d.Set("weight", props.Weight) + } + + return nil +} + +func resourceCdnFrontDoorOriginUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginsClient + profileClient := meta.(*clients.Client).Cdn.FrontDoorProfileClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginID(d.Id()) + if err != nil { + return err + } + + params := cdn.AFDOriginUpdatePropertiesParameters{} + + if d.HasChange("certificate_name_check_enabled") { + params.EnforceCertificateNameCheck = utils.Bool(d.Get("certificate_name_check_enabled").(bool)) + } + if d.HasChange("health_probes_enabled") { + params.EnabledState = expandEnabledBool(d.Get("health_probes_enabled").(bool)) + } + if d.HasChange("host_name") { + params.HostName = utils.String(d.Get("host_name").(string)) + } + if d.HasChange("http_port") { + params.HTTPPort = utils.Int32(int32(d.Get("http_port").(int))) + } + if d.HasChange("https_port") { + params.HTTPSPort = utils.Int32(int32(d.Get("https_port").(int))) + } + if d.HasChange("origin_host_header") { + params.OriginHostHeader = utils.String(d.Get("origin_host_header").(string)) + } + if d.HasChange("private_link") { + // I need to get the profile SKU so I know if it is valid or not to define a private link as + // private links are only allowed in the premium sku... + profileId := parse.NewFrontDoorProfileID(id.SubscriptionId, id.ResourceGroup, id.ProfileName) + profile, err := profileClient.Get(ctx, profileId.ResourceGroup, profileId.ProfileName) + if err != nil { + if utils.ResponseWasNotFound(profile.Response) { + return fmt.Errorf("retrieving parent %s: not found", profileId) + } + + return fmt.Errorf("retrieving parent %s: %+v", profileId, err) + } + if profile.Sku == nil { + return fmt.Errorf("retrieving parent %s: `sku` was nil", profileId) + } + skuName := profile.Sku.Name + + enableCertNameCheck := d.Get("certificate_name_check_enabled").(bool) + privateLinkSettings, err := expandPrivateLinkSettings(d.Get("private_link").([]interface{}), skuName, enableCertNameCheck) + if err != nil { + return err + } + params.SharedPrivateLinkResource = privateLinkSettings + } + if d.HasChange("priority") { + params.Priority = utils.Int32(int32(d.Get("priority").(int))) + } + if d.HasChange("weight") { + params.Weight = utils.Int32(int32(d.Get("weight").(int))) + } + + payload := cdn.AFDOriginUpdateParameters{ + AFDOriginUpdatePropertiesParameters: ¶ms, + } + future, err := client.Update(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName, payload) + if err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the update of %s: %+v", *id, err) + } + + return resourceCdnFrontDoorOriginRead(d, meta) +} + +func resourceCdnFrontDoorOriginDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorOriginsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorOriginID(d.Id()) + if err != nil { + return err + } + + // @tombuildsstuff: JC/WS to dig into if we need to conditionally remove the Private Link + // via an Update before deletion - presumably we'd also need a Lock on the private link resource + + /* + original: + // TODO: Check to see if there is a Load Balancer Private Link connected, + // if so disconnect the Private Link association with the Frontdoor Origin + // else the destroy will fail because the Private Link Service has an active + // Private Link Endpoint connection... + + // It looks like Frontdoor does remove the Private link, I just need to poll here until it is removed... + // Investigate this further... + // WS: There is a bug in the service code, for only the load balancer scenario, the private link connection is not removed until the + // origin is totally destroyed. The workaround for this issue is to put a depends_on the private link service to the origin so the origin + // will be deleted first before the private link service is destroyed. + */ + + future, err := client.Delete(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) + } + + return nil +} + +func expandPrivateLinkSettings(input []interface{}, skuName cdn.SkuName, enableCertNameCheck bool) (*cdn.SharedPrivateLinkResourceProperties, error) { + if len(input) == 0 { + // TODO: Should this return an empty object? + // WS: This cannot return an empty object, the service team requires this to be set to nil else you will get the following error during creation: + // Property 'AfdOrigin.SharedPrivateLinkResource.PrivateLink' is required but it was not set; Property 'AfdOrigin.SharedPrivateLinkResource.RequestMessage' is required but it was not set + return nil, nil + } + + if skuName != cdn.SkuNamePremiumAzureFrontDoor { + return nil, fmt.Errorf("the `private_link` field can only be configured when the Frontdoor Profile is using a %q SKU, got %q", cdn.SkuNamePremiumAzureFrontDoor, skuName) + } + + if !enableCertNameCheck { + return nil, fmt.Errorf("the `private_link` field can only be configured when `certificate_name_check_enabled` is set to `true`") + } + + // Check if this a Load Balancer Private Link or not, the Load Balancer Private Link requires + // that you stand up your own Private Link Service, which is why I am attempting to parse a + // Private Link Service ID here... + settings := input[0].(map[string]interface{}) + targetType := settings["target_type"].(string) + _, err := privateLinkServiceParse.PrivateLinkServiceID(settings["private_link_target_id"].(string)) + if err != nil && targetType == "" { + // It is not a Load Balancer and the Target Type is empty, which is invalid... + return nil, fmt.Errorf("either `private_link` or `target_type` must be specified") + } + + config := input[0].(map[string]interface{}) + + resourceId := config["private_link_target_id"].(string) + location := location.Normalize(config["location"].(string)) + groupId := config["target_type"].(string) + requestMessage := config["request_message"].(string) + + return &cdn.SharedPrivateLinkResourceProperties{ + PrivateLink: &cdn.ResourceReference{ + ID: utils.String(resourceId), + }, + GroupID: utils.String(groupId), + PrivateLinkLocation: utils.String(location), + RequestMessage: utils.String(requestMessage), + }, nil +} + +func flattenPrivateLinkSettings(input *cdn.SharedPrivateLinkResourceProperties) []interface{} { + if input == nil { + return []interface{}{} + } + + privateLinkTargetId := "" + if input.PrivateLink != nil && input.PrivateLink.ID != nil { + privateLinkTargetId = *input.PrivateLink.ID + } + + requestMessage := "" + if input.RequestMessage != nil { + requestMessage = *input.RequestMessage + } + + targetType := "" + if input.GroupID != nil { + targetType = *input.GroupID + } + + return []interface{}{ + map[string]interface{}{ + "location": location.NormalizeNilable(input.PrivateLinkLocation), + "private_link_target_id": privateLinkTargetId, + "request_message": requestMessage, + "target_type": targetType, + }, + } +} diff --git a/internal/services/cdn/cdn_frontdoor_origin_resource_test.go b/internal/services/cdn/cdn_frontdoor_origin_resource_test.go new file mode 100644 index 000000000000..1c696a792ef0 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_origin_resource_test.go @@ -0,0 +1,636 @@ +package cdn_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CdnFrontDoorOriginResource struct { +} + +func TestAccCdnFrontDoorOrigin_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCdnFrontDoorOrigin_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_privateLinkBlobPrimary(t *testing.T) { + t.Skip("@tombuildsstuff: temporarily skipping until the private link is manually approved as part of the test step") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkBlobPrimary(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + // TODO: approve the connection by looking up and updating the private link + // data.CheckWithClient(func(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) error { + // clients.Network.PrivateLinkServiceClient.UpdatePrivateEndpointConnection() + // }), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_privateLinkStorageStaticWebSite(t *testing.T) { + t.Skip("@tombuildsstuff: temporarily skipping until the private link is manually approved as part of the test step") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkStaticWebSite(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + // TODO: approve the connection by looking up and updating the private link + // data.CheckWithClient(func(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) error { + // clients.Network.PrivateLinkServiceClient.UpdatePrivateEndpointConnection() + // }), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_privateLinkAppServices(t *testing.T) { + t.Skip("@tombuildsstuff: temporarily skipping until the private link is manually approved as part of the test step") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + // NOTE: The Private Link will not be approved at this point but it will + // be created. There is currently no way to automate the approval process. + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkAppServices(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + // TODO: approve the connection by looking up and updating the private link + // data.CheckWithClient(func(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) error { + // clients.Network.PrivateLinkServiceClient.UpdatePrivateEndpointConnection() + // }), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorOrigin_privateLinkLoadBalancer(t *testing.T) { + t.Skip("@tombuildsstuff: temporarily skipping until the private link is manually approved as part of the test step") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_origin", "test") + r := CdnFrontDoorOriginResource{} + + // NOTE: The Private Link will not be approved at this point but it will + // be created. There is currently no way to automate the approval process. + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkLoadBalancer(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + // TODO: approve the connection by looking up and updating the private link + // data.CheckWithClient(func(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) error { + // clients.Network.PrivateLinkServiceClient.UpdatePrivateEndpointConnection() + // }), + ), + }, + data.ImportStep(), + }) +} + +func (r CdnFrontDoorOriginResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.FrontDoorOriginID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Cdn.FrontDoorOriginsClient + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (r CdnFrontDoorOriginResource) templatePrivateLinkStorage(data acceptance.TestData) string { + template := r.template(data, "Premium_AzureFrontDoor", false) + return fmt.Sprintf(` + +%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 = "Premium" + account_replication_type = "LRS" + + allow_nested_items_to_be_public = false + + network_rules { + default_action = "Deny" + } + + tags = { + environment = "Test" + } +} +`, template, data.RandomString) +} + +func (r CdnFrontDoorOriginResource) templatePrivateLinkStorageStaticWebSite(data acceptance.TestData) string { + template := r.template(data, "Premium_AzureFrontDoor", false) + return fmt.Sprintf(` + +%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" + account_kind = "StorageV2" + + allow_nested_items_to_be_public = false + + network_rules { + default_action = "Deny" + } + + static_website { + index_document = "index.html" + error_404_document = "404.html" + } + + tags = { + environment = "Test" + } +} +`, template, data.RandomString) +} + +func (r CdnFrontDoorOriginResource) templatePrivateLinkLoadBalancer(data acceptance.TestData) string { + template := r.template(data, "Premium_AzureFrontDoor", true) + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +%s + +resource "azurerm_virtual_network" "test" { + name = "acctestvn-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + address_space = ["10.5.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "acctestsn-%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.5.1.0/24"] + enforce_private_link_service_network_policies = true +} + +resource "azurerm_public_ip" "test" { + name = "acctestpi-%[2]d" + sku = "Standard" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" +} + +resource "azurerm_lb" "test" { + name = "acctestlb-%[2]d" + sku = "Standard" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + frontend_ip_configuration { + name = azurerm_public_ip.test.name + public_ip_address_id = azurerm_public_ip.test.id + } +} + +resource "azurerm_private_link_service" "test" { + name = "acctestPLS-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + visibility_subscription_ids = [data.azurerm_client_config.current.subscription_id] + load_balancer_frontend_ip_configuration_ids = [azurerm_lb.test.frontend_ip_configuration.0.id] + + nat_ip_configuration { + name = "primary" + private_ip_address = "10.5.1.17" + private_ip_address_version = "IPv4" + subnet_id = azurerm_subnet.test.id + primary = true + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) templatePrivateLinkWebApp(data acceptance.TestData) string { + template := r.template(data, "Premium_AzureFrontDoor", false) + return fmt.Sprintf(` + +%s + +resource "azurerm_service_plan" "test" { + name = "acctestASP-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + os_type = "Linux" + sku_name = "P1v3" +} + +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_storage_container" "test" { + name = "testaccsc%[3]s" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + + resource_types { + service = false + container = false + object = true + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-01" + expiry = "2024-03-30" + + permissions { + read = false + write = true + delete = false + list = false + add = false + create = false + update = false + process = false + tag = false + filter = false + } +} + +resource "azurerm_linux_web_app" "test" { + name = "acctestWA-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + site_config {} +} +`, template, data.RandomInteger, data.RandomString) +} + +func (r CdnFrontDoorOriginResource) basic(data acceptance.TestData) string { + template := r.template(data, "Standard_AzureFrontDoor", false) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = false + host_name = "contoso.com" + http_port = 80 + https_port = 443 + origin_host_header = "www.contoso.com" + priority = 1 + weight = 1 +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_origin" "import" { + name = azurerm_cdn_frontdoor_origin.test.name + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = false + host_name = "contoso.com" + http_port = 80 + https_port = 443 + origin_host_header = "www.contoso.com" + priority = 1 + weight = 1 +} +`, config) +} + +func (r CdnFrontDoorOriginResource) complete(data acceptance.TestData) string { + template := r.template(data, "Standard_AzureFrontDoor", false) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = false + host_name = "contoso.com" + http_port = 80 + https_port = 443 + origin_host_header = "www.contoso.com" + priority = 1 + weight = 1 +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) update(data acceptance.TestData) string { + template := r.template(data, "Standard_AzureFrontDoor", false) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = false + host_name = "contoso.com" + http_port = 80 + https_port = 443 + origin_host_header = "www.contoso.com" + priority = 1 + weight = 1 +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) privateLinkBlobPrimary(data acceptance.TestData) string { + template := r.templatePrivateLinkStorage(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = true + host_name = azurerm_storage_account.test.primary_blob_host + origin_host_header = azurerm_storage_account.test.primary_blob_host + priority = 1 + weight = 500 + + private_link { + request_message = "Request access for CDN Frontdoor Private Link Origin" + target_type = "blob" + location = azurerm_resource_group.test.location + private_link_target_id = azurerm_storage_account.test.id + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) privateLinkStaticWebSite(data acceptance.TestData) string { + template := r.templatePrivateLinkStorageStaticWebSite(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = true + host_name = azurerm_storage_account.test.primary_web_host + origin_host_header = azurerm_storage_account.test.primary_web_host + priority = 1 + weight = 500 + + private_link { + request_message = "Request access for CDN Frontdoor Private Link Origin" + target_type = "web" + location = azurerm_resource_group.test.location + private_link_target_id = azurerm_storage_account.test.id + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) privateLinkAppServices(data acceptance.TestData) string { + template := r.templatePrivateLinkWebApp(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = true + host_name = azurerm_linux_web_app.test.default_hostname + origin_host_header = azurerm_linux_web_app.test.default_hostname + priority = 1 + weight = 500 + + private_link { + request_message = "Request access for CDN Frontdoor Private Link Origin" + target_type = "sites" + location = azurerm_resource_group.test.location + private_link_target_id = azurerm_linux_web_app.test.id + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorOriginResource) privateLinkLoadBalancer(data acceptance.TestData) string { + template := r.templatePrivateLinkLoadBalancer(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_origin" "test" { + name = "acctest-cdnfdorigin-%d" + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.test.id + + health_probes_enabled = true + certificate_name_check_enabled = true + host_name = azurerm_private_link_service.test.nat_ip_configuration.0.private_ip_address + origin_host_header = azurerm_private_link_service.test.nat_ip_configuration.0.private_ip_address + priority = 1 + weight = 500 + + private_link { + request_message = "Request access for CDN Frontdoor Private Link Origin" + location = azurerm_resource_group.test.location + private_link_target_id = azurerm_private_link_service.test.id + } +} +`, template, data.RandomInteger) +} + +func (CdnFrontDoorOriginResource) template(data acceptance.TestData, profileSku string, isLoadBalancer bool) string { + // NOTE: This is a hack for what I believe is a bug in the CDN Frontdoor API. I am currently speaking with the service + // team about how to correctly fix this issue, but in the meantime this is what we need to do to get this scenario to + // work. + var loadBalancerDependsOn string + if isLoadBalancer { + loadBalancerDependsOn = "depends_on = [azurerm_private_link_service.test]" + } + + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-cdn-afdx-%d" + location = "%s" +} + +resource "azurerm_cdn_frontdoor_profile" "test" { + %s + name = "acctest-cdnfdprofile-%d" + resource_group_name = azurerm_resource_group.test.name + sku_name = %q +} + +resource "azurerm_cdn_frontdoor_origin_group" "test" { + name = "acctest-cdnfd-group-%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + load_balancing { + additional_latency_in_milliseconds = 0 + sample_size = 16 + successful_samples_required = 3 + } +} +`, data.RandomInteger, data.Locations.Primary, loadBalancerDependsOn, data.RandomInteger, profileSku, data.RandomInteger) +} diff --git a/internal/services/cdn/cdn_frontdoor_rule_set_data_source_test.go b/internal/services/cdn/cdn_frontdoor_rule_set_data_source_test.go index e8c1b47ccfb3..9ddf20b3ad01 100644 --- a/internal/services/cdn/cdn_frontdoor_rule_set_data_source_test.go +++ b/internal/services/cdn/cdn_frontdoor_rule_set_data_source_test.go @@ -30,8 +30,8 @@ func (CdnFrontDoorRuleSetDataSource) basic(data acceptance.TestData) string { data "azurerm_cdn_frontdoor_rule_set" "test" { name = azurerm_cdn_frontdoor_rule_set.test.name - profile_name = azurerm_cdn_frontdoor_rule_set.test.profile_name - resource_group_name = azurerm_cdn_frontdoor_rule_set.test.resource_group_name + profile_name = azurerm_cdn_frontdoor_profile.test.name + resource_group_name = azurerm_cdn_frontdoor_profile.test.resource_group_name } `, CdnFrontDoorRuleSetResource{}.complete(data)) } diff --git a/internal/services/cdn/cdn_frontdoor_security_policy_resource.go b/internal/services/cdn/cdn_frontdoor_security_policy_resource.go new file mode 100644 index 000000000000..493d02081da9 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_security_policy_resource.go @@ -0,0 +1,281 @@ +package cdn + +import ( + "fmt" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + cdnfrontdoorsecurityparams "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/frontdoorsecurityparams" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceCdnFrontDoorSecurityPolicy() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceCdnFrontdoorSecurityPolicyCreate, + Read: resourceCdnFrontdoorSecurityPolicyRead, + Delete: resourceCdnFrontdoorSecurityPolicyDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FrontDoorSecurityPolicyID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "cdn_frontdoor_profile_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorProfileID, + }, + + "security_policies": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + + "firewall": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + + "cdn_frontdoor_firewall_policy_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorFirewallPolicyID, + }, + + "association": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + + // NOTE: The max number of domains vary depending on sku: 100 Standard, 500 Premium + "domain": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 500, + + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "cdn_frontdoor_domain_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FrontDoorSecurityPolicyDomainID, + }, + + "active": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + }, + }, + }, + + // NOTE: Per the service team the only acceptable value as of GA is "/*" + "patterns_to_match": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "/*", + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceCdnFrontdoorSecurityPolicyCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorSecurityPoliciesClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + // NOTE: The profile id is used to retrieve properties from the related profile that must match in this security policy + profileId, err := parse.FrontDoorProfileID(d.Get("cdn_frontdoor_profile_id").(string)) + if err != nil { + return err + } + + securityPolicyName := d.Get("name").(string) + id := parse.NewFrontDoorSecurityPolicyID(profileId.SubscriptionId, profileId.ResourceGroup, profileId.ProfileName, securityPolicyName) + + existing, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_cdn_frontdoor_security_policy", id.ID()) + } + + profileClient := meta.(*clients.Client).Cdn.FrontDoorProfileClient + profile, err := profileClient.Get(ctx, profileId.ResourceGroup, profileId.ProfileName) + if err != nil { + return fmt.Errorf("unable to retrieve the %q from the linked %q: %+v", "sku_name", "azurerm_cdn_frontdoor_profile", err) + } + + if profile.Sku == nil { + return fmt.Errorf("retreving the parent %q: `sku` was nil", *profileId) + } + + isStandardSku := strings.HasPrefix(strings.ToLower(string(profile.Sku.Name)), "standard") + + params, err := cdnfrontdoorsecurityparams.ExpandCdnFrontdoorFirewallPolicyParameters(d.Get("security_policies").([]interface{}), isStandardSku) + if err != nil { + return fmt.Errorf("expanding %q: %+v", "security_policies", err) + } + + props := cdn.SecurityPolicy{ + SecurityPolicyProperties: &cdn.SecurityPolicyProperties{ + Parameters: params, + }, + } + + future, err := client.Create(ctx, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName, props) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the creation of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceCdnFrontdoorSecurityPolicyRead(d, meta) +} + +func resourceCdnFrontdoorSecurityPolicyRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorSecurityPoliciesClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorSecurityPolicyID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.SecurityPolicyName) + d.Set("cdn_frontdoor_profile_id", parse.NewFrontDoorProfileID(id.SubscriptionId, id.ResourceGroup, id.ProfileName).ID()) + + if props := resp.SecurityPolicyProperties; props != nil { + waf, ok := props.Parameters.AsSecurityPolicyWebApplicationFirewallParameters() + if !ok { + return fmt.Errorf("flattening %s: %s", id, "expected security policy web application firewall parameters") + } + + // we know it's a firewall policy at this point, + // create the objects to hold the policy data + associations := make([]interface{}, 0) + + wafPolicyId := "" + if waf.WafPolicy != nil && waf.WafPolicy.ID != nil { + wafPolicyId = *waf.WafPolicy.ID + } + + if waf.Associations != nil { + for _, item := range *waf.Associations { + associations = append(associations, map[string]interface{}{ + "domain": cdnfrontdoorsecurityparams.FlattenSecurityPoliciesActivatedResourceReference(item.Domains), + "patterns_to_match": utils.FlattenStringSlice(item.PatternsToMatch), + }) + } + } + + securityPolicy := []interface{}{ + map[string]interface{}{ + "firewall": []interface{}{ + map[string]interface{}{ + "association": associations, + "cdn_frontdoor_firewall_policy_id": wafPolicyId, + }, + }, + }, + } + + d.Set("security_policies", securityPolicy) + } + + return nil +} + +func resourceCdnFrontdoorSecurityPolicyDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Cdn.FrontDoorSecurityPoliciesClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FrontDoorSecurityPolicyID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) + } + + return nil +} diff --git a/internal/services/cdn/cdn_frontdoor_security_policy_resource_test.go b/internal/services/cdn/cdn_frontdoor_security_policy_resource_test.go new file mode 100644 index 000000000000..5ec0df6682e0 --- /dev/null +++ b/internal/services/cdn/cdn_frontdoor_security_policy_resource_test.go @@ -0,0 +1,460 @@ +package cdn_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CdnFrontDoorSecurityPolicyResource struct{} + +func TestAccCdnFrontDoorSecurityPolicy_basic(t *testing.T) { + t.Skip("@WodansSon: Skipping test until Cdn FrontDoor Custom Domain resource is implemented") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // TODO: we can remove these import-ignores by fixing the resource + // WS: Fixed, custom domain needs the profile id during create because it needs the name + data.ImportStep("azurerm_cdn_frontdoor_custom_domain.test.cdn_frontdoor_profile_id"), + }) +} + +func TestAccCdnFrontDoorSecurityPolicy_basicEndpoint(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicEndpoint(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCdnFrontDoorSecurityPolicy_requiresImport(t *testing.T) { + t.Skip("@WodansSon: Skipping test until Cdn FrontDoor Custom Domain resource is implemented") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCdnFrontDoorSecurityPolicy_requiresImportEndpoint(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicEndpoint(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImportEndpoint), + }) +} + +func TestAccCdnFrontDoorSecurityPolicy_complete(t *testing.T) { + t.Skip("@WodansSon: Skipping test until Cdn FrontDoor Custom Domain resource is implemented") + + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // TODO: we can remove these import-ignores by fixing the resource + // WS: Fixed, custom domain needs the profile id during create because it needs the name + data.ImportStep("azurerm_cdn_frontdoor_custom_domain.test.cdn_frontdoor_profile_id"), + }) +} + +func TestAccCdnFrontDoorSecurityPolicy_completeEndpoint(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cdn_frontdoor_security_policy", "test") + r := CdnFrontDoorSecurityPolicyResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.completeEndpoint(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // TODO: we can remove these import-ignores by fixing the resource + // WS: Fixed, custom domain needs the profile id during create because it needs the name + data.ImportStep(), + }) +} + +func (r CdnFrontDoorSecurityPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.FrontDoorSecurityPolicyID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Cdn.FrontDoorSecurityPoliciesClient + resp, err := client.Get(ctx, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (r CdnFrontDoorSecurityPolicyResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-cdn-afdx-%[1]d" + location = "%s" +} + +resource "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestWAF%[1]d" + resource_group_name = azurerm_resource_group.test.name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_name + enabled = true + mode = "Prevention" + redirect_url = "https://www.fabrikam.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"] + } + } + + managed_rule { + type = "DefaultRuleSet" + version = "preview-0.1" + action = "Block" + + override { + rule_group_name = "PHP" + + rule { + rule_id = "933111" + enabled = false + action = "Block" + } + } + } + + managed_rule { + type = "BotProtection" + version = "preview-0.1" + action = "Block" + } +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%[1]d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_cdn_frontdoor_profile" "test" { + name = "accTestProfile-%[1]d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "Premium_AzureFrontDoor" +} + +resource "azurerm_cdn_frontdoor_custom_domain" "test" { + name = "accTestCustomDomain-%[1]d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + dns_zone_id = azurerm_dns_zone.test.id + host_name = join(".", ["fabrikam", azurerm_dns_zone.test.name]) + + tls { + certificate_type = "ManagedCertificate" + minimum_tls_version = "TLS12" + } +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r CdnFrontDoorSecurityPolicyResource) templateEndpoint(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-cdn-afdx-%[1]d" + location = "%s" +} + +resource "azurerm_cdn_frontdoor_firewall_policy" "test" { + name = "accTestWAF%[1]d" + resource_group_name = azurerm_resource_group.test.name + sku_name = azurerm_cdn_frontdoor_profile.test.sku_name + enabled = true + mode = "Prevention" + redirect_url = "https://www.fabrikam.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"] + } + } + + managed_rule { + type = "DefaultRuleSet" + version = "preview-0.1" + action = "Block" + + override { + rule_group_name = "PHP" + + rule { + rule_id = "933111" + enabled = false + action = "Block" + } + } + } + + managed_rule { + type = "BotProtection" + version = "preview-0.1" + action = "Block" + } +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%[1]d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_cdn_frontdoor_profile" "test" { + name = "accTestProfile-%[1]d" + resource_group_name = azurerm_resource_group.test.name + sku_name = "Premium_AzureFrontDoor" +} + +resource "azurerm_cdn_frontdoor_endpoint" "test" { + name = "acctest-cdnfdendpoint-%[1]d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r CdnFrontDoorSecurityPolicyResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_security_policy" "test" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_custom_domain.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorSecurityPolicyResource) basicEndpoint(data acceptance.TestData) string { + template := r.templateEndpoint(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_security_policy" "test" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorSecurityPolicyResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_security_policy" "import" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_custom_domain.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, config, data.RandomInteger) +} + +func (r CdnFrontDoorSecurityPolicyResource) requiresImportEndpoint(data acceptance.TestData) string { + config := r.basicEndpoint(data) + return fmt.Sprintf(` +%s + +resource "azurerm_cdn_frontdoor_security_policy" "import" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, config, data.RandomInteger) +} + +func (r CdnFrontDoorSecurityPolicyResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_security_policy" "test" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_custom_domain.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, template, data.RandomInteger) +} + +func (r CdnFrontDoorSecurityPolicyResource) completeEndpoint(data acceptance.TestData) string { + template := r.templateEndpoint(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_cdn_frontdoor_security_policy" "test" { + name = "accTestSecPol%d" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.test.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.test.id + + association { + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.test.id + } + + patterns_to_match = ["/*"] + } + } + } +} +`, template, data.RandomInteger) +} diff --git a/internal/services/cdn/client/client.go b/internal/services/cdn/client/client.go index f805f89c9e1d..bf75b270da9d 100644 --- a/internal/services/cdn/client/client.go +++ b/internal/services/cdn/client/client.go @@ -8,20 +8,20 @@ import ( ) type Client struct { - FrontDoorEndpointsClient *cdnFrontDoorSdk.AFDEndpointsClient - FrontDoorOriginGroupsClient *cdnFrontDoorSdk.AFDOriginGroupsClient - FrontDoorOriginsClient *cdnFrontDoorSdk.AFDOriginsClient - FrontDoorCustomDomainsClient *cdnFrontDoorSdk.AFDCustomDomainsClient - FrontDoorSecurityPoliciesClient *cdnFrontDoorSdk.SecurityPoliciesClient - FrontDoorRoutesClient *cdnFrontDoorSdk.RoutesClient - FrontDoorRulesClient *cdnFrontDoorSdk.RulesClient - FrontDoorProfileClient *cdnFrontDoorSdk.ProfilesClient - FrontDoorSecretsClient *cdnFrontDoorSdk.SecretsClient - FrontDoorRuleSetsClient *cdnFrontDoorSdk.RuleSetsClient - FrontDoorLegacyPoliciesClient *frontdoor.PoliciesClient - CustomDomainsClient *cdnSdk.CustomDomainsClient - EndpointsClient *cdnSdk.EndpointsClient - ProfilesClient *cdnSdk.ProfilesClient + FrontDoorEndpointsClient *cdnFrontDoorSdk.AFDEndpointsClient + FrontDoorOriginGroupsClient *cdnFrontDoorSdk.AFDOriginGroupsClient + FrontDoorOriginsClient *cdnFrontDoorSdk.AFDOriginsClient + FrontDoorCustomDomainsClient *cdnFrontDoorSdk.AFDCustomDomainsClient + FrontDoorSecurityPoliciesClient *cdnFrontDoorSdk.SecurityPoliciesClient + FrontDoorRoutesClient *cdnFrontDoorSdk.RoutesClient + FrontDoorRulesClient *cdnFrontDoorSdk.RulesClient + FrontDoorProfileClient *cdnFrontDoorSdk.ProfilesClient + FrontDoorSecretsClient *cdnFrontDoorSdk.SecretsClient + FrontDoorRuleSetsClient *cdnFrontDoorSdk.RuleSetsClient + FrontDoorLegacyFirewallPoliciesClient *frontdoor.PoliciesClient + CustomDomainsClient *cdnSdk.CustomDomainsClient + EndpointsClient *cdnSdk.EndpointsClient + ProfilesClient *cdnSdk.ProfilesClient } func NewClient(o *common.ClientOptions) *Client { @@ -40,8 +40,8 @@ func NewClient(o *common.ClientOptions) *Client { frontDoorPolicySecurityPoliciesClient := cdnFrontDoorSdk.NewSecurityPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&frontDoorPolicySecurityPoliciesClient.Client, o.ResourceManagerAuthorizer) - frontDoorLegacyPoliciesClient := frontdoor.NewPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) - o.ConfigureClient(&frontDoorLegacyPoliciesClient.Client, o.ResourceManagerAuthorizer) + frontDoorLegacyFirewallPoliciesClient := frontdoor.NewPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&frontDoorLegacyFirewallPoliciesClient.Client, o.ResourceManagerAuthorizer) frontDoorRoutesClient := cdnFrontDoorSdk.NewRoutesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&frontDoorRoutesClient.Client, o.ResourceManagerAuthorizer) @@ -68,19 +68,19 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&profilesClient.Client, o.ResourceManagerAuthorizer) return &Client{ - FrontDoorEndpointsClient: &frontDoorEndpointsClient, - FrontDoorOriginGroupsClient: &frontDoorOriginGroupsClient, - FrontDoorOriginsClient: &frontDoorOriginsClient, - FrontDoorCustomDomainsClient: &frontDoorCustomDomainsClient, - FrontDoorSecurityPoliciesClient: &frontDoorPolicySecurityPoliciesClient, - FrontDoorRoutesClient: &frontDoorRoutesClient, - FrontDoorRulesClient: &frontDoorRulesClient, - FrontDoorProfileClient: &frontDoorProfilesClient, - FrontDoorSecretsClient: &frontDoorPolicySecretsClient, - FrontDoorRuleSetsClient: &frontDoorRuleSetsClient, - FrontDoorLegacyPoliciesClient: &frontDoorLegacyPoliciesClient, - CustomDomainsClient: &customDomainsClient, - EndpointsClient: &endpointsClient, - ProfilesClient: &profilesClient, + FrontDoorEndpointsClient: &frontDoorEndpointsClient, + FrontDoorOriginGroupsClient: &frontDoorOriginGroupsClient, + FrontDoorOriginsClient: &frontDoorOriginsClient, + FrontDoorCustomDomainsClient: &frontDoorCustomDomainsClient, + FrontDoorSecurityPoliciesClient: &frontDoorPolicySecurityPoliciesClient, + FrontDoorRoutesClient: &frontDoorRoutesClient, + FrontDoorRulesClient: &frontDoorRulesClient, + FrontDoorProfileClient: &frontDoorProfilesClient, + FrontDoorSecretsClient: &frontDoorPolicySecretsClient, + FrontDoorRuleSetsClient: &frontDoorRuleSetsClient, + FrontDoorLegacyFirewallPoliciesClient: &frontDoorLegacyFirewallPoliciesClient, + CustomDomainsClient: &customDomainsClient, + EndpointsClient: &endpointsClient, + ProfilesClient: &profilesClient, } } diff --git a/internal/services/cdn/frontdoorsecurityparams/cdn_frontdoor_security_params.go b/internal/services/cdn/frontdoorsecurityparams/cdn_frontdoor_security_params.go new file mode 100644 index 000000000000..f3daa9df11c2 --- /dev/null +++ b/internal/services/cdn/frontdoorsecurityparams/cdn_frontdoor_security_params.go @@ -0,0 +1,110 @@ +package cdnfrontdoorsecurityparams + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CdnFrontdoorSecurityParameters struct { + TypeName cdn.Type + ConfigName string +} + +type CdnFrontdoorSecurityMappings struct { + Firewall CdnFrontdoorSecurityParameters +} + +func ExpandCdnFrontdoorFirewallPolicyParameters(input []interface{}, isStandardSku bool) (*cdn.SecurityPolicyWebApplicationFirewallParameters, error) { + results := cdn.SecurityPolicyWebApplicationFirewallParameters{} + if len(input) == 0 { + return &results, nil + } + + associations := make([]cdn.SecurityPolicyWebApplicationFirewallAssociation, 0) + + // pull off only the firewall policy from the security_policies list + policyType := input[0].(map[string]interface{}) + firewallPolicy := policyType["firewall"].([]interface{}) + v := firewallPolicy[0].(map[string]interface{}) + + if id := v["cdn_frontdoor_firewall_policy_id"].(string); id != "" { + results.WafPolicy = &cdn.ResourceReference{ + ID: utils.String(id), + } + } + + configAssociations := v["association"].([]interface{}) + + for _, item := range configAssociations { + v := item.(map[string]interface{}) + domains := expandSecurityPoliciesActivatedResourceReference(v["domain"].([]interface{})) + + if isStandardSku { + if len(*domains) > 100 { + return &results, fmt.Errorf("the %q sku is only allowed to have 100 or less domains associated with the firewall policy, got %d", cdn.SkuNameStandardAzureFrontDoor, len(*domains)) + } + } else { + if len(*domains) > 500 { + return &results, fmt.Errorf("the %q sku is only allowed to have 500 or less domains associated with the firewall policy, got %d", cdn.SkuNamePremiumAzureFrontDoor, len(*domains)) + } + } + + association := cdn.SecurityPolicyWebApplicationFirewallAssociation{ + Domains: domains, + PatternsToMatch: utils.ExpandStringSlice(v["patterns_to_match"].([]interface{})), + } + + associations = append(associations, association) + } + + results.Associations = &associations + + return &results, nil +} + +func expandSecurityPoliciesActivatedResourceReference(input []interface{}) *[]cdn.ActivatedResourceReference { + results := make([]cdn.ActivatedResourceReference, 0) + if len(input) == 0 { + return &results + } + + for _, item := range input { + v := item.(map[string]interface{}) + + if id := v["cdn_frontdoor_domain_id"].(string); id != "" { + results = append(results, cdn.ActivatedResourceReference{ + ID: utils.String(id), + }) + } + } + + return &results +} + +func FlattenSecurityPoliciesActivatedResourceReference(input *[]cdn.ActivatedResourceReference) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range *input { + frontDoorDomainId := "" + if item.ID != nil { + frontDoorDomainId = *item.ID + } + + active := false + if item.IsActive != nil { + active = *item.IsActive + } + + results = append(results, map[string]interface{}{ + "active": active, + "cdn_frontdoor_domain_id": frontDoorDomainId, + }) + } + + return results +} diff --git a/internal/services/cdn/parse/front_door_custom_domain.go b/internal/services/cdn/parse/front_door_custom_domain.go new file mode 100644 index 000000000000..4d13da872b67 --- /dev/null +++ b/internal/services/cdn/parse/front_door_custom_domain.go @@ -0,0 +1,131 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontDoorCustomDomainId struct { + SubscriptionId string + ResourceGroup string + ProfileName string + CustomDomainName string +} + +func NewFrontDoorCustomDomainID(subscriptionId, resourceGroup, profileName, customDomainName string) FrontDoorCustomDomainId { + return FrontDoorCustomDomainId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ProfileName: profileName, + CustomDomainName: customDomainName, + } +} + +func (id FrontDoorCustomDomainId) String() string { + segments := []string{ + fmt.Sprintf("Custom Domain Name %q", id.CustomDomainName), + fmt.Sprintf("Profile Name %q", id.ProfileName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Front Door Custom Domain", segmentsStr) +} + +func (id FrontDoorCustomDomainId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Cdn/profiles/%s/customDomains/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.CustomDomainName) +} + +// FrontDoorCustomDomainID parses a FrontDoorCustomDomain ID into an FrontDoorCustomDomainId struct +func FrontDoorCustomDomainID(input string) (*FrontDoorCustomDomainId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorCustomDomainId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ProfileName, err = id.PopSegment("profiles"); err != nil { + return nil, err + } + if resourceId.CustomDomainName, err = id.PopSegment("customDomains"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontDoorCustomDomainIDInsensitively parses an FrontDoorCustomDomain ID into an FrontDoorCustomDomainId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontDoorCustomDomainID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontDoorCustomDomainIDInsensitively(input string) (*FrontDoorCustomDomainId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorCustomDomainId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'profiles' segment + profilesKey := "profiles" + for key := range id.Path { + if strings.EqualFold(key, profilesKey) { + profilesKey = key + break + } + } + if resourceId.ProfileName, err = id.PopSegment(profilesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'customDomains' segment + customDomainsKey := "customDomains" + for key := range id.Path { + if strings.EqualFold(key, customDomainsKey) { + customDomainsKey = key + break + } + } + if resourceId.CustomDomainName, err = id.PopSegment(customDomainsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/front_door_custom_domain_test.go b/internal/services/cdn/parse/front_door_custom_domain_test.go new file mode 100644 index 000000000000..a6380ae021a4 --- /dev/null +++ b/internal/services/cdn/parse/front_door_custom_domain_test.go @@ -0,0 +1,264 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontDoorCustomDomainId{} + +func TestFrontDoorCustomDomainIDFormatter(t *testing.T) { + actual := NewFrontDoorCustomDomainID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "customDomain1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontDoorCustomDomainID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorCustomDomainId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1", + Expected: &FrontDoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/CUSTOMDOMAINS/CUSTOMDOMAIN1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorCustomDomainID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.CustomDomainName != v.Expected.CustomDomainName { + t.Fatalf("Expected %q but got %q for CustomDomainName", v.Expected.CustomDomainName, actual.CustomDomainName) + } + } +} + +func TestFrontDoorCustomDomainIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorCustomDomainId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1", + Expected: &FrontDoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customdomains/customDomain1", + Expected: &FrontDoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/CUSTOMDOMAINS/customDomain1", + Expected: &FrontDoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/CuStOmDoMaInS/customDomain1", + Expected: &FrontDoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorCustomDomainIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.CustomDomainName != v.Expected.CustomDomainName { + t.Fatalf("Expected %q but got %q for CustomDomainName", v.Expected.CustomDomainName, actual.CustomDomainName) + } + } +} diff --git a/internal/services/cdn/parse/front_door_endpoint_test.go b/internal/services/cdn/parse/front_door_endpoint_test.go index 488adfeefeae..485c2acc7841 100644 --- a/internal/services/cdn/parse/front_door_endpoint_test.go +++ b/internal/services/cdn/parse/front_door_endpoint_test.go @@ -11,8 +11,8 @@ import ( var _ resourceids.Id = FrontDoorEndpointId{} func TestFrontDoorEndpointIDFormatter(t *testing.T) { - actual := NewFrontDoorEndpointID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "profile1", "endpoint1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1" + actual := NewFrontDoorEndpointID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "endpoint1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -57,34 +57,34 @@ func TestFrontDoorEndpointID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", Expected: &FrontDoorEndpointId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", AfdEndpointName: "endpoint1", }, @@ -92,7 +92,7 @@ func TestFrontDoorEndpointID(t *testing.T) { { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/AFDENDPOINTS/ENDPOINT1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/AFDENDPOINTS/ENDPOINT1", Error: true, }, } @@ -166,34 +166,34 @@ func TestFrontDoorEndpointIDInsensitively(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", Expected: &FrontDoorEndpointId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", AfdEndpointName: "endpoint1", }, @@ -201,10 +201,10 @@ func TestFrontDoorEndpointIDInsensitively(t *testing.T) { { // lower-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdendpoints/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdendpoints/endpoint1", Expected: &FrontDoorEndpointId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", AfdEndpointName: "endpoint1", }, @@ -212,10 +212,10 @@ func TestFrontDoorEndpointIDInsensitively(t *testing.T) { { // upper-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PROFILES/profile1/AFDENDPOINTS/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/AFDENDPOINTS/endpoint1", Expected: &FrontDoorEndpointId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", AfdEndpointName: "endpoint1", }, @@ -223,10 +223,10 @@ func TestFrontDoorEndpointIDInsensitively(t *testing.T) { { // mixed-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/AfDeNdPoInTs/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/AfDeNdPoInTs/endpoint1", Expected: &FrontDoorEndpointId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", AfdEndpointName: "endpoint1", }, diff --git a/internal/services/cdn/parse/front_door_firewall_policy.go b/internal/services/cdn/parse/front_door_firewall_policy.go new file mode 100644 index 000000000000..89aed188c84e --- /dev/null +++ b/internal/services/cdn/parse/front_door_firewall_policy.go @@ -0,0 +1,113 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontDoorFirewallPolicyId struct { + SubscriptionId string + ResourceGroup string + FrontDoorWebApplicationFirewallPolicyName string +} + +func NewFrontDoorFirewallPolicyID(subscriptionId, resourceGroup, frontDoorWebApplicationFirewallPolicyName string) FrontDoorFirewallPolicyId { + return FrontDoorFirewallPolicyId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + FrontDoorWebApplicationFirewallPolicyName: frontDoorWebApplicationFirewallPolicyName, + } +} + +func (id FrontDoorFirewallPolicyId) String() string { + segments := []string{ + fmt.Sprintf("Front Door Web Application Firewall Policy Name %q", id.FrontDoorWebApplicationFirewallPolicyName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Front Door Firewall Policy", segmentsStr) +} + +func (id FrontDoorFirewallPolicyId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.FrontDoorWebApplicationFirewallPolicyName) +} + +// FrontDoorFirewallPolicyID parses a FrontDoorFirewallPolicy ID into an FrontDoorFirewallPolicyId struct +func FrontDoorFirewallPolicyID(input string) (*FrontDoorFirewallPolicyId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorFirewallPolicyId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.FrontDoorWebApplicationFirewallPolicyName, err = id.PopSegment("frontDoorWebApplicationFirewallPolicies"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontDoorFirewallPolicyIDInsensitively parses an FrontDoorFirewallPolicy ID into an FrontDoorFirewallPolicyId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontDoorFirewallPolicyID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontDoorFirewallPolicyIDInsensitively(input string) (*FrontDoorFirewallPolicyId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorFirewallPolicyId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'frontDoorWebApplicationFirewallPolicies' segment + frontDoorWebApplicationFirewallPoliciesKey := "frontDoorWebApplicationFirewallPolicies" + for key := range id.Path { + if strings.EqualFold(key, frontDoorWebApplicationFirewallPoliciesKey) { + frontDoorWebApplicationFirewallPoliciesKey = key + break + } + } + if resourceId.FrontDoorWebApplicationFirewallPolicyName, err = id.PopSegment(frontDoorWebApplicationFirewallPoliciesKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/front_door_firewall_policy_test.go b/internal/services/cdn/parse/front_door_firewall_policy_test.go new file mode 100644 index 000000000000..df69d0c756ef --- /dev/null +++ b/internal/services/cdn/parse/front_door_firewall_policy_test.go @@ -0,0 +1,229 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontDoorFirewallPolicyId{} + +func TestFrontDoorFirewallPolicyIDFormatter(t *testing.T) { + actual := NewFrontDoorFirewallPolicyID("12345678-1234-9876-4563-123456789012", "resGroup1", "policy1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/policy1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontDoorFirewallPolicyID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorFirewallPolicyId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", + Error: true, + }, + + { + // missing value for FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/policy1", + Expected: &FrontDoorFirewallPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + FrontDoorWebApplicationFirewallPolicyName: "policy1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/FRONTDOORWEBAPPLICATIONFIREWALLPOLICIES/POLICY1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorFirewallPolicyID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.FrontDoorWebApplicationFirewallPolicyName != v.Expected.FrontDoorWebApplicationFirewallPolicyName { + t.Fatalf("Expected %q but got %q for FrontDoorWebApplicationFirewallPolicyName", v.Expected.FrontDoorWebApplicationFirewallPolicyName, actual.FrontDoorWebApplicationFirewallPolicyName) + } + } +} + +func TestFrontDoorFirewallPolicyIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorFirewallPolicyId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", + Error: true, + }, + + { + // missing value for FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/policy1", + Expected: &FrontDoorFirewallPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + FrontDoorWebApplicationFirewallPolicyName: "policy1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontdoorwebapplicationfirewallpolicies/policy1", + Expected: &FrontDoorFirewallPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + FrontDoorWebApplicationFirewallPolicyName: "policy1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/FRONTDOORWEBAPPLICATIONFIREWALLPOLICIES/policy1", + Expected: &FrontDoorFirewallPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + FrontDoorWebApplicationFirewallPolicyName: "policy1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/FrOnTdOoRwEbApPlIcAtIoNfIrEwAlLpOlIcIeS/policy1", + Expected: &FrontDoorFirewallPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + FrontDoorWebApplicationFirewallPolicyName: "policy1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorFirewallPolicyIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.FrontDoorWebApplicationFirewallPolicyName != v.Expected.FrontDoorWebApplicationFirewallPolicyName { + t.Fatalf("Expected %q but got %q for FrontDoorWebApplicationFirewallPolicyName", v.Expected.FrontDoorWebApplicationFirewallPolicyName, actual.FrontDoorWebApplicationFirewallPolicyName) + } + } +} diff --git a/internal/services/cdn/parse/front_door_origin.go b/internal/services/cdn/parse/front_door_origin.go new file mode 100644 index 000000000000..ac7a74449423 --- /dev/null +++ b/internal/services/cdn/parse/front_door_origin.go @@ -0,0 +1,149 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontDoorOriginId struct { + SubscriptionId string + ResourceGroup string + ProfileName string + OriginGroupName string + OriginName string +} + +func NewFrontDoorOriginID(subscriptionId, resourceGroup, profileName, originGroupName, originName string) FrontDoorOriginId { + return FrontDoorOriginId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ProfileName: profileName, + OriginGroupName: originGroupName, + OriginName: originName, + } +} + +func (id FrontDoorOriginId) String() string { + segments := []string{ + fmt.Sprintf("Origin Name %q", id.OriginName), + fmt.Sprintf("Origin Group Name %q", id.OriginGroupName), + fmt.Sprintf("Profile Name %q", id.ProfileName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Front Door Origin", segmentsStr) +} + +func (id FrontDoorOriginId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Cdn/profiles/%s/originGroups/%s/origins/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.OriginGroupName, id.OriginName) +} + +// FrontDoorOriginID parses a FrontDoorOrigin ID into an FrontDoorOriginId struct +func FrontDoorOriginID(input string) (*FrontDoorOriginId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorOriginId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ProfileName, err = id.PopSegment("profiles"); err != nil { + return nil, err + } + if resourceId.OriginGroupName, err = id.PopSegment("originGroups"); err != nil { + return nil, err + } + if resourceId.OriginName, err = id.PopSegment("origins"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontDoorOriginIDInsensitively parses an FrontDoorOrigin ID into an FrontDoorOriginId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontDoorOriginID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontDoorOriginIDInsensitively(input string) (*FrontDoorOriginId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorOriginId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'profiles' segment + profilesKey := "profiles" + for key := range id.Path { + if strings.EqualFold(key, profilesKey) { + profilesKey = key + break + } + } + if resourceId.ProfileName, err = id.PopSegment(profilesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'originGroups' segment + originGroupsKey := "originGroups" + for key := range id.Path { + if strings.EqualFold(key, originGroupsKey) { + originGroupsKey = key + break + } + } + if resourceId.OriginGroupName, err = id.PopSegment(originGroupsKey); err != nil { + return nil, err + } + + // find the correct casing for the 'origins' segment + originsKey := "origins" + for key := range id.Path { + if strings.EqualFold(key, originsKey) { + originsKey = key + break + } + } + if resourceId.OriginName, err = id.PopSegment(originsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/front_door_origin_group.go b/internal/services/cdn/parse/front_door_origin_group.go new file mode 100644 index 000000000000..fe1739596e74 --- /dev/null +++ b/internal/services/cdn/parse/front_door_origin_group.go @@ -0,0 +1,131 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontDoorOriginGroupId struct { + SubscriptionId string + ResourceGroup string + ProfileName string + OriginGroupName string +} + +func NewFrontDoorOriginGroupID(subscriptionId, resourceGroup, profileName, originGroupName string) FrontDoorOriginGroupId { + return FrontDoorOriginGroupId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ProfileName: profileName, + OriginGroupName: originGroupName, + } +} + +func (id FrontDoorOriginGroupId) String() string { + segments := []string{ + fmt.Sprintf("Origin Group Name %q", id.OriginGroupName), + fmt.Sprintf("Profile Name %q", id.ProfileName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Front Door Origin Group", segmentsStr) +} + +func (id FrontDoorOriginGroupId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Cdn/profiles/%s/originGroups/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.OriginGroupName) +} + +// FrontDoorOriginGroupID parses a FrontDoorOriginGroup ID into an FrontDoorOriginGroupId struct +func FrontDoorOriginGroupID(input string) (*FrontDoorOriginGroupId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorOriginGroupId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ProfileName, err = id.PopSegment("profiles"); err != nil { + return nil, err + } + if resourceId.OriginGroupName, err = id.PopSegment("originGroups"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontDoorOriginGroupIDInsensitively parses an FrontDoorOriginGroup ID into an FrontDoorOriginGroupId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontDoorOriginGroupID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontDoorOriginGroupIDInsensitively(input string) (*FrontDoorOriginGroupId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorOriginGroupId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'profiles' segment + profilesKey := "profiles" + for key := range id.Path { + if strings.EqualFold(key, profilesKey) { + profilesKey = key + break + } + } + if resourceId.ProfileName, err = id.PopSegment(profilesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'originGroups' segment + originGroupsKey := "originGroups" + for key := range id.Path { + if strings.EqualFold(key, originGroupsKey) { + originGroupsKey = key + break + } + } + if resourceId.OriginGroupName, err = id.PopSegment(originGroupsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/front_door_origin_group_test.go b/internal/services/cdn/parse/front_door_origin_group_test.go new file mode 100644 index 000000000000..0ac837741463 --- /dev/null +++ b/internal/services/cdn/parse/front_door_origin_group_test.go @@ -0,0 +1,264 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontDoorOriginGroupId{} + +func TestFrontDoorOriginGroupIDFormatter(t *testing.T) { + actual := NewFrontDoorOriginGroupID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "originGroup1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontDoorOriginGroupID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorOriginGroupId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1", + Expected: &FrontDoorOriginGroupId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/ORIGINGROUPS/ORIGINGROUP1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorOriginGroupID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.OriginGroupName != v.Expected.OriginGroupName { + t.Fatalf("Expected %q but got %q for OriginGroupName", v.Expected.OriginGroupName, actual.OriginGroupName) + } + } +} + +func TestFrontDoorOriginGroupIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorOriginGroupId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1", + Expected: &FrontDoorOriginGroupId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/origingroups/originGroup1", + Expected: &FrontDoorOriginGroupId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/ORIGINGROUPS/originGroup1", + Expected: &FrontDoorOriginGroupId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/OrIgInGrOuPs/originGroup1", + Expected: &FrontDoorOriginGroupId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorOriginGroupIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.OriginGroupName != v.Expected.OriginGroupName { + t.Fatalf("Expected %q but got %q for OriginGroupName", v.Expected.OriginGroupName, actual.OriginGroupName) + } + } +} diff --git a/internal/services/cdn/parse/front_door_origin_test.go b/internal/services/cdn/parse/front_door_origin_test.go new file mode 100644 index 000000000000..4de2e782d988 --- /dev/null +++ b/internal/services/cdn/parse/front_door_origin_test.go @@ -0,0 +1,299 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontDoorOriginId{} + +func TestFrontDoorOriginIDFormatter(t *testing.T) { + actual := NewFrontDoorOriginID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "originGroup1", "origin1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/origin1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontDoorOriginID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorOriginId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Error: true, + }, + + { + // missing OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/", + Error: true, + }, + + { + // missing value for OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/origin1", + Expected: &FrontDoorOriginId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + OriginName: "origin1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/ORIGINGROUPS/ORIGINGROUP1/ORIGINS/ORIGIN1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorOriginID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.OriginGroupName != v.Expected.OriginGroupName { + t.Fatalf("Expected %q but got %q for OriginGroupName", v.Expected.OriginGroupName, actual.OriginGroupName) + } + if actual.OriginName != v.Expected.OriginName { + t.Fatalf("Expected %q but got %q for OriginName", v.Expected.OriginName, actual.OriginName) + } + } +} + +func TestFrontDoorOriginIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorOriginId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Error: true, + }, + + { + // missing OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/", + Error: true, + }, + + { + // missing value for OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/origin1", + Expected: &FrontDoorOriginId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + OriginName: "origin1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/origingroups/originGroup1/origins/origin1", + Expected: &FrontDoorOriginId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + OriginName: "origin1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/ORIGINGROUPS/originGroup1/ORIGINS/origin1", + Expected: &FrontDoorOriginId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + OriginName: "origin1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/OrIgInGrOuPs/originGroup1/OrIgInS/origin1", + Expected: &FrontDoorOriginId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + OriginGroupName: "originGroup1", + OriginName: "origin1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorOriginIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.OriginGroupName != v.Expected.OriginGroupName { + t.Fatalf("Expected %q but got %q for OriginGroupName", v.Expected.OriginGroupName, actual.OriginGroupName) + } + if actual.OriginName != v.Expected.OriginName { + t.Fatalf("Expected %q but got %q for OriginName", v.Expected.OriginName, actual.OriginName) + } + } +} diff --git a/internal/services/cdn/parse/front_door_profile_test.go b/internal/services/cdn/parse/front_door_profile_test.go index a05a36dac453..e55e4112de1d 100644 --- a/internal/services/cdn/parse/front_door_profile_test.go +++ b/internal/services/cdn/parse/front_door_profile_test.go @@ -11,8 +11,8 @@ import ( var _ resourceids.Id = FrontDoorProfileId{} func TestFrontDoorProfileIDFormatter(t *testing.T) { - actual := NewFrontDoorProfileID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "profile1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1" + actual := NewFrontDoorProfileID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -57,29 +57,29 @@ func TestFrontDoorProfileID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1", Expected: &FrontDoorProfileId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", }, }, { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1", Error: true, }, } @@ -150,52 +150,52 @@ func TestFrontDoorProfileIDInsensitively(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1", Expected: &FrontDoorProfileId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", }, }, { // lower-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1", Expected: &FrontDoorProfileId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", }, }, { // upper-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PROFILES/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1", Expected: &FrontDoorProfileId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", }, }, { // mixed-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1", Expected: &FrontDoorProfileId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", }, }, diff --git a/internal/services/cdn/parse/front_door_rule_set_test.go b/internal/services/cdn/parse/front_door_rule_set_test.go index 45e91d416cf7..736d442839c6 100644 --- a/internal/services/cdn/parse/front_door_rule_set_test.go +++ b/internal/services/cdn/parse/front_door_rule_set_test.go @@ -11,8 +11,8 @@ import ( var _ resourceids.Id = FrontDoorRuleSetId{} func TestFrontDoorRuleSetIDFormatter(t *testing.T) { - actual := NewFrontDoorRuleSetID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "profile1", "ruleSet1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1" + actual := NewFrontDoorRuleSetID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "ruleSet1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -57,34 +57,34 @@ func TestFrontDoorRuleSetID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", Expected: &FrontDoorRuleSetId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", }, @@ -92,7 +92,7 @@ func TestFrontDoorRuleSetID(t *testing.T) { { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1", Error: true, }, } @@ -166,34 +166,34 @@ func TestFrontDoorRuleSetIDInsensitively(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", Expected: &FrontDoorRuleSetId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", }, @@ -201,10 +201,10 @@ func TestFrontDoorRuleSetIDInsensitively(t *testing.T) { { // lower-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/rulesets/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/rulesets/ruleSet1", Expected: &FrontDoorRuleSetId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", }, @@ -212,10 +212,10 @@ func TestFrontDoorRuleSetIDInsensitively(t *testing.T) { { // upper-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PROFILES/profile1/RULESETS/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/RULESETS/ruleSet1", Expected: &FrontDoorRuleSetId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", }, @@ -223,10 +223,10 @@ func TestFrontDoorRuleSetIDInsensitively(t *testing.T) { { // mixed-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/RuLeSeTs/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/RuLeSeTs/ruleSet1", Expected: &FrontDoorRuleSetId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", }, diff --git a/internal/services/cdn/parse/front_door_rule_test.go b/internal/services/cdn/parse/front_door_rule_test.go index 84e83c640e65..eb75b42bdab3 100644 --- a/internal/services/cdn/parse/front_door_rule_test.go +++ b/internal/services/cdn/parse/front_door_rule_test.go @@ -11,8 +11,8 @@ import ( var _ resourceids.Id = FrontDoorRuleId{} func TestFrontDoorRuleIDFormatter(t *testing.T) { - actual := NewFrontDoorRuleID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "profile1", "ruleSet1", "rule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1" + actual := NewFrontDoorRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "ruleSet1", "rule1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1" if actual != expected { t.Fatalf("Expected %q but got %q", expected, actual) } @@ -57,46 +57,46 @@ func TestFrontDoorRuleID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Error: true, }, { // missing RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", Error: true, }, { // missing value for RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", Expected: &FrontDoorRuleId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", RuleName: "rule1", @@ -105,7 +105,7 @@ func TestFrontDoorRuleID(t *testing.T) { { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1/RULES/RULE1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1/RULES/RULE1", Error: true, }, } @@ -182,46 +182,46 @@ func TestFrontDoorRuleIDInsensitively(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Error: true, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Error: true, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Error: true, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Error: true, }, { // missing RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", Error: true, }, { // missing value for RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", Error: true, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", Expected: &FrontDoorRuleId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", RuleName: "rule1", @@ -230,10 +230,10 @@ func TestFrontDoorRuleIDInsensitively(t *testing.T) { { // lower-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/rulesets/ruleSet1/rules/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/rulesets/ruleSet1/rules/rule1", Expected: &FrontDoorRuleId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", RuleName: "rule1", @@ -242,10 +242,10 @@ func TestFrontDoorRuleIDInsensitively(t *testing.T) { { // upper-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PROFILES/profile1/RULESETS/ruleSet1/RULES/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/RULESETS/ruleSet1/RULES/rule1", Expected: &FrontDoorRuleId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", RuleName: "rule1", @@ -254,10 +254,10 @@ func TestFrontDoorRuleIDInsensitively(t *testing.T) { { // mixed-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/RuLeSeTs/ruleSet1/RuLeS/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/RuLeSeTs/ruleSet1/RuLeS/rule1", Expected: &FrontDoorRuleId{ SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", + ResourceGroup: "resGroup1", ProfileName: "profile1", RuleSetName: "ruleSet1", RuleName: "rule1", diff --git a/internal/services/cdn/parse/front_door_security_policy.go b/internal/services/cdn/parse/front_door_security_policy.go new file mode 100644 index 000000000000..fa64a9954c49 --- /dev/null +++ b/internal/services/cdn/parse/front_door_security_policy.go @@ -0,0 +1,131 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontDoorSecurityPolicyId struct { + SubscriptionId string + ResourceGroup string + ProfileName string + SecurityPolicyName string +} + +func NewFrontDoorSecurityPolicyID(subscriptionId, resourceGroup, profileName, securityPolicyName string) FrontDoorSecurityPolicyId { + return FrontDoorSecurityPolicyId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ProfileName: profileName, + SecurityPolicyName: securityPolicyName, + } +} + +func (id FrontDoorSecurityPolicyId) String() string { + segments := []string{ + fmt.Sprintf("Security Policy Name %q", id.SecurityPolicyName), + fmt.Sprintf("Profile Name %q", id.ProfileName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Front Door Security Policy", segmentsStr) +} + +func (id FrontDoorSecurityPolicyId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Cdn/profiles/%s/securityPolicies/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.SecurityPolicyName) +} + +// FrontDoorSecurityPolicyID parses a FrontDoorSecurityPolicy ID into an FrontDoorSecurityPolicyId struct +func FrontDoorSecurityPolicyID(input string) (*FrontDoorSecurityPolicyId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorSecurityPolicyId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ProfileName, err = id.PopSegment("profiles"); err != nil { + return nil, err + } + if resourceId.SecurityPolicyName, err = id.PopSegment("securityPolicies"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontDoorSecurityPolicyIDInsensitively parses an FrontDoorSecurityPolicy ID into an FrontDoorSecurityPolicyId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontDoorSecurityPolicyID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontDoorSecurityPolicyIDInsensitively(input string) (*FrontDoorSecurityPolicyId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontDoorSecurityPolicyId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'profiles' segment + profilesKey := "profiles" + for key := range id.Path { + if strings.EqualFold(key, profilesKey) { + profilesKey = key + break + } + } + if resourceId.ProfileName, err = id.PopSegment(profilesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'securityPolicies' segment + securityPoliciesKey := "securityPolicies" + for key := range id.Path { + if strings.EqualFold(key, securityPoliciesKey) { + securityPoliciesKey = key + break + } + } + if resourceId.SecurityPolicyName, err = id.PopSegment(securityPoliciesKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/front_door_security_policy_test.go b/internal/services/cdn/parse/front_door_security_policy_test.go new file mode 100644 index 000000000000..e84ab01eb1b7 --- /dev/null +++ b/internal/services/cdn/parse/front_door_security_policy_test.go @@ -0,0 +1,264 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontDoorSecurityPolicyId{} + +func TestFrontDoorSecurityPolicyIDFormatter(t *testing.T) { + actual := NewFrontDoorSecurityPolicyID("12345678-1234-9876-4563-123456789012", "resGroup1", "profile1", "securityPolicy1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/securityPolicy1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontDoorSecurityPolicyID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorSecurityPolicyId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/securityPolicy1", + Expected: &FrontDoorSecurityPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + SecurityPolicyName: "securityPolicy1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/SECURITYPOLICIES/SECURITYPOLICY1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorSecurityPolicyID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.SecurityPolicyName != v.Expected.SecurityPolicyName { + t.Fatalf("Expected %q but got %q for SecurityPolicyName", v.Expected.SecurityPolicyName, actual.SecurityPolicyName) + } + } +} + +func TestFrontDoorSecurityPolicyIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontDoorSecurityPolicyId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/securityPolicy1", + Expected: &FrontDoorSecurityPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + SecurityPolicyName: "securityPolicy1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securitypolicies/securityPolicy1", + Expected: &FrontDoorSecurityPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + SecurityPolicyName: "securityPolicy1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PROFILES/profile1/SECURITYPOLICIES/securityPolicy1", + Expected: &FrontDoorSecurityPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + SecurityPolicyName: "securityPolicy1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/SeCuRiTyPoLiCiEs/securityPolicy1", + Expected: &FrontDoorSecurityPolicyId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ProfileName: "profile1", + SecurityPolicyName: "securityPolicy1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontDoorSecurityPolicyIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.SecurityPolicyName != v.Expected.SecurityPolicyName { + t.Fatalf("Expected %q but got %q for SecurityPolicyName", v.Expected.SecurityPolicyName, actual.SecurityPolicyName) + } + } +} diff --git a/internal/services/cdn/parse/frontdoor_custom_domain.go b/internal/services/cdn/parse/frontdoor_custom_domain.go new file mode 100644 index 000000000000..3e7576e45d0b --- /dev/null +++ b/internal/services/cdn/parse/frontdoor_custom_domain.go @@ -0,0 +1,131 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type FrontdoorCustomDomainId struct { + SubscriptionId string + ResourceGroup string + ProfileName string + CustomDomainName string +} + +func NewFrontdoorCustomDomainID(subscriptionId, resourceGroup, profileName, customDomainName string) FrontdoorCustomDomainId { + return FrontdoorCustomDomainId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ProfileName: profileName, + CustomDomainName: customDomainName, + } +} + +func (id FrontdoorCustomDomainId) String() string { + segments := []string{ + fmt.Sprintf("Custom Domain Name %q", id.CustomDomainName), + fmt.Sprintf("Profile Name %q", id.ProfileName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Frontdoor Custom Domain", segmentsStr) +} + +func (id FrontdoorCustomDomainId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Cdn/profiles/%s/customDomains/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ProfileName, id.CustomDomainName) +} + +// FrontdoorCustomDomainID parses a FrontdoorCustomDomain ID into an FrontdoorCustomDomainId struct +func FrontdoorCustomDomainID(input string) (*FrontdoorCustomDomainId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontdoorCustomDomainId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ProfileName, err = id.PopSegment("profiles"); err != nil { + return nil, err + } + if resourceId.CustomDomainName, err = id.PopSegment("customDomains"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// FrontdoorCustomDomainIDInsensitively parses an FrontdoorCustomDomain ID into an FrontdoorCustomDomainId struct, insensitively +// This should only be used to parse an ID for rewriting, the FrontdoorCustomDomainID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func FrontdoorCustomDomainIDInsensitively(input string) (*FrontdoorCustomDomainId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := FrontdoorCustomDomainId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'profiles' segment + profilesKey := "profiles" + for key := range id.Path { + if strings.EqualFold(key, profilesKey) { + profilesKey = key + break + } + } + if resourceId.ProfileName, err = id.PopSegment(profilesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'customDomains' segment + customDomainsKey := "customDomains" + for key := range id.Path { + if strings.EqualFold(key, customDomainsKey) { + customDomainsKey = key + break + } + } + if resourceId.CustomDomainName, err = id.PopSegment(customDomainsKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/cdn/parse/frontdoor_custom_domain_test.go b/internal/services/cdn/parse/frontdoor_custom_domain_test.go new file mode 100644 index 000000000000..7373d1bdfa7d --- /dev/null +++ b/internal/services/cdn/parse/frontdoor_custom_domain_test.go @@ -0,0 +1,264 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = FrontdoorCustomDomainId{} + +func TestFrontdoorCustomDomainIDFormatter(t *testing.T) { + actual := NewFrontdoorCustomDomainID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "profile1", "customDomain1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestFrontdoorCustomDomainID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontdoorCustomDomainId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1", + Expected: &FrontdoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/CUSTOMDOMAINS/CUSTOMDOMAIN1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontdoorCustomDomainID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.CustomDomainName != v.Expected.CustomDomainName { + t.Fatalf("Expected %q but got %q for CustomDomainName", v.Expected.CustomDomainName, actual.CustomDomainName) + } + } +} + +func TestFrontdoorCustomDomainIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *FrontdoorCustomDomainId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Error: true, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Error: true, + }, + + { + // missing CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Error: true, + }, + + { + // missing value for CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1", + Expected: &FrontdoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/customdomains/customDomain1", + Expected: &FrontdoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PROFILES/profile1/CUSTOMDOMAINS/customDomain1", + Expected: &FrontdoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/PrOfIlEs/profile1/CuStOmDoMaInS/customDomain1", + Expected: &FrontdoorCustomDomainId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + ProfileName: "profile1", + CustomDomainName: "customDomain1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := FrontdoorCustomDomainIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ProfileName != v.Expected.ProfileName { + t.Fatalf("Expected %q but got %q for ProfileName", v.Expected.ProfileName, actual.ProfileName) + } + if actual.CustomDomainName != v.Expected.CustomDomainName { + t.Fatalf("Expected %q but got %q for CustomDomainName", v.Expected.CustomDomainName, actual.CustomDomainName) + } + } +} diff --git a/internal/services/cdn/registration.go b/internal/services/cdn/registration.go index daa1eff667fe..84356b8e519e 100644 --- a/internal/services/cdn/registration.go +++ b/internal/services/cdn/registration.go @@ -32,9 +32,10 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { "azurerm_cdn_profile": dataSourceCdnProfile(), // FrontDoor - "azurerm_cdn_frontdoor_endpoint": dataSourceCdnFrontDoorEndpoint(), - "azurerm_cdn_frontdoor_profile": dataSourceCdnFrontDoorProfile(), - "azurerm_cdn_frontdoor_rule_set": dataSourceCdnFrontDoorRuleSet(), + "azurerm_cdn_frontdoor_endpoint": dataSourceCdnFrontDoorEndpoint(), + "azurerm_cdn_frontdoor_origin_group": dataSourceCdnFrontDoorOriginGroup(), + "azurerm_cdn_frontdoor_profile": dataSourceCdnFrontDoorProfile(), + "azurerm_cdn_frontdoor_rule_set": dataSourceCdnFrontDoorRuleSet(), } } @@ -47,8 +48,12 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_cdn_profile": resourceCdnProfile(), // FrontDoor - "azurerm_cdn_frontdoor_endpoint": resourceCdnFrontDoorEndpoint(), - "azurerm_cdn_frontdoor_profile": resourceCdnFrontDoorProfile(), - "azurerm_cdn_frontdoor_rule_set": resourceCdnFrontDoorRuleSet(), + "azurerm_cdn_frontdoor_endpoint": resourceCdnFrontDoorEndpoint(), + "azurerm_cdn_frontdoor_firewall_policy": resourceCdnFrontDoorFirewallPolicy(), + "azurerm_cdn_frontdoor_origin": resourceCdnFrontDoorOrigin(), + "azurerm_cdn_frontdoor_origin_group": resourceCdnFrontDoorOriginGroup(), + "azurerm_cdn_frontdoor_profile": resourceCdnFrontDoorProfile(), + "azurerm_cdn_frontdoor_rule_set": resourceCdnFrontDoorRuleSet(), + "azurerm_cdn_frontdoor_security_policy": resourceCdnFrontDoorSecurityPolicy(), } } diff --git a/internal/services/cdn/resourceids.go b/internal/services/cdn/resourceids.go index 8c08a9e353f7..fe6e58d9c0ca 100644 --- a/internal/services/cdn/resourceids.go +++ b/internal/services/cdn/resourceids.go @@ -6,7 +6,12 @@ package cdn //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=CustomDomain -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/endpoints/endpoint1/customDomains/domain1 // CDN FrontDoor -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorEndpoint -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1 -rewrite=true -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorProfile -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1 -rewrite=true -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorRuleSet -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1 -rewrite=true -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorCustomDomain -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorEndpoint -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorFirewallPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/policy1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorOrigin -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/origin1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorOriginGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorProfile -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorRuleSet -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1 -rewrite=true +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FrontDoorSecurityPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/securityPolicy1 -rewrite=true diff --git a/internal/services/cdn/validate/cdn.go b/internal/services/cdn/validate/cdn.go index 54034e4a9e9b..daf4c329b8e2 100644 --- a/internal/services/cdn/validate/cdn.go +++ b/internal/services/cdn/validate/cdn.go @@ -12,21 +12,21 @@ import ( func EndpointDeliveryRuleName() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]*$"), - "The Delivery Rule Name must start with a letter any may only contain letters and numbers.", + "the Delivery Rule Name must start with a letter any may only contain letters and numbers", ) } func RuleActionCacheExpirationDuration() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile(`^(\d+\.)?([0-1][0-9]|[2][0-3]):[0-5][0-9]:[0-5][0-9]$`), - "The Cache duration must be in this format [d.]hh:mm:ss.", + "the Cache duration must be in this format [d.]hh:mm:ss", ) } func RuleActionUrlRedirectPath() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile("^(/.*)?$"), - "The Url Redirect Path must start with a slash.", + "the Url Redirect Path must start with a slash", ) } @@ -35,19 +35,19 @@ func RuleActionUrlRedirectQueryString() pluginsdk.SchemaValidateFunc { querystring := i.(string) if len(querystring) > 100 { - return nil, []error{fmt.Errorf("The Url Query String's max length is 100.")} + return nil, []error{fmt.Errorf("the Url Query String's max length is 100")} } re := regexp.MustCompile("^[?&]") if re.MatchString(querystring) { - return nil, []error{fmt.Errorf("The Url Query String must not start with a question mark or ampersand.")} + return nil, []error{fmt.Errorf("the Url Query String must not start with a question mark or ampersand")} } kvre := regexp.MustCompile("^[^?&]+=[^?&]+$") kvs := strings.Split(querystring, "&") for _, kv := range kvs { if len(kv) > 0 && !kvre.MatchString(kv) { - return nil, []error{fmt.Errorf("The Url Query String must be in = format and separated by an ampersand.")} + return nil, []error{fmt.Errorf("the Url Query String must be in = format and separated by an ampersand")} } } @@ -58,20 +58,20 @@ func RuleActionUrlRedirectQueryString() pluginsdk.SchemaValidateFunc { func RuleActionUrlRedirectFragment() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile("^([^#].*)?$"), - "The Url Fragment must not start with a hash.", + "the Url Fragment must not start with a hash.", ) } func RuleActionUrlRewriteSourcePattern() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile("^/[^\n]{0,259}$"), - "The Url Rewrite Source Pattern must start with a slash and can not have more than 260 characters.", + "the Url Rewrite Source Pattern must start with a slash and can not have more than 260 characters", ) } func RuleActionUrlRewriteDestination() pluginsdk.SchemaValidateFunc { return validation.StringMatch( regexp.MustCompile("^/[^\n]{0,259}$"), - "The Url Rewrite Destination must start with a slash and can not have more than 260 characters.", + "the Url Rewrite Destination must start with a slash and can not have more than 260 characters", ) } diff --git a/internal/services/cdn/validate/front_door_custom_domain_id.go b/internal/services/cdn/validate/front_door_custom_domain_id.go new file mode 100644 index 000000000000..e549b2f41206 --- /dev/null +++ b/internal/services/cdn/validate/front_door_custom_domain_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" +) + +func FrontDoorCustomDomainID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.FrontDoorCustomDomainID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/cdn/validate/front_door_custom_domain_id_test.go b/internal/services/cdn/validate/front_door_custom_domain_id_test.go new file mode 100644 index 000000000000..1027698c1dcb --- /dev/null +++ b/internal/services/cdn/validate/front_door_custom_domain_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestFrontDoorCustomDomainID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Valid: false, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Valid: false, + }, + + { + // missing CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Valid: false, + }, + + { + // missing value for CustomDomainName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/customDomains/customDomain1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/CUSTOMDOMAINS/CUSTOMDOMAIN1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorCustomDomainID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_endpoint_id_test.go b/internal/services/cdn/validate/front_door_endpoint_id_test.go index 4ed43c1f45e4..e02091f71804 100644 --- a/internal/services/cdn/validate/front_door_endpoint_id_test.go +++ b/internal/services/cdn/validate/front_door_endpoint_id_test.go @@ -42,37 +42,37 @@ func TestFrontDoorEndpointID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Valid: false, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Valid: false, }, { // missing AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Valid: false, }, { // missing value for AfdEndpointName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/afdEndpoints/endpoint1", Valid: true, }, { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/AFDENDPOINTS/ENDPOINT1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/AFDENDPOINTS/ENDPOINT1", Valid: false, }, } diff --git a/internal/services/cdn/validate/front_door_firewall_policy_id.go b/internal/services/cdn/validate/front_door_firewall_policy_id.go new file mode 100644 index 000000000000..8835e3e0baaf --- /dev/null +++ b/internal/services/cdn/validate/front_door_firewall_policy_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" +) + +func FrontDoorFirewallPolicyID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.FrontDoorFirewallPolicyID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/cdn/validate/front_door_firewall_policy_id_test.go b/internal/services/cdn/validate/front_door_firewall_policy_id_test.go new file mode 100644 index 000000000000..a650a7f76edc --- /dev/null +++ b/internal/services/cdn/validate/front_door_firewall_policy_id_test.go @@ -0,0 +1,76 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestFrontDoorFirewallPolicyID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", + Valid: false, + }, + + { + // missing value for FrontDoorWebApplicationFirewallPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/frontDoorWebApplicationFirewallPolicies/policy1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/FRONTDOORWEBAPPLICATIONFIREWALLPOLICIES/POLICY1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorFirewallPolicyID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_firewall_policy_name.go b/internal/services/cdn/validate/front_door_firewall_policy_name.go new file mode 100644 index 000000000000..5ceb879a49a2 --- /dev/null +++ b/internal/services/cdn/validate/front_door_firewall_policy_name.go @@ -0,0 +1,15 @@ +package validate + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" +) + +func FrontDoorFirewallPolicyName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `(^[a-zA-Z])([\da-zA-Z]{0,127})$`); !m { + return nil, append(regexErrs, fmt.Errorf(`%q must be between 1 and 128 characters in length, must begin with a letter and may only contain letters and numbers`, k)) + } + + return nil, nil +} diff --git a/internal/services/cdn/validate/front_door_firewall_policy_name_test.go b/internal/services/cdn/validate/front_door_firewall_policy_name_test.go new file mode 100644 index 000000000000..e93199d08388 --- /dev/null +++ b/internal/services/cdn/validate/front_door_firewall_policy_name_test.go @@ -0,0 +1,86 @@ +package validate + +import "testing" + +func TestFrontDoorFirewallPolicyName(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + // Empty + Input: "", + Valid: false, + }, + + { + // Min Len + Input: "A", + Valid: true, + }, + + { + // Max Len + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: true, + }, + + { + // Too Long + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: false, + }, + + { + // Invalid Character + Input: "1%A", + Valid: false, + }, + + { + // Start With Hyphen + Input: "-1", + Valid: false, + }, + + { + // End With Hyphen + Input: "1-", + Valid: false, + }, + + { + // Start With Letter, End With Letter + Input: "AA", + Valid: true, + }, + + { + // Start With Number, End With Number + Input: "11", + Valid: false, + }, + + { + // Start With Letter, End With Number + Input: "A1", + Valid: true, + }, + + { + // Start With Letter, End With Letter and Hyphen Separator + Input: "A-A", + Valid: false, + }, + } + + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorFirewallPolicyName(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Testing value %q, Expected %t but got %t", tc.Input, tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_origin_group_id.go b/internal/services/cdn/validate/front_door_origin_group_id.go new file mode 100644 index 000000000000..9ea19a01282e --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_group_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" +) + +func FrontDoorOriginGroupID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.FrontDoorOriginGroupID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/cdn/validate/front_door_origin_group_id_test.go b/internal/services/cdn/validate/front_door_origin_group_id_test.go new file mode 100644 index 000000000000..4709f450956b --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_group_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestFrontDoorOriginGroupID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Valid: false, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Valid: false, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Valid: false, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/ORIGINGROUPS/ORIGINGROUP1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorOriginGroupID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_origin_group_name.go b/internal/services/cdn/validate/front_door_origin_group_name.go new file mode 100644 index 000000000000..85c209b935f5 --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_group_name.go @@ -0,0 +1,15 @@ +package validate + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" +) + +func FrontDoorOriginGroupName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[\da-zA-Z][-\da-zA-Z]{0,88}[\da-zA-Z]$`); !m { + return nil, append(regexErrs, fmt.Errorf(`%q must be between 2 and 90 characters in length, begin with a letter or number, end with a letter or number and may contain only letters, numbers and hyphens, got %q`, k, i)) + } + + return nil, nil +} diff --git a/internal/services/cdn/validate/front_door_origin_group_name_test.go b/internal/services/cdn/validate/front_door_origin_group_name_test.go new file mode 100644 index 000000000000..e068979dc740 --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_group_name_test.go @@ -0,0 +1,110 @@ +package validate + +import "testing" + +func TestFrontDoorOriginGroupName(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + // Empty + Input: "", + Valid: false, + }, + + { + // Max Len + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: true, + }, + + { + // Too Long + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: false, + }, + + { + // Invalid Character + Input: "1%A", + Valid: false, + }, + + { + // Start With Hyphen + Input: "-1", + Valid: false, + }, + + { + // End With Hyphen + Input: "1-", + Valid: false, + }, + + { + // Too Short + Input: "1", + Valid: false, + }, + + { + // Start With Letter, End With Letter + Input: "AA", + Valid: true, + }, + + { + // Start With Number, End With Number + Input: "11", + Valid: true, + }, + + { + // Start With Letter, End With Number + Input: "A1", + Valid: true, + }, + + { + // Start With Number, End With Letter + Input: "1A", + Valid: true, + }, + + { + // Start With Letter, End With Letter and Hyphen Separator + Input: "A-A", + Valid: true, + }, + + { + // Start With Number, End With Number and Hyphen Separator + Input: "1-1", + Valid: true, + }, + + { + // Start With Letter, End With Number and Hyphen Separator + Input: "A-1", + Valid: true, + }, + + { + // Start With Number, End With Letter and Hyphen Separator + Input: "1-A", + Valid: true, + }, + } + + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorOriginGroupName(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_origin_id.go b/internal/services/cdn/validate/front_door_origin_id.go new file mode 100644 index 000000000000..f20ff81b846f --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" +) + +func FrontDoorOriginID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.FrontDoorOriginID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/cdn/validate/front_door_origin_id_test.go b/internal/services/cdn/validate/front_door_origin_id_test.go new file mode 100644 index 000000000000..b904162867b0 --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_id_test.go @@ -0,0 +1,100 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestFrontDoorOriginID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Valid: false, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Valid: false, + }, + + { + // missing OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Valid: false, + }, + + { + // missing value for OriginGroupName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/", + Valid: false, + }, + + { + // missing OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/", + Valid: false, + }, + + { + // missing value for OriginName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/originGroups/originGroup1/origins/origin1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/ORIGINGROUPS/ORIGINGROUP1/ORIGINS/ORIGIN1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorOriginID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_origin_name.go b/internal/services/cdn/validate/front_door_origin_name.go new file mode 100644 index 000000000000..4fd1a1c8648c --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_name.go @@ -0,0 +1,15 @@ +package validate + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" +) + +func FrontDoorOriginName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[\da-zA-Z][-\da-zA-Z]{0,88}[\da-zA-Z]$`); !m { + return nil, append(regexErrs, fmt.Errorf(`%q must be between 2 and 90 characters in length, begin with a letter or number, end with a letter or number and may contain only letters, numbers and hyphens, got %q`, k, i)) + } + + return nil, nil +} diff --git a/internal/services/cdn/validate/front_door_origin_name_test.go b/internal/services/cdn/validate/front_door_origin_name_test.go new file mode 100644 index 000000000000..5126b552cd86 --- /dev/null +++ b/internal/services/cdn/validate/front_door_origin_name_test.go @@ -0,0 +1,110 @@ +package validate + +import "testing" + +func TestFrontDoorOriginName(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + // Empty + Input: "", + Valid: false, + }, + + { + // Max Len + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: true, + }, + + { + // Too Long + Input: "AAAAAAAAAAAAAHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + Valid: false, + }, + + { + // Invalid Character + Input: "1%A", + Valid: false, + }, + + { + // Start With Hyphen + Input: "-1", + Valid: false, + }, + + { + // End With Hyphen + Input: "1-", + Valid: false, + }, + + { + // Too Short + Input: "1", + Valid: false, + }, + + { + // Start With Letter, End With Letter + Input: "AA", + Valid: true, + }, + + { + // Start With Number, End With Number + Input: "11", + Valid: true, + }, + + { + // Start With Letter, End With Number + Input: "A1", + Valid: true, + }, + + { + // Start With Number, End With Letter + Input: "1A", + Valid: true, + }, + + { + // Start With Letter, End With Letter and Hyphen Separator + Input: "A-A", + Valid: true, + }, + + { + // Start With Number, End With Number and Hyphen Separator + Input: "1-1", + Valid: true, + }, + + { + // Start With Letter, End With Number and Hyphen Separator + Input: "A-1", + Valid: true, + }, + + { + // Start With Number, End With Letter and Hyphen Separator + Input: "1-A", + Valid: true, + }, + } + + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorOriginName(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_profile_id_test.go b/internal/services/cdn/validate/front_door_profile_id_test.go index a4aa17075a03..97fc6fb42849 100644 --- a/internal/services/cdn/validate/front_door_profile_id_test.go +++ b/internal/services/cdn/validate/front_door_profile_id_test.go @@ -42,25 +42,25 @@ func TestFrontDoorProfileID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Valid: false, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1", Valid: true, }, { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1", Valid: false, }, } diff --git a/internal/services/cdn/validate/front_door_rule_id_test.go b/internal/services/cdn/validate/front_door_rule_id_test.go index 436e9526689d..2bf029c0cc6f 100644 --- a/internal/services/cdn/validate/front_door_rule_id_test.go +++ b/internal/services/cdn/validate/front_door_rule_id_test.go @@ -42,49 +42,49 @@ func TestFrontDoorRuleID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Valid: false, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Valid: false, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Valid: false, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Valid: false, }, { // missing RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/", Valid: false, }, { // missing value for RuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1/rules/rule1", Valid: true, }, { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1/RULES/RULE1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1/RULES/RULE1", Valid: false, }, } diff --git a/internal/services/cdn/validate/front_door_rule_set_id_test.go b/internal/services/cdn/validate/front_door_rule_set_id_test.go index d08969e748a5..8b282a6ef80c 100644 --- a/internal/services/cdn/validate/front_door_rule_set_id_test.go +++ b/internal/services/cdn/validate/front_door_rule_set_id_test.go @@ -42,37 +42,37 @@ func TestFrontDoorRuleSetID(t *testing.T) { { // missing ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", Valid: false, }, { // missing value for ProfileName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", Valid: false, }, { // missing RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", Valid: false, }, { // missing value for RuleSetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/", Valid: false, }, { // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/ruleSets/ruleSet1", Valid: true, }, { // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1", + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/RULESETS/RULESET1", Valid: false, }, } diff --git a/internal/services/cdn/validate/front_door_security_policy_domain_id.go b/internal/services/cdn/validate/front_door_security_policy_domain_id.go new file mode 100644 index 000000000000..a3e140750102 --- /dev/null +++ b/internal/services/cdn/validate/front_door_security_policy_domain_id.go @@ -0,0 +1,24 @@ +package validate + +import ( + "fmt" +) + +func FrontDoorSecurityPolicyDomainID(i interface{}, k string) (_ []string, errors []error) { + _, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("%q is invalid: expected type of %q to be string", "domain", k)} + } + + var err []error + + if _, err = FrontDoorCustomDomainID(i, k); err == nil { + return nil, nil + } + + if _, err = FrontDoorEndpointID(i, k); err == nil { + return nil, nil + } + + return nil, []error{fmt.Errorf("%q is invalid: the %q needs to be a valid Frontdoor Custom Domain ID or a Frontdoor Endpoint ID: %+v", "domain", k, err)} +} diff --git a/internal/services/cdn/validate/front_door_security_policy_id.go b/internal/services/cdn/validate/front_door_security_policy_id.go new file mode 100644 index 000000000000..1dab539a2080 --- /dev/null +++ b/internal/services/cdn/validate/front_door_security_policy_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse" +) + +func FrontDoorSecurityPolicyID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.FrontDoorSecurityPolicyID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/cdn/validate/front_door_security_policy_id_test.go b/internal/services/cdn/validate/front_door_security_policy_id_test.go new file mode 100644 index 000000000000..1e4f2c2f4971 --- /dev/null +++ b/internal/services/cdn/validate/front_door_security_policy_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestFrontDoorSecurityPolicyID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/", + Valid: false, + }, + + { + // missing value for ProfileName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/", + Valid: false, + }, + + { + // missing SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/", + Valid: false, + }, + + { + // missing value for SecurityPolicyName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Cdn/profiles/profile1/securityPolicies/securityPolicy1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CDN/PROFILES/PROFILE1/SECURITYPOLICIES/SECURITYPOLICY1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := FrontDoorSecurityPolicyID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cdn/validate/front_door_validation_helpers.go b/internal/services/cdn/validate/front_door_validation_helpers.go new file mode 100644 index 000000000000..f7bf10195464 --- /dev/null +++ b/internal/services/cdn/validate/front_door_validation_helpers.go @@ -0,0 +1,163 @@ +package validate + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2021-06-01/cdn" + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" +) + +func CdnFrontDoorRouteName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[\da-zA-Z][-\da-zA-Z]{0,88}[\da-zA-Z]$`); !m { + return nil, append(regexErrs, fmt.Errorf(`%q must be between 2 and 90 characters begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens, got %q`, k, i)) + } + + return nil, nil +} + +func CdnFrontDoorCacheDuration(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if strings.HasPrefix(v, "0.") { + return nil, []error{fmt.Errorf(`%q must not begin with %q if the duration is less than 1 day. If the %q is less than 1 day it should be in the HH:MM:SS format, got %q`, k, "0.", k, v)} + } + + if m, _ := validate.RegExHelper(i, k, `^([1-9]|([1-9][0-9])|([1-3][0-6][0-5])).((?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d))$|^((?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d))$`); !m { + return nil, []error{fmt.Errorf(`%q must be between in the d.HH:MM:SS or HH:MM:SS format and must be equal to or lower than %q, got %q`, k, "365.23:59:59", v)} + } + + if v == "00:00:00" { + return nil, []error{fmt.Errorf(`%q must be longer than zero seconds, got %q`, k, v)} + } + + return nil, nil +} + +func CdnFrontDoorUrlPathConditionMatchValue(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if strings.HasPrefix(v, "/") { + return nil, []error{fmt.Errorf(`%q must not begin with the URLs leading slash(e.g. /), got %q`, k, v)} + } + + return nil, nil +} + +func CdnFrontDoorCustomDomainName(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if m, _ := validate.RegExHelper(i, k, `^[a-zA-Z0-9][a-zA-Z0-9-]{0,258}[a-zA-Z0-9]$`); !m { + return nil, []error{fmt.Errorf(`%q must be between 2 and 260 characters in length, must begin with a letter or number, end with a letter or number and contain only letters, numbers and hyphens, got %q`, k, v)} + } + + return nil, nil +} + +func CdnFrontDoorSecretName(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if m, _ := validate.RegExHelper(i, k, `^[a-zA-Z0-9][a-zA-Z0-9-]{0,258}[a-zA-Z0-9]$`); !m { + return nil, []error{fmt.Errorf(`%q must be between 2 and 260 characters in length, must begin with a letter or number, end with a letter or number and contain only letters, numbers and hyphens, got %q`, k, v)} + } + + return nil, nil +} + +func CdnFrontDoorActionsBlock(actions []cdn.BasicDeliveryRuleAction) error { + routeConfigurationOverride := false + responseHeader := false + requestHeader := false + urlRewrite := false + urlRedirect := false + + for _, rule := range actions { + if !routeConfigurationOverride { + _, routeConfigurationOverride = rule.AsDeliveryRuleRouteConfigurationOverrideAction() + } + + if !responseHeader { + _, responseHeader = rule.AsDeliveryRuleResponseHeaderAction() + } + + if !requestHeader { + _, requestHeader = rule.AsDeliveryRuleRequestHeaderAction() + } + + if !urlRewrite { + _, urlRewrite = rule.AsURLRewriteAction() + } + + if !urlRedirect { + _, urlRedirect = rule.AsURLRedirectAction() + } + } + + if urlRedirect && urlRewrite { + return fmt.Errorf("the %q and the %q are both present in the %q match block", "url_redirect_action", "url_rewrite_action", "actions") + } + + return nil +} + +func CdnFrontDoorRuleName(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if m, _ := validate.RegExHelper(i, k, `^[a-zA-Z][\da-zA-Z]{0,259}$`); !m { + return nil, []error{fmt.Errorf(`%q must be between 1 and 260 characters in length, begin with a letter and may contain only letters and numbers, got %q`, k, v)} + } + + return nil, nil +} + +func CdnFrontDoorUrlRedirectActionQueryString(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("%q is invalid: expected type of %q to be string", "url_redirect_action", k)} + } + + // Query string must be in = format. ? and & will be added automatically so do not include them. + if v != "" { + if strings.ContainsAny(v, "?&") { + return nil, []error{fmt.Errorf("%q is invalid: %q must not include the %q or the %q characters in the %q field. They will be automatically added by Frontdoor, got %q", "url_redirect_action", k, "?", "&", "query_string", v)} + } + + if m, _ := validate.RegExHelper(i, k, `^(\b[\da-zA-Z\-\._~]*)(={1})((\b[\da-zA-Z\-\._~]*)|(\{{1}\b(socket_ip|client_ip|client_port|hostname|geo_country|http_method|http_version|query_string|request_scheme|request_uri|ssl_protocol|server_port|url_path){1}\}){1})$`); !m { + return nil, []error{fmt.Errorf("%q is invalid: %q must be in the = or ={action_server_variable} format, got %q", "url_redirect_action", k, v)} + } + } else { + return nil, []error{fmt.Errorf("%q is invalid: %q must not be empty, got %q", "url_redirect_action", k, v)} + } + + return nil, nil +} + +func CdnFrontDoorUrlRedirectActionDestinationPath(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("%q is invalid: expected type of %q to be string", "url_redirect_action", k)} + } + + if v != "" { + if !strings.HasPrefix(v, "/") { + return nil, []error{fmt.Errorf("%q is invalid: %q must begin with a %q, got %q. If you are trying to preserve the incoming path leave the %q value empty", "url_redirect_action", k, "/", v, "destination_path")} + } + } + + return nil, nil +} diff --git a/internal/services/cognitive/cognitive_account_resource.go b/internal/services/cognitive/cognitive_account_resource.go index 5a12af8a1029..ad3ae85214c9 100644 --- a/internal/services/cognitive/cognitive_account_resource.go +++ b/internal/services/cognitive/cognitive_account_resource.go @@ -89,6 +89,7 @@ func resourceCognitiveAccount() *pluginsdk.Resource { "LUIS", "LUIS.Authoring", "MetricsAdvisor", + "OpenAI", "Personalizer", "QnAMaker", "Recommendations", @@ -236,6 +237,12 @@ func resourceCognitiveAccount() *pluginsdk.Resource { ValidateFunc: search.ValidateSearchServiceID, }, + "custom_question_answering_search_service_key": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "storage": { Type: pluginsdk.TypeList, Optional: true, @@ -636,10 +643,10 @@ func expandCognitiveAccountNetworkAcls(d *pluginsdk.ResourceData) (*cognitiveser defaultAction := cognitiveservicesaccounts.NetworkRuleAction(v["default_action"].(string)) ipRulesRaw := v["ip_rules"].(*pluginsdk.Set) - ipRules := make([]cognitiveservicesaccounts.IpRule, 0) + ipRules := make([]cognitiveservicesaccounts.IPRule, 0) for _, v := range ipRulesRaw.List() { - rule := cognitiveservicesaccounts.IpRule{ + rule := cognitiveservicesaccounts.IPRule{ Value: v.(string), } ipRules = append(ipRules, rule) @@ -660,7 +667,7 @@ func expandCognitiveAccountNetworkAcls(d *pluginsdk.ResourceData) (*cognitiveser ruleSet := cognitiveservicesaccounts.NetworkRuleSet{ DefaultAction: &defaultAction, - IpRules: &ipRules, + IPRules: &ipRules, VirtualNetworkRules: &networkRules, } return &ruleSet, subnetIds @@ -698,6 +705,13 @@ func expandCognitiveAccountAPIProperties(d *pluginsdk.ResourceData) (*cognitives return nil, fmt.Errorf("the Search Service ID `custom_question_answering_search_service_id` can only be set when kind is set to `TextAnalytics`") } } + if v, ok := d.GetOk("custom_question_answering_search_service_key"); ok { + if kind == "TextAnalytics" { + props.QnaAzureSearchEndpointKey = utils.String(v.(string)) + } else { + return nil, fmt.Errorf("the Search Service Key `custom_question_answering_search_service_key` can only be set when kind is set to `TextAnalytics`") + } + } if v, ok := d.GetOk("metrics_advisor_aad_client_id"); ok { if kind == "MetricsAdvisor" { props.AadClientId = utils.String(v.(string)) @@ -735,8 +749,8 @@ func flattenCognitiveAccountNetworkAcls(input *cognitiveservicesaccounts.Network } ipRules := make([]interface{}, 0) - if input.IpRules != nil { - for _, v := range *input.IpRules { + if input.IPRules != nil { + for _, v := range *input.IPRules { ipRules = append(ipRules, v.Value) } } diff --git a/internal/services/cognitive/cognitive_account_resource_test.go b/internal/services/cognitive/cognitive_account_resource_test.go index c91b3424c38e..9554d235cf48 100644 --- a/internal/services/cognitive/cognitive_account_resource_test.go +++ b/internal/services/cognitive/cognitive_account_resource_test.go @@ -193,21 +193,21 @@ func TestAccCognitiveAccount_customQuestionAnsweringSearchServiceId(t *testing.T check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("custom_question_answering_search_service_key"), { Config: r.customQuestionAnsweringSearchServiceIdUpdated(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("custom_question_answering_search_service_key"), { Config: r.customQuestionAnsweringSearchServiceIdRemoved(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("custom_question_answering_search_service_key"), }) } @@ -639,14 +639,15 @@ resource "azurerm_search_service" "test" { } resource "azurerm_cognitive_account" "test" { - name = "acctestcogacc-%[1]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - kind = "TextAnalytics" - sku_name = "F0" - custom_question_answering_search_service_id = azurerm_search_service.test.id + name = "acctestcogacc-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + kind = "TextAnalytics" + sku_name = "F0" + custom_question_answering_search_service_id = azurerm_search_service.test.id + custom_question_answering_search_service_key = azurerm_search_service.test.primary_key } -`, data.RandomInteger, data.Locations.Primary) +`, data.RandomInteger, "West US") } func (CognitiveAccountResource) customQuestionAnsweringSearchServiceIdUpdated(data acceptance.TestData) string { @@ -675,14 +676,15 @@ resource "azurerm_search_service" "test2" { } resource "azurerm_cognitive_account" "test" { - name = "acctestcogacc-%[1]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - kind = "TextAnalytics" - sku_name = "F0" - custom_question_answering_search_service_id = azurerm_search_service.test2.id + name = "acctestcogacc-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + kind = "TextAnalytics" + sku_name = "F0" + custom_question_answering_search_service_id = azurerm_search_service.test2.id + custom_question_answering_search_service_key = azurerm_search_service.test2.primary_key } -`, data.RandomInteger, data.Locations.Primary) +`, data.RandomInteger, "West US") } func (CognitiveAccountResource) customQuestionAnsweringSearchServiceIdRemoved(data acceptance.TestData) string { @@ -717,7 +719,7 @@ resource "azurerm_cognitive_account" "test" { kind = "TextAnalytics" sku_name = "F0" } -`, data.RandomInteger, data.Locations.Primary) +`, data.RandomInteger, "West US") } func (CognitiveAccountResource) cognitiveServices(data acceptance.TestData) string { diff --git a/internal/services/compute/client/client.go b/internal/services/compute/client/client.go index ce3fe7323043..d3cf4358d2d0 100644 --- a/internal/services/compute/client/client.go +++ b/internal/services/compute/client/client.go @@ -4,6 +4,9 @@ import ( "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" "github.com/Azure/azure-sdk-for-go/services/marketplaceordering/mgmt/2015-06-01/marketplaceordering" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/availabilitysets" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/sshpublickeys" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) @@ -12,8 +15,8 @@ type Client struct { AvailabilitySetsClient *availabilitysets.AvailabilitySetsClient CapacityReservationsClient *compute.CapacityReservationsClient CapacityReservationGroupsClient *compute.CapacityReservationGroupsClient - DedicatedHostsClient *compute.DedicatedHostsClient - DedicatedHostGroupsClient *compute.DedicatedHostGroupsClient + DedicatedHostsClient *dedicatedhosts.DedicatedHostsClient + DedicatedHostGroupsClient *dedicatedhostgroups.DedicatedHostGroupsClient DisksClient *compute.DisksClient DiskAccessClient *compute.DiskAccessesClient DiskEncryptionSetsClient *compute.DiskEncryptionSetsClient @@ -24,7 +27,7 @@ type Client struct { GalleryImageVersionsClient *compute.GalleryImageVersionsClient ImagesClient *compute.ImagesClient MarketplaceAgreementsClient *marketplaceordering.MarketplaceAgreementsClient - ProximityPlacementGroupsClient *compute.ProximityPlacementGroupsClient + ProximityPlacementGroupsClient *proximityplacementgroups.ProximityPlacementGroupsClient SSHPublicKeysClient *sshpublickeys.SshPublicKeysClient SnapshotsClient *compute.SnapshotsClient UsageClient *compute.UsageClient @@ -48,10 +51,10 @@ func NewClient(o *common.ClientOptions) *Client { capacityReservationGroupsClient := compute.NewCapacityReservationGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&capacityReservationGroupsClient.Client, o.ResourceManagerAuthorizer) - dedicatedHostsClient := compute.NewDedicatedHostsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + dedicatedHostsClient := dedicatedhosts.NewDedicatedHostsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&dedicatedHostsClient.Client, o.ResourceManagerAuthorizer) - dedicatedHostGroupsClient := compute.NewDedicatedHostGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + dedicatedHostGroupsClient := dedicatedhostgroups.NewDedicatedHostGroupsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&dedicatedHostGroupsClient.Client, o.ResourceManagerAuthorizer) disksClient := compute.NewDisksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) @@ -84,7 +87,7 @@ func NewClient(o *common.ClientOptions) *Client { marketplaceAgreementsClient := marketplaceordering.NewMarketplaceAgreementsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&marketplaceAgreementsClient.Client, o.ResourceManagerAuthorizer) - proximityPlacementGroupsClient := compute.NewProximityPlacementGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + proximityPlacementGroupsClient := proximityplacementgroups.NewProximityPlacementGroupsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&proximityPlacementGroupsClient.Client, o.ResourceManagerAuthorizer) snapshotsClient := compute.NewSnapshotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) diff --git a/internal/services/compute/dedicated_host_data_source.go b/internal/services/compute/dedicated_host_data_source.go index 6b2f2ff5c865..04b8ec950f04 100644 --- a/internal/services/compute/dedicated_host_data_source.go +++ b/internal/services/compute/dedicated_host_data_source.go @@ -4,15 +4,15 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceDedicatedHost() *pluginsdk.Resource { @@ -40,7 +40,7 @@ func dataSourceDedicatedHost() *pluginsdk.Resource { "location": commonschema.LocationComputed(), - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } @@ -51,22 +51,29 @@ func dataSourceDedicatedHostRead(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewDedicatedHostID(subscriptionId, d.Get("resource_group_name").(string), d.Get("dedicated_host_group_name").(string), d.Get("name").(string)) + id := dedicatedhosts.NewHostID(subscriptionId, d.Get("resource_group_name").(string), d.Get("dedicated_host_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, "") + resp, err := client.Get(ctx, id, dedicatedhosts.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } + return fmt.Errorf("reading %s: %+v", id, err) } d.SetId(id.ID()) d.Set("name", id.HostName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("dedicated_host_group_name", id.HostGroupName) - d.Set("location", location.NormalizeNilable(resp.Location)) + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) + + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } + } - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/compute/dedicated_host_group_data_source.go b/internal/services/compute/dedicated_host_group_data_source.go index 322429c23bf0..0446ea8c1a9f 100644 --- a/internal/services/compute/dedicated_host_group_data_source.go +++ b/internal/services/compute/dedicated_host_group_data_source.go @@ -5,16 +5,16 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" "github.com/hashicorp/go-azure-helpers/resourcemanager/zones" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceDedicatedHostGroup() *pluginsdk.Resource { @@ -59,10 +59,10 @@ func dataSourceDedicatedHostGroupRead(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewDedicatedHostGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.HostGroupName, "") + id := dedicatedhostgroups.NewHostGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id, dedicatedhostgroups.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } return fmt.Errorf("reading %s: %+v", id, err) @@ -71,20 +71,21 @@ func dataSourceDedicatedHostGroupRead(d *pluginsdk.ResourceData, meta interface{ d.SetId(id.ID()) d.Set("name", id.HostGroupName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) - d.Set("location", location.NormalizeNilable(resp.Location)) - d.Set("zones", zones.Flatten(resp.Zones)) + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) + d.Set("zones", zones.Flatten(model.Zones)) - if props := resp.DedicatedHostGroupProperties; props != nil { - d.Set("automatic_placement_enabled", props.SupportAutomaticPlacement) + if props := model.Properties; props != nil { + d.Set("automatic_placement_enabled", props.SupportAutomaticPlacement) + d.Set("platform_fault_domain_count", props.PlatformFaultDomainCount) + } - platformFaultDomainCount := 0 - if props.PlatformFaultDomainCount != nil { - platformFaultDomainCount = int(*props.PlatformFaultDomainCount) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err } - d.Set("platform_fault_domain_count", platformFaultDomainCount) } - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/compute/dedicated_host_group_resource.go b/internal/services/compute/dedicated_host_group_resource.go index 06de97bed5f0..f8eba877674f 100644 --- a/internal/services/compute/dedicated_host_group_resource.go +++ b/internal/services/compute/dedicated_host_group_resource.go @@ -5,15 +5,14 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -28,7 +27,7 @@ func resourceDedicatedHostGroup() *pluginsdk.Resource { Delete: resourceDedicatedHostGroupDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.HostGroupID(id) + _, err := dedicatedhostgroups.ParseHostGroupID(id) return err }), @@ -47,11 +46,9 @@ func resourceDedicatedHostGroup() *pluginsdk.Resource { ValidateFunc: validate.DedicatedHostGroupName(), }, - "location": azure.SchemaLocation(), + "resource_group_name": commonschema.ResourceGroupName(), - // There's a bug in the Azure API where this is returned in upper-case - // BUG: https://github.com/Azure/azure-rest-api-specs/issues/8068 - "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + "location": commonschema.Location(), "platform_fault_domain_count": { Type: pluginsdk.TypeInt, @@ -68,7 +65,7 @@ func resourceDedicatedHostGroup() *pluginsdk.Resource { }, "zone": commonschema.ZoneSingleOptionalForceNew(), - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } @@ -79,48 +76,45 @@ func resourceDedicatedHostGroupCreate(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewHostGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - + id := dedicatedhostgroups.NewHostGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + existing, err := client.Get(ctx, id, dedicatedhostgroups.DefaultGetOperationOptions()) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_dedicated_host_group", id.ID()) } } - location := azure.NormalizeLocation(d.Get("location").(string)) platformFaultDomainCount := d.Get("platform_fault_domain_count").(int) t := d.Get("tags").(map[string]interface{}) - parameters := compute.DedicatedHostGroup{ - Location: utils.String(location), - DedicatedHostGroupProperties: &compute.DedicatedHostGroupProperties{ - PlatformFaultDomainCount: utils.Int32(int32(platformFaultDomainCount)), + payload := dedicatedhostgroups.DedicatedHostGroup{ + Location: location.Normalize(d.Get("location").(string)), + Properties: &dedicatedhostgroups.DedicatedHostGroupProperties{ + PlatformFaultDomainCount: int64(platformFaultDomainCount), }, Tags: tags.Expand(t), } if zone, ok := d.GetOk("zone"); ok { - parameters.Zones = &[]string{ + payload.Zones = &[]string{ zone.(string), } } if v, ok := d.GetOk("automatic_placement_enabled"); ok { - parameters.DedicatedHostGroupProperties.SupportAutomaticPlacement = utils.Bool(v.(bool)) + payload.Properties.SupportAutomaticPlacement = utils.Bool(v.(bool)) } - if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, payload); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } d.SetId(id.ID()) - return resourceDedicatedHostGroupRead(d, meta) } @@ -129,44 +123,45 @@ func resourceDedicatedHostGroupRead(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.HostGroupID(d.Id()) + id, err := dedicatedhostgroups.ParseHostGroupID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + resp, err := client.Get(ctx, *id, dedicatedhostgroups.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Dedicated Host Group %q does not exist - removing from state", d.Id()) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[INFO] %q was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("reading Dedicated Host Group %q (: %+v", id.String(), err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("name", id.HostGroupName) + d.Set("resource_group_name", id.ResourceGroupName) - d.Set("location", location.NormalizeNilable(resp.Location)) + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) - zone := "" - if resp.Zones != nil && len(*resp.Zones) > 0 { - z := *resp.Zones - zone = z[0] - } - d.Set("zone", zone) + zone := "" + if model.Zones != nil && len(*model.Zones) > 0 { + z := *model.Zones + zone = z[0] + } + d.Set("zone", zone) - if props := resp.DedicatedHostGroupProperties; props != nil { - platformFaultDomainCount := 0 - if props.PlatformFaultDomainCount != nil { - platformFaultDomainCount = int(*props.PlatformFaultDomainCount) + if props := model.Properties; props != nil { + d.Set("platform_fault_domain_count", props.PlatformFaultDomainCount) + d.Set("automatic_placement_enabled", props.SupportAutomaticPlacement) } - d.Set("platform_fault_domain_count", platformFaultDomainCount) - d.Set("automatic_placement_enabled", props.SupportAutomaticPlacement) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourceDedicatedHostGroupUpdate(d *pluginsdk.ResourceData, meta interface{}) error { @@ -174,16 +169,17 @@ func resourceDedicatedHostGroupUpdate(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroupName := d.Get("resource_group_name").(string) - t := d.Get("tags").(map[string]interface{}) + id, err := dedicatedhostgroups.ParseHostGroupID(d.Id()) + if err != nil { + return err + } - parameters := compute.DedicatedHostGroupUpdate{ - Tags: tags.Expand(t), + payload := dedicatedhostgroups.DedicatedHostGroupUpdate{ + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - if _, err := client.Update(ctx, resourceGroupName, name, parameters); err != nil { - return fmt.Errorf("updating Dedicated Host Group %q (Resource Group %q): %+v", name, resourceGroupName, err) + if _, err := client.Update(ctx, *id, payload); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } return resourceDedicatedHostGroupRead(d, meta) @@ -194,13 +190,13 @@ func resourceDedicatedHostGroupDelete(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.HostGroupID(d.Id()) + id, err := dedicatedhostgroups.ParseHostGroupID(d.Id()) if err != nil { return err } - if _, err := client.Delete(ctx, id.ResourceGroup, id.Name); err != nil { - return fmt.Errorf("deleting Dedicated Host Group %q : %+v", id.String(), err) + if _, err := client.Delete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/compute/dedicated_host_group_resource_test.go b/internal/services/compute/dedicated_host_group_resource_test.go index 1abe0a0aac07..0fac0d29f4ed 100644 --- a/internal/services/compute/dedicated_host_group_resource_test.go +++ b/internal/services/compute/dedicated_host_group_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -78,17 +79,17 @@ func TestAccDedicatedHostGroup_complete(t *testing.T) { } func (r DedicatedHostGroupResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.HostGroupID(state.ID) + id, err := dedicatedhostgroups.ParseHostGroupID(state.ID) if err != nil { return nil, err } - resp, err := clients.Compute.DedicatedHostGroupsClient.Get(ctx, id.ResourceGroup, id.Name, "") + resp, err := clients.Compute.DedicatedHostGroupsClient.Get(ctx, *id, dedicatedhostgroups.DefaultGetOperationOptions()) if err != nil { return nil, fmt.Errorf("retrieving Compute Dedicated Host Group %q", id) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DedicatedHostGroupResource) basic(data acceptance.TestData) string { diff --git a/internal/services/compute/dedicated_host_resource.go b/internal/services/compute/dedicated_host_resource.go index 41dc03fec72a..2dc6307bf4d9 100644 --- a/internal/services/compute/dedicated_host_resource.go +++ b/internal/services/compute/dedicated_host_resource.go @@ -6,14 +6,15 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -28,7 +29,7 @@ func resourceDedicatedHost() *pluginsdk.Resource { Delete: resourceDedicatedHostDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.DedicatedHostID(id) + _, err := dedicatedhosts.ParseHostID(id) return err }), @@ -47,15 +48,15 @@ func resourceDedicatedHost() *pluginsdk.Resource { ValidateFunc: validate.DedicatedHostName(), }, - "location": azure.SchemaLocation(), - "dedicated_host_group_id": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.DedicatedHostGroupID, + ValidateFunc: dedicatedhostgroups.ValidateHostGroupID, }, + "location": commonschema.Location(), + "sku_name": { Type: pluginsdk.TypeString, ForceNew: true, @@ -124,14 +125,15 @@ func resourceDedicatedHost() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - string(compute.DedicatedHostLicenseTypesNone), - string(compute.DedicatedHostLicenseTypesWindowsServerHybrid), - string(compute.DedicatedHostLicenseTypesWindowsServerPerpetual), + // TODO: remove `None` in 4.0 in favour of this field being set to an empty string (since it's optional) + string(dedicatedhosts.DedicatedHostLicenseTypesNone), + string(dedicatedhosts.DedicatedHostLicenseTypesWindowsServerHybrid), + string(dedicatedhosts.DedicatedHostLicenseTypesWindowsServerPerpetual), }, false), - Default: string(compute.DedicatedHostLicenseTypesNone), + Default: string(dedicatedhosts.DedicatedHostLicenseTypesNone), }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } @@ -141,102 +143,90 @@ func resourceDedicatedHostCreate(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - hostGroupId, err := parse.DedicatedHostGroupID(d.Get("dedicated_host_group_id").(string)) + hostGroupId, err := dedicatedhostgroups.ParseHostGroupID(d.Get("dedicated_host_group_id").(string)) if err != nil { return err } - id := parse.NewDedicatedHostID(hostGroupId.SubscriptionId, hostGroupId.ResourceGroup, hostGroupId.HostGroupName, d.Get("name").(string)) + id := dedicatedhosts.NewHostID(hostGroupId.SubscriptionId, hostGroupId.ResourceGroupName, hostGroupId.HostGroupName, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, "") + existing, err := client.Get(ctx, id, dedicatedhosts.DefaultGetOperationOptions()) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_dedicated_host", id.ID()) } } - parameters := compute.DedicatedHost{ - Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), - DedicatedHostProperties: &compute.DedicatedHostProperties{ + licenseType := dedicatedhosts.DedicatedHostLicenseTypes(d.Get("license_type").(string)) + payload := dedicatedhosts.DedicatedHost{ + Location: location.Normalize(d.Get("location").(string)), + Properties: &dedicatedhosts.DedicatedHostProperties{ AutoReplaceOnFailure: utils.Bool(d.Get("auto_replace_on_failure").(bool)), - LicenseType: compute.DedicatedHostLicenseTypes(d.Get("license_type").(string)), - PlatformFaultDomain: utils.Int32(int32(d.Get("platform_fault_domain").(int))), + LicenseType: &licenseType, + PlatformFaultDomain: utils.Int64(int64(d.Get("platform_fault_domain").(int))), }, - Sku: &compute.Sku{ + Sku: dedicatedhosts.Sku{ Name: utils.String(d.Get("sku_name").(string)), }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, parameters) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of %s: %+v", id, err) - } d.SetId(id.ID()) return resourceDedicatedHostRead(d, meta) } func resourceDedicatedHostRead(d *pluginsdk.ResourceData, meta interface{}) error { - groupsClient := meta.(*clients.Client).Compute.DedicatedHostGroupsClient hostsClient := meta.(*clients.Client).Compute.DedicatedHostsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DedicatedHostID(d.Id()) + id, err := dedicatedhosts.ParseHostID(d.Id()) if err != nil { return err } - hostGroupId := parse.NewDedicatedHostGroupID(id.SubscriptionId, id.ResourceGroup, id.HostGroupName) - group, err := groupsClient.Get(ctx, hostGroupId.ResourceGroup, hostGroupId.HostGroupName, "") + resp, err := hostsClient.Get(ctx, *id, dedicatedhosts.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(group.Response) { - log.Printf("[INFO] Parent %s does not exist - removing from state", hostGroupId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") return nil } - return fmt.Errorf("retrieving %s: %+v", hostGroupId, err) - } - - resp, err := hostsClient.Get(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, "") - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - log.Printf("[INFO] Dedicated Host %q does not exist - removing from state", d.Id()) - d.SetId("") - return nil - } - - return fmt.Errorf("retrieving Dedicated Host %q (Host Group Name %q / Resource Group %q): %+v", id.HostName, id.HostGroupName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.HostName) - d.Set("dedicated_host_group_id", hostGroupId.ID()) + d.Set("dedicated_host_group_id", dedicatedhostgroups.NewHostGroupID(id.SubscriptionId, id.ResourceGroupName, id.HostGroupName).ID()) + + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) + d.Set("sku_name", model.Sku.Name) + if props := model.Properties; props != nil { + d.Set("auto_replace_on_failure", props.AutoReplaceOnFailure) + d.Set("license_type", props.LicenseType) + + platformFaultDomain := 0 + if props.PlatformFaultDomain != nil { + platformFaultDomain = int(*props.PlatformFaultDomain) + } + d.Set("platform_fault_domain", platformFaultDomain) + } - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } - d.Set("sku_name", resp.Sku.Name) - if props := resp.DedicatedHostProperties; props != nil { - d.Set("auto_replace_on_failure", props.AutoReplaceOnFailure) - d.Set("license_type", props.LicenseType) - - platformFaultDomain := 0 - if props.PlatformFaultDomain != nil { - platformFaultDomain = int(*props.PlatformFaultDomain) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err } - d.Set("platform_fault_domain", platformFaultDomain) } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourceDedicatedHostUpdate(d *pluginsdk.ResourceData, meta interface{}) error { @@ -244,25 +234,30 @@ func resourceDedicatedHostUpdate(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DedicatedHostID(d.Id()) + id, err := dedicatedhosts.ParseHostID(d.Id()) if err != nil { return err } - parameters := compute.DedicatedHostUpdate{ - DedicatedHostProperties: &compute.DedicatedHostProperties{ - AutoReplaceOnFailure: utils.Bool(d.Get("auto_replace_on_failure").(bool)), - LicenseType: compute.DedicatedHostLicenseTypes(d.Get("license_type").(string)), - }, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + payload := dedicatedhosts.DedicatedHostUpdate{} + + if d.HasChanges("auto_replace_on_failure", "license_type") { + payload.Properties = &dedicatedhosts.DedicatedHostProperties{} + if d.HasChange("auto_replace_on_failure") { + payload.Properties.AutoReplaceOnFailure = utils.Bool(d.Get("auto_replace_on_failure").(bool)) + } + if d.HasChange("license_type") { + licenseType := dedicatedhosts.DedicatedHostLicenseTypes(d.Get("license_type").(string)) + payload.Properties.LicenseType = &licenseType + } } - future, err := client.Update(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, parameters) - if err != nil { - return fmt.Errorf("updating %s: %+v", *id, err) + if d.HasChange("tags") { + payload.Tags = tags.Expand(d.Get("tags").(map[string]interface{})) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of %s: %+v", *id, err) + + if err := client.UpdateThenPoll(ctx, *id, payload); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } return resourceDedicatedHostRead(d, meta) @@ -273,22 +268,15 @@ func resourceDedicatedHostDelete(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DedicatedHostID(d.Id()) + id, err := dedicatedhosts.ParseHostID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.HostGroupName, id.HostName) - if err != nil { + if err := client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - if !response.WasNotFound(future.Response()) { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } - } - // API has bug, which appears to be eventually consistent. Tracked by this issue: https://github.com/Azure/azure-rest-api-specs/issues/8137 log.Printf("[DEBUG] Waiting for %s to be fully deleted..", *id) stateConf := &pluginsdk.StateChangeConf{ @@ -307,11 +295,11 @@ func resourceDedicatedHostDelete(d *pluginsdk.ResourceData, meta interface{}) er return nil } -func dedicatedHostDeletedRefreshFunc(ctx context.Context, client *compute.DedicatedHostsClient, id parse.DedicatedHostId) pluginsdk.StateRefreshFunc { +func dedicatedHostDeletedRefreshFunc(ctx context.Context, client *dedicatedhosts.DedicatedHostsClient, id dedicatedhosts.HostId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, "") + res, err := client.Get(ctx, id, dedicatedhosts.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(res.Response) { + if response.WasNotFound(res.HttpResponse) { return "NotFound", "NotFound", nil } diff --git a/internal/services/compute/dedicated_host_resource_test.go b/internal/services/compute/dedicated_host_resource_test.go index ddc6d6b2f091..09b33e7e0a84 100644 --- a/internal/services/compute/dedicated_host_resource_test.go +++ b/internal/services/compute/dedicated_host_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -166,17 +167,17 @@ func TestAccDedicatedHost_requiresImport(t *testing.T) { } func (t DedicatedHostResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.DedicatedHostID(state.ID) + id, err := dedicatedhosts.ParseHostID(state.ID) if err != nil { return nil, err } - resp, err := clients.Compute.DedicatedHostsClient.Get(ctx, id.ResourceGroup, id.HostGroupName, id.HostName, "") + resp, err := clients.Compute.DedicatedHostsClient.Get(ctx, *id, dedicatedhosts.DefaultGetOperationOptions()) if err != nil { return nil, fmt.Errorf("retrieving Compute Dedicated Host %q", id.String()) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (r DedicatedHostResource) basic(data acceptance.TestData) string { diff --git a/internal/services/compute/disk_encryption_set_resource.go b/internal/services/compute/disk_encryption_set_resource.go index 8c6adc8be552..588ce5929550 100644 --- a/internal/services/compute/disk_encryption_set_resource.go +++ b/internal/services/compute/disk_encryption_set_resource.go @@ -114,11 +114,14 @@ func resourceDiskEncryptionSetCreate(d *pluginsdk.ResourceData, meta interface{} if err != nil { return fmt.Errorf("validating Key Vault Key %q for Disk Encryption Set: %+v", keyVaultKeyId, err) } - if !keyVaultDetails.softDeleteEnabled { - return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Soft Delete must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) - } - if !keyVaultDetails.purgeProtectionEnabled { - return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Purge Protection must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + + if keyVaultDetails != nil { + if !keyVaultDetails.softDeleteEnabled { + return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Soft Delete must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + } + if !keyVaultDetails.purgeProtectionEnabled { + return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Purge Protection must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + } } location := azure.NormalizeLocation(d.Get("location").(string)) @@ -136,9 +139,6 @@ func resourceDiskEncryptionSetCreate(d *pluginsdk.ResourceData, meta interface{} EncryptionSetProperties: &compute.EncryptionSetProperties{ ActiveKey: &compute.KeyForDiskEncryptionSet{ KeyURL: utils.String(keyVaultKeyId), - SourceVault: &compute.SourceVault{ - ID: utils.String(keyVaultDetails.keyVaultId), - }, }, RotationToLatestKeyVersionEnabled: utils.Bool(rotationToLatestKeyVersionEnabled), EncryptionType: compute.DiskEncryptionSetType(encryptionType), @@ -147,6 +147,12 @@ func resourceDiskEncryptionSetCreate(d *pluginsdk.ResourceData, meta interface{} Tags: tags.Expand(t), } + if keyVaultDetails != nil { + params.EncryptionSetProperties.ActiveKey.SourceVault = &compute.SourceVault{ + ID: utils.String(keyVaultDetails.keyVaultId), + } + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, params) if err != nil { return fmt.Errorf("creating %s: %+v", id, err) @@ -231,20 +237,27 @@ func resourceDiskEncryptionSetUpdate(d *pluginsdk.ResourceData, meta interface{} if err != nil { return fmt.Errorf("validating Key Vault Key %q for Disk Encryption Set: %+v", keyVaultKeyId, err) } - if !keyVaultDetails.softDeleteEnabled { - return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Soft Delete must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) - } - if !keyVaultDetails.purgeProtectionEnabled { - return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Purge Protection must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + + if keyVaultDetails != nil { + if !keyVaultDetails.softDeleteEnabled { + return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Soft Delete must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + } + if !keyVaultDetails.purgeProtectionEnabled { + return fmt.Errorf("validating Key Vault %q (Resource Group %q) for Disk Encryption Set: Purge Protection must be enabled but it isn't!", keyVaultDetails.keyVaultName, keyVaultDetails.resourceGroupName) + } } + update.DiskEncryptionSetUpdateProperties = &compute.DiskEncryptionSetUpdateProperties{ ActiveKey: &compute.KeyForDiskEncryptionSet{ KeyURL: utils.String(keyVaultKeyId), - SourceVault: &compute.SourceVault{ - ID: utils.String(keyVaultDetails.keyVaultId), - }, }, } + + if keyVaultDetails != nil { + update.DiskEncryptionSetUpdateProperties.ActiveKey.SourceVault = &compute.SourceVault{ + ID: utils.String(keyVaultDetails.keyVaultId), + } + } } if d.HasChange("auto_key_rotation_enabled") { @@ -335,7 +348,7 @@ func diskEncryptionSetRetrieveKeyVault(ctx context.Context, keyVaultsClient *cli return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultKeyId.KeyVaultBaseUrl, err) } if keyVaultID == nil { - return nil, fmt.Errorf("Unable to determine the Resource ID for the Key Vault at URL %q", keyVaultKeyId.KeyVaultBaseUrl) + return nil, nil } parsedKeyVaultID, err := keyVaultParse.VaultID(*keyVaultID) diff --git a/internal/services/compute/images_data_source.go b/internal/services/compute/images_data_source.go index 89c211e6084e..9c3a022519fc 100644 --- a/internal/services/compute/images_data_source.go +++ b/internal/services/compute/images_data_source.go @@ -3,6 +3,7 @@ package compute import ( "context" "fmt" + "sort" "time" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" @@ -139,7 +140,23 @@ func dataSourceImagesRead(d *pluginsdk.ResourceData, meta interface{}) error { return fmt.Errorf("no images were found that match the specified tags") } - d.SetId(time.Now().UTC().String()) + tagsId := "" + tagKeys := make([]string, 0, len(filterTags)) + for key := range filterTags { + tagKeys = append(tagKeys, key) + } + sort.Strings(tagKeys) + for _, key := range tagKeys { + value := "" + if v, ok := filterTags[key]; ok && v != nil { + value = *v + } + tagsId += fmt.Sprintf("[%s:%s]", key, value) + } + if tagsId == "" { + tagsId = "[]" + } + d.SetId(fmt.Sprintf("resourceGroups/%s/tags/%s/images", resourceGroup, tagsId)) d.Set("resource_group_name", resourceGroup) diff --git a/internal/services/compute/linux_virtual_machine_resource.go b/internal/services/compute/linux_virtual_machine_resource.go index 6a48829f4380..6fb2e691f435 100644 --- a/internal/services/compute/linux_virtual_machine_resource.go +++ b/internal/services/compute/linux_virtual_machine_resource.go @@ -12,6 +12,9 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/availabilitysets" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" @@ -150,7 +153,7 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { "dedicated_host_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.DedicatedHostID, + ValidateFunc: dedicatedhosts.ValidateHostID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE :shrug: // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, @@ -162,7 +165,7 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { "dedicated_host_group_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.DedicatedHostGroupID, + ValidateFunc: dedicatedhostgroups.ValidateHostGroupID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, @@ -191,8 +194,8 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - // NOTE: whilst Delete is an option here, it's only applicable for VMSS string(compute.VirtualMachineEvictionPolicyTypesDeallocate), + string(compute.VirtualMachineEvictionPolicyTypesDelete), }, false), }, @@ -254,7 +257,7 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { "proximity_placement_group_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.ProximityPlacementGroupID, + ValidateFunc: proximityplacementgroups.ValidateProximityPlacementGroupID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE :shrug: // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, diff --git a/internal/services/compute/linux_virtual_machine_resource_disk_os_test.go b/internal/services/compute/linux_virtual_machine_resource_disk_os_test.go index 185a41ceffef..ccfbc0940ef5 100644 --- a/internal/services/compute/linux_virtual_machine_resource_disk_os_test.go +++ b/internal/services/compute/linux_virtual_machine_resource_disk_os_test.go @@ -156,6 +156,21 @@ func TestAccLinuxVirtualMachine_diskOSEphemeralDefault(t *testing.T) { }) } +func TestAccLinuxVirtualMachine_diskOSDiskEphemeralSpot(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + r := LinuxVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskOSEphemeralSpot(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + }) +} + func TestAccLinuxVirtualMachine_diskOSEphemeralResourceDisk(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") r := LinuxVirtualMachineResource{} @@ -738,6 +753,46 @@ resource "azurerm_linux_virtual_machine" "test" { `, r.template(data), data.RandomInteger) } +func (r LinuxVirtualMachineResource) diskOSEphemeralSpot(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2s_v2" + admin_username = "adminuser" + priority = "Spot" + eviction_policy = "Delete" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadOnly" + storage_account_type = "Standard_LRS" + + diff_disk_settings { + option = "Local" + } + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, r.template(data), data.RandomInteger) +} + func (r LinuxVirtualMachineResource) diskOSEphemeralResourceDisk(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/compute/linux_virtual_machine_scale_set_identity_resource_test.go b/internal/services/compute/linux_virtual_machine_scale_set_identity_resource_test.go index 574773a9148f..e7fa989da250 100644 --- a/internal/services/compute/linux_virtual_machine_scale_set_identity_resource_test.go +++ b/internal/services/compute/linux_virtual_machine_scale_set_identity_resource_test.go @@ -141,6 +141,7 @@ resource "azurerm_linux_virtual_machine_scale_set" "test" { instances = 1 admin_username = "adminuser" admin_password = "P@ssword1234!" + overprovision = false disable_password_authentication = false diff --git a/internal/services/compute/linux_virtual_machine_scale_set_resource.go b/internal/services/compute/linux_virtual_machine_scale_set_resource.go index a276c8473178..34cd02e9d63c 100644 --- a/internal/services/compute/linux_virtual_machine_scale_set_resource.go +++ b/internal/services/compute/linux_virtual_machine_scale_set_resource.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/zones" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -1058,7 +1059,8 @@ func resourceLinuxVirtualMachineScaleSetSchema() map[string]*pluginsdk.Schema { "instances": { Type: pluginsdk.TypeInt, - Required: true, + Optional: true, + Default: 0, ValidateFunc: validation.IntAtLeast(0), }, @@ -1203,7 +1205,7 @@ func resourceLinuxVirtualMachineScaleSetSchema() map[string]*pluginsdk.Schema { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validate.ProximityPlacementGroupID, + ValidateFunc: proximityplacementgroups.ValidateProximityPlacementGroupID, // the Compute API is broken and returns the Resource Group name in UPPERCASE :shrug:, github issue: https://github.com/Azure/azure-rest-api-specs/issues/10016 DiffSuppressFunc: suppress.CaseDifference, ConflictsWith: []string{ diff --git a/internal/services/compute/linux_virtual_machine_scale_set_scaling_resource_test.go b/internal/services/compute/linux_virtual_machine_scale_set_scaling_resource_test.go index b39157675633..02cd1c5a1abc 100644 --- a/internal/services/compute/linux_virtual_machine_scale_set_scaling_resource_test.go +++ b/internal/services/compute/linux_virtual_machine_scale_set_scaling_resource_test.go @@ -38,6 +38,21 @@ func TestAccLinuxVirtualMachineScaleSet_scalingCapacityReservationGroupId(t *tes }) } +func TestAccLinuxVirtualMachineScaleSet_defaultInstanceCount(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test") + r := LinuxVirtualMachineScaleSetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.defaultInstanceCount(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + }) +} + func TestAccLinuxVirtualMachineScaleSet_scalingInstanceCount(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test") r := LinuxVirtualMachineScaleSetResource{} @@ -436,6 +451,46 @@ resource "azurerm_linux_virtual_machine_scale_set" "test" { `, r.template(data), data.RandomInteger, instanceCount) } +func (r LinuxVirtualMachineScaleSetResource) defaultInstanceCount(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine_scale_set" "test" { + name = "acctestvmss-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssword1234!" + + disable_password_authentication = false + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r LinuxVirtualMachineScaleSetResource) scalingInstanceCountIgnoreUpdatedSku(data acceptance.TestData, instanceCount int) string { return fmt.Sprintf(` %s diff --git a/internal/services/compute/managed_disk_data_source.go b/internal/services/compute/managed_disk_data_source.go index b52c98ad9a75..8d0a1dc6c66b 100644 --- a/internal/services/compute/managed_disk_data_source.go +++ b/internal/services/compute/managed_disk_data_source.go @@ -60,6 +60,54 @@ func dataSourceManagedDisk() *pluginsdk.Resource { Computed: true, }, + "encryption_settings": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "disk_encryption_key": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_url": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "source_vault_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + "key_encryption_key": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_url": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "source_vault_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "image_reference_id": { Type: pluginsdk.TypeString, Computed: true, @@ -162,6 +210,10 @@ func dataSourceManagedDiskRead(d *pluginsdk.ResourceData, meta interface{}) erro diskEncryptionSetId = *props.Encryption.DiskEncryptionSetID } d.Set("disk_encryption_set_id", diskEncryptionSetId) + + if err := d.Set("encryption_settings", flattenManagedDiskEncryptionSettings(props.EncryptionSettingsCollection)); err != nil { + return fmt.Errorf("setting `encryption_settings`: %+v", err) + } } return tags.FlattenAndSet(d, resp.Tags) diff --git a/internal/services/compute/managed_disk_data_source_test.go b/internal/services/compute/managed_disk_data_source_test.go index 6d027527c39f..6509cae50440 100644 --- a/internal/services/compute/managed_disk_data_source_test.go +++ b/internal/services/compute/managed_disk_data_source_test.go @@ -67,6 +67,27 @@ func TestAccDataSourceManagedDisk_diskAccess(t *testing.T) { }) } +func TestAccDataSourceManagedDisk_encryptionSettings(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_managed_disk", "test") + r := ManagedDiskDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.encryptionSettings(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("encryption_settings.#").HasValue("1"), + check.That(data.ResourceName).Key("encryption_settings.0.enabled").HasValue("true"), + check.That(data.ResourceName).Key("encryption_settings.0.disk_encryption_key.#").HasValue("1"), + check.That(data.ResourceName).Key("encryption_settings.0.disk_encryption_key.0.secret_url").Exists(), + check.That(data.ResourceName).Key("encryption_settings.0.disk_encryption_key.0.source_vault_id").Exists(), + check.That(data.ResourceName).Key("encryption_settings.0.key_encryption_key.#").HasValue("1"), + check.That(data.ResourceName).Key("encryption_settings.0.key_encryption_key.0.key_url").Exists(), + check.That(data.ResourceName).Key("encryption_settings.0.key_encryption_key.0.source_vault_id").Exists(), + ), + }, + }) +} + func (ManagedDiskDataSource) basic(data acceptance.TestData, name string, resourceGroupName string) string { return fmt.Sprintf(` provider "azurerm" { @@ -168,3 +189,103 @@ data "azurerm_managed_disk" "test" { } `, data.Locations.Primary, data.RandomInteger) } + +func (ManagedDiskDataSource) encryptionSettings(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + recover_soft_deleted_key_vaults = false + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + purge_soft_deleted_secrets_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + 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 = "standard" + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.object_id}" + + key_permissions = [ + "Create", + "Delete", + "Get", + "Purge", + ] + + secret_permissions = [ + "Delete", + "Get", + "Set", + ] + } + + enabled_for_disk_encryption = true + + tags = { + environment = "Production" + } +} + +resource "azurerm_key_vault_secret" "test" { + name = "secret-%s" + value = "szechuan" + key_vault_id = azurerm_key_vault.test.id +} + +resource "azurerm_key_vault_key" "test" { + name = "key-%s" + key_vault_id = azurerm_key_vault.test.id + key_type = "EC" + key_size = 2048 + + key_opts = [ + "sign", + "verify", + ] +} + +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 = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings { + enabled = true + + disk_encryption_key { + secret_url = "${azurerm_key_vault_secret.test.id}" + source_vault_id = "${azurerm_key_vault.test.id}" + } + + key_encryption_key { + key_url = "${azurerm_key_vault_key.test.id}" + source_vault_id = "${azurerm_key_vault.test.id}" + } + } +} + +data "azurerm_managed_disk" "test" { + name = azurerm_managed_disk.test.name + resource_group_name = azurerm_resource_group.test.name +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomString, data.RandomInteger) +} diff --git a/internal/services/compute/parse/dedicated_host.go b/internal/services/compute/parse/dedicated_host.go deleted file mode 100644 index f9c7816b810b..000000000000 --- a/internal/services/compute/parse/dedicated_host.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type DedicatedHostId struct { - SubscriptionId string - ResourceGroup string - HostGroupName string - HostName string -} - -func NewDedicatedHostID(subscriptionId, resourceGroup, hostGroupName, hostName string) DedicatedHostId { - return DedicatedHostId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - HostGroupName: hostGroupName, - HostName: hostName, - } -} - -func (id DedicatedHostId) String() string { - segments := []string{ - fmt.Sprintf("Host Name %q", id.HostName), - fmt.Sprintf("Host Group Name %q", id.HostGroupName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Dedicated Host", segmentsStr) -} - -func (id DedicatedHostId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/hostGroups/%s/hosts/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.HostGroupName, id.HostName) -} - -// DedicatedHostID parses a DedicatedHost ID into an DedicatedHostId struct -func DedicatedHostID(input string) (*DedicatedHostId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := DedicatedHostId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.HostGroupName, err = id.PopSegment("hostGroups"); err != nil { - return nil, err - } - if resourceId.HostName, err = id.PopSegment("hosts"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/compute/parse/dedicated_host_group.go b/internal/services/compute/parse/dedicated_host_group.go deleted file mode 100644 index 453a86e5019b..000000000000 --- a/internal/services/compute/parse/dedicated_host_group.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type DedicatedHostGroupId struct { - SubscriptionId string - ResourceGroup string - HostGroupName string -} - -func NewDedicatedHostGroupID(subscriptionId, resourceGroup, hostGroupName string) DedicatedHostGroupId { - return DedicatedHostGroupId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - HostGroupName: hostGroupName, - } -} - -func (id DedicatedHostGroupId) String() string { - segments := []string{ - fmt.Sprintf("Host Group Name %q", id.HostGroupName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Dedicated Host Group", segmentsStr) -} - -func (id DedicatedHostGroupId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/hostGroups/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.HostGroupName) -} - -// DedicatedHostGroupID parses a DedicatedHostGroup ID into an DedicatedHostGroupId struct -func DedicatedHostGroupID(input string) (*DedicatedHostGroupId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := DedicatedHostGroupId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.HostGroupName, err = id.PopSegment("hostGroups"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/compute/parse/dedicated_host_group_test.go b/internal/services/compute/parse/dedicated_host_group_test.go deleted file mode 100644 index 0888e81b0477..000000000000 --- a/internal/services/compute/parse/dedicated_host_group_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = DedicatedHostGroupId{} - -func TestDedicatedHostGroupIDFormatter(t *testing.T) { - actual := NewDedicatedHostGroupID("12345678-1234-9876-4563-123456789012", "resGroup1", "hostGroup1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestDedicatedHostGroupID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *DedicatedHostGroupId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", - Error: true, - }, - - { - // missing value for HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1", - Expected: &DedicatedHostGroupId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - HostGroupName: "hostGroup1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/HOSTGROUPS/HOSTGROUP1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := DedicatedHostGroupID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.HostGroupName != v.Expected.HostGroupName { - t.Fatalf("Expected %q but got %q for HostGroupName", v.Expected.HostGroupName, actual.HostGroupName) - } - } -} diff --git a/internal/services/compute/parse/dedicated_host_test.go b/internal/services/compute/parse/dedicated_host_test.go deleted file mode 100644 index 13475856972c..000000000000 --- a/internal/services/compute/parse/dedicated_host_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = DedicatedHostId{} - -func TestDedicatedHostIDFormatter(t *testing.T) { - actual := NewDedicatedHostID("12345678-1234-9876-4563-123456789012", "resGroup1", "hostGroup1", "host1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/host1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestDedicatedHostID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *DedicatedHostId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", - Error: true, - }, - - { - // missing value for HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/", - Error: true, - }, - - { - // missing HostName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/", - Error: true, - }, - - { - // missing value for HostName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/host1", - Expected: &DedicatedHostId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - HostGroupName: "hostGroup1", - HostName: "host1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/HOSTGROUPS/HOSTGROUP1/HOSTS/HOST1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := DedicatedHostID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.HostGroupName != v.Expected.HostGroupName { - t.Fatalf("Expected %q but got %q for HostGroupName", v.Expected.HostGroupName, actual.HostGroupName) - } - if actual.HostName != v.Expected.HostName { - t.Fatalf("Expected %q but got %q for HostName", v.Expected.HostName, actual.HostName) - } - } -} diff --git a/internal/services/compute/parse/proximity_placement_group.go b/internal/services/compute/parse/proximity_placement_group.go deleted file mode 100644 index abb24fffd41c..000000000000 --- a/internal/services/compute/parse/proximity_placement_group.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ProximityPlacementGroupId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewProximityPlacementGroupID(subscriptionId, resourceGroup, name string) ProximityPlacementGroupId { - return ProximityPlacementGroupId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id ProximityPlacementGroupId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Proximity Placement Group", segmentsStr) -} - -func (id ProximityPlacementGroupId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/proximityPlacementGroups/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// ProximityPlacementGroupID parses a ProximityPlacementGroup ID into an ProximityPlacementGroupId struct -func ProximityPlacementGroupID(input string) (*ProximityPlacementGroupId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ProximityPlacementGroupId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("proximityPlacementGroups"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/compute/parse/proximity_placement_group_test.go b/internal/services/compute/parse/proximity_placement_group_test.go deleted file mode 100644 index a5069aefd4fd..000000000000 --- a/internal/services/compute/parse/proximity_placement_group_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ProximityPlacementGroupId{} - -func TestProximityPlacementGroupIDFormatter(t *testing.T) { - actual := NewProximityPlacementGroupID("12345678-1234-9876-4563-123456789012", "group1", "proximityPlacementGroup1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/proximityPlacementGroup1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestProximityPlacementGroupID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ProximityPlacementGroupId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/proximityPlacementGroup1", - Expected: &ProximityPlacementGroupId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "group1", - Name: "proximityPlacementGroup1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.COMPUTE/PROXIMITYPLACEMENTGROUPS/PROXIMITYPLACEMENTGROUP1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ProximityPlacementGroupID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/compute/proximity_placement_group_data_source.go b/internal/services/compute/proximity_placement_group_data_source.go index e81b41cf9e24..1827f1522923 100644 --- a/internal/services/compute/proximity_placement_group_data_source.go +++ b/internal/services/compute/proximity_placement_group_data_source.go @@ -4,15 +4,15 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceProximityPlacementGroup() *pluginsdk.Resource { @@ -34,7 +34,7 @@ func dataSourceProximityPlacementGroup() *pluginsdk.Resource { "location": commonschema.LocationComputed(), - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } @@ -45,19 +45,25 @@ func dataSourceProximityPlacementGroupRead(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewProximityPlacementGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + id := proximityplacementgroups.NewProximityPlacementGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + resp, err := client.Get(ctx, id, proximityplacementgroups.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } - return fmt.Errorf("making Read request on %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } d.SetId(id.ID()) - d.Set("location", location.NormalizeNilable(resp.Location)) - return tags.FlattenAndSet(d, resp.Tags) + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } + } + + return nil } diff --git a/internal/services/compute/proximity_placement_group_resource.go b/internal/services/compute/proximity_placement_group_resource.go index 1de0703a126d..01039f9abd69 100644 --- a/internal/services/compute/proximity_placement_group_resource.go +++ b/internal/services/compute/proximity_placement_group_resource.go @@ -2,19 +2,18 @@ package compute import ( "fmt" - "log" "time" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceProximityPlacementGroup() *pluginsdk.Resource { @@ -25,7 +24,7 @@ func resourceProximityPlacementGroup() *pluginsdk.Resource { Delete: resourceProximityPlacementGroupDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.ProximityPlacementGroupID(id) + _, err := proximityplacementgroups.ParseProximityPlacementGroupID(id) return err }), @@ -44,11 +43,11 @@ func resourceProximityPlacementGroup() *pluginsdk.Resource { ValidateFunc: validation.StringIsNotEmpty, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), - "location": azure.SchemaLocation(), + "location": commonschema.Location(), - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } @@ -59,32 +58,28 @@ func resourceProximityPlacementGroupCreateUpdate(d *pluginsdk.ResourceData, meta ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for AzureRM Proximity Placement Group creation.") - - id := parse.NewProximityPlacementGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + id := proximityplacementgroups.NewProximityPlacementGroupID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + existing, err := client.Get(ctx, id, proximityplacementgroups.DefaultGetOperationOptions()) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing %s: %s", id, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_proximity_placement_group", id.ID()) } } - ppg := compute.ProximityPlacementGroup{ - Name: &id.Name, - Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + payload := proximityplacementgroups.ProximityPlacementGroup{ + Location: location.Normalize(d.Get("location").(string)), Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, ppg) - if err != nil { - return err + if _, err := client.CreateOrUpdate(ctx, id, payload); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } d.SetId(id.ID()) @@ -97,27 +92,31 @@ func resourceProximityPlacementGroupRead(d *pluginsdk.ResourceData, meta interfa ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ProximityPlacementGroupID(d.Id()) + id, err := proximityplacementgroups.ParseProximityPlacementGroupID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + resp, err := client.Get(ctx, *id, proximityplacementgroups.DefaultGetOperationOptions()) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("making Read request on Proximity Placement Group %q : %+v", id.String(), err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) + if model := resp.Model; model != nil { + d.Set("name", id.ProximityPlacementGroupName) + d.Set("resource_group_name", id.ResourceGroupName) + + d.Set("location", location.Normalize(model.Location)) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourceProximityPlacementGroupDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -125,11 +124,14 @@ func resourceProximityPlacementGroupDelete(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ProximityPlacementGroupID(d.Id()) + id, err := proximityplacementgroups.ParseProximityPlacementGroupID(d.Id()) if err != nil { return err } - _, err = client.Delete(ctx, id.ResourceGroup, id.Name) - return err + if _, err = client.Delete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil } diff --git a/internal/services/compute/proximity_placement_group_resource_test.go b/internal/services/compute/proximity_placement_group_resource_test.go index d8ffa6053186..b510deb4e1c8 100644 --- a/internal/services/compute/proximity_placement_group_resource_test.go +++ b/internal/services/compute/proximity_placement_group_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -88,30 +87,27 @@ func TestAccProximityPlacementGroup_withTags(t *testing.T) { } func (t ProximityPlacementGroupResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ProximityPlacementGroupID(state.ID) + id, err := proximityplacementgroups.ParseProximityPlacementGroupID(state.ID) if err != nil { return nil, err } - resp, err := clients.Compute.ProximityPlacementGroupsClient.Get(ctx, id.ResourceGroup, id.Name, "") + resp, err := clients.Compute.ProximityPlacementGroupsClient.Get(ctx, *id, proximityplacementgroups.DefaultGetOperationOptions()) if err != nil { - return nil, fmt.Errorf("retrieving Compute Proximity Placement Group %q", id) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (ProximityPlacementGroupResource) Destroy(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ProximityPlacementGroupID(state.ID) + id, err := proximityplacementgroups.ParseProximityPlacementGroupID(state.ID) if err != nil { return nil, err } - resp, err := client.Compute.ProximityPlacementGroupsClient.Delete(ctx, id.ResourceGroup, id.Name) - if err != nil { - if !response.WasNotFound(resp.Response) { - return nil, fmt.Errorf("deleting Proximity Placement Group %q: %+v", id, err) - } + if _, err := client.Compute.ProximityPlacementGroupsClient.Delete(ctx, *id); err != nil { + return nil, fmt.Errorf("deleting %s: %+v", *id, err) } return utils.Bool(true), nil diff --git a/internal/services/compute/resourceids.go b/internal/services/compute/resourceids.go index 46e1aa7c721f..1ef1ea274ea7 100644 --- a/internal/services/compute/resourceids.go +++ b/internal/services/compute/resourceids.go @@ -2,14 +2,11 @@ package compute //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=CapacityReservationGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/capacityReservationGroups/capacityReservationGroup1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=CapacityReservation -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/capacityReservationGroups/capacityReservationGroup1/capacityReservations/capacityReservation1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DedicatedHostGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DedicatedHost -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/host1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DiskEncryptionSet -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/diskEncryptionSets/set1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=GalleryApplication -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/applications/galleryApplication1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=GalleryApplicationVersion -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/applications/galleryApplication1/versions/galleryApplicationVersion1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Image -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/images/image1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedDisk -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/disks/disk1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProximityPlacementGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/proximityPlacementGroups/group1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SharedImage -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/images/image1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SharedImageGallery -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SharedImageVersion -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/images/image1/versions/version1 @@ -23,5 +20,4 @@ package compute //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DataDisk -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/virtualMachines/machine1/dataDisks/disk1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Snapshot -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/snapshots/snapshot1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Plan -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.MarketplaceOrdering/agreements/agreement1/offers/offer1/plans/hourly -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ProximityPlacementGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/proximityPlacementGroup1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=HostGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/hostGroups/hostgroup1 diff --git a/internal/services/compute/shared_image_data_source.go b/internal/services/compute/shared_image_data_source.go index 2dbf49fe8201..c211c418f291 100644 --- a/internal/services/compute/shared_image_data_source.go +++ b/internal/services/compute/shared_image_data_source.go @@ -41,6 +41,11 @@ func dataSourceSharedImage() *pluginsdk.Resource { "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + "architecture": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "os_type": { Type: pluginsdk.TypeString, Computed: true, @@ -131,6 +136,7 @@ func dataSourceSharedImageRead(d *pluginsdk.ResourceData, meta interface{}) erro d.Set("description", props.Description) d.Set("eula", props.Eula) d.Set("os_type", string(props.OsType)) + d.Set("architecture", string(props.Architecture)) d.Set("specialized", props.OsState == compute.OperatingSystemStateTypesSpecialized) d.Set("hyper_v_generation", string(props.HyperVGeneration)) d.Set("privacy_statement_uri", props.PrivacyStatementURI) diff --git a/internal/services/compute/shared_image_resource.go b/internal/services/compute/shared_image_resource.go index 8f2372db0361..12211d5cf043 100644 --- a/internal/services/compute/shared_image_resource.go +++ b/internal/services/compute/shared_image_resource.go @@ -60,6 +60,17 @@ func resourceSharedImage() *pluginsdk.Resource { "resource_group_name": azure.SchemaResourceGroupName(), + "architecture": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(compute.ArchitectureTypesX64), + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.ArchitectureTypesX64), + string(compute.ArchitectureTypesArm64), + }, false), + }, + "os_type": { Type: pluginsdk.TypeString, Required: true, @@ -107,19 +118,22 @@ func resourceSharedImage() *pluginsdk.Resource { Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "publisher": { - Type: pluginsdk.TypeString, - ForceNew: true, - Required: true, + Type: pluginsdk.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validate.SharedImageIdentifierAttribute, }, "offer": { - Type: pluginsdk.TypeString, - ForceNew: true, - Required: true, + Type: pluginsdk.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validate.SharedImageIdentifierAttribute, }, "sku": { - Type: pluginsdk.TypeString, - ForceNew: true, - Required: true, + Type: pluginsdk.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validate.SharedImageIdentifierAttribute, }, }, }, @@ -277,6 +291,7 @@ func resourceSharedImageCreateUpdate(d *pluginsdk.ResourceData, meta interface{} Identifier: expandGalleryImageIdentifier(d), PrivacyStatementURI: utils.String(d.Get("privacy_statement_uri").(string)), ReleaseNoteURI: utils.String(d.Get("release_note_uri").(string)), + Architecture: compute.Architecture(d.Get("architecture").(string)), OsType: compute.OperatingSystemTypes(d.Get("os_type").(string)), HyperVGeneration: compute.HyperVGeneration(d.Get("hyper_v_generation").(string)), PurchasePlan: expandGalleryImagePurchasePlan(d.Get("purchase_plan").([]interface{})), @@ -392,6 +407,7 @@ func resourceSharedImageRead(d *pluginsdk.ResourceData, meta interface{}) error d.Set("min_recommended_memory_in_gb", minRecommendedMemoryInGB) d.Set("os_type", string(props.OsType)) + d.Set("architecture", string(props.Architecture)) d.Set("specialized", props.OsState == compute.OperatingSystemStateTypesSpecialized) d.Set("hyper_v_generation", string(props.HyperVGeneration)) d.Set("privacy_statement_uri", props.PrivacyStatementURI) diff --git a/internal/services/compute/shared_image_resource_test.go b/internal/services/compute/shared_image_resource_test.go index 02baa96a135f..28c4cc910c11 100644 --- a/internal/services/compute/shared_image_resource_test.go +++ b/internal/services/compute/shared_image_resource_test.go @@ -47,6 +47,22 @@ func TestAccSharedImage_basic_hyperVGeneration_V2(t *testing.T) { }) } +func TestAccSharedImage_basic_Arm(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_shared_image", "test") + r := SharedImageResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicWithArch(data, "Arm64"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("description").HasValue(""), + check.That(data.ResourceName).Key("architecture").HasValue("Arm64"), + ), + }, + data.ImportStep(), + }) +} + func TestAccSharedImage_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_shared_image", "test") r := SharedImageResource{} @@ -343,6 +359,45 @@ resource "azurerm_shared_image" "test" { `, hyperVGen, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } +func (SharedImageResource) basicWithArch(data acceptance.TestData, arch string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +variable "architecture" { + default = "%s" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_shared_image_gallery" "test" { + name = "acctestsig%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_shared_image" "test" { + name = "acctestimg%d" + gallery_name = azurerm_shared_image_gallery.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + architecture = var.architecture != "" ? var.architecture : null + os_type = "Linux" + hyper_v_generation = "V2" + + identifier { + publisher = "AccTesPublisher%d" + offer = "AccTesOffer%d" + sku = "AccTesSku%d" + } +} +`, arch, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + func (SharedImageResource) specialized(data acceptance.TestData, hyperVGen string) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/compute/shared_image_version_data_source.go b/internal/services/compute/shared_image_version_data_source.go index 38bde5406b64..68cf07227afb 100644 --- a/internal/services/compute/shared_image_version_data_source.go +++ b/internal/services/compute/shared_image_version_data_source.go @@ -168,34 +168,40 @@ func obtainImage(client *compute.GalleryImageVersionsClient, ctx context.Context switch galleryImageVersionName { case "latest": - images, err := client.ListByGalleryImage(ctx, resourceGroup, galleryName, galleryImageName) + imagesIterator, err := client.ListByGalleryImageComplete(ctx, resourceGroup, galleryName, galleryImageName) if err != nil { - if utils.ResponseWasNotFound(images.Response().Response) { + if utils.ResponseWasNotFound(imagesIterator.Response().Response) { return nil, notFoundError } return nil, fmt.Errorf("retrieving Shared Image Versions (Image %q / Gallery %q / Resource Group %q): %+v", galleryImageName, galleryName, resourceGroup, err) } + images := make([]compute.GalleryImageVersion, 0) + for imagesIterator.NotDone() { + images = append(images, imagesIterator.Value()) + if err := imagesIterator.NextWithContext(ctx); err != nil { + return nil, fmt.Errorf("listing Shared Image Versions (Image %q / Gallery %q / Resource Group %q): %+v", galleryImageName, galleryName, resourceGroup, err) + } + } + // the last image in the list is the latest version - if len(images.Values()) > 0 { - values := images.Values() - var errs []error + if len(images) > 0 { if sortBySemVer { - values, errs = sortSharedImageVersions(values) + var errs []error + images, errs = sortSharedImageVersions(images) if len(errs) > 0 { return nil, fmt.Errorf("parsing version(s): %v", errs) } } - image := values[len(values)-1] + image := images[len(images)-1] return &image, nil } - return nil, notFoundError case "recent": - images, err := client.ListByGalleryImage(ctx, resourceGroup, galleryName, galleryImageName) + imagesIterator, err := client.ListByGalleryImageComplete(ctx, resourceGroup, galleryName, galleryImageName) if err != nil { - if utils.ResponseWasNotFound(images.Response().Response) { + if utils.ResponseWasNotFound(imagesIterator.Response().Response) { return nil, notFoundError } return nil, fmt.Errorf("retrieving Shared Image Versions (Image %q / Gallery %q / Resource Group %q): %+v", galleryImageName, galleryName, resourceGroup, err) @@ -203,13 +209,18 @@ func obtainImage(client *compute.GalleryImageVersionsClient, ctx context.Context var image *compute.GalleryImageVersion var recentDate *time.Time // compare dates until we find the image that was updated most recently - for _, currImage := range images.Values() { + for imagesIterator.NotDone() { + currImage := imagesIterator.Value() if profile := currImage.PublishingProfile; profile != nil { if profile.PublishedDate != nil && (recentDate == nil || profile.PublishedDate.Time.After(*recentDate)) { recentDate = &profile.PublishedDate.Time image = &currImage } } + + if err := imagesIterator.NextWithContext(ctx); err != nil { + return nil, fmt.Errorf("listing Shared Image Versions (Image %q / Gallery %q / Resource Group %q): %+v", galleryImageName, galleryName, resourceGroup, err) + } } if image != nil { diff --git a/internal/services/compute/shared_image_version_resource.go b/internal/services/compute/shared_image_version_resource.go index edd06bf961ca..361f6dbbb1fc 100644 --- a/internal/services/compute/shared_image_version_resource.go +++ b/internal/services/compute/shared_image_version_resource.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" + storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" @@ -111,6 +112,23 @@ func resourceSharedImageVersion() *pluginsdk.Resource { }, }, + "blob_uri": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsURLWithScheme([]string{"http", "https"}), + RequiredWith: []string{"storage_account_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, + }, + + "storage_account_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + RequiredWith: []string{"blob_uri"}, + ValidateFunc: storageValidate.StorageAccountID, + }, + "end_of_life_date": { Type: pluginsdk.TypeString, Optional: true, @@ -122,7 +140,7 @@ func resourceSharedImageVersion() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, - ExactlyOneOf: []string{"os_disk_snapshot_id", "managed_image_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, // TODO -- add a validation function when snapshot has its own validation function }, @@ -134,7 +152,7 @@ func resourceSharedImageVersion() *pluginsdk.Resource { validate.ImageID, validate.VirtualMachineID, ), - ExactlyOneOf: []string{"os_disk_snapshot_id", "managed_image_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, }, "replication_mode": { @@ -225,6 +243,15 @@ func resourceSharedImageVersionCreateUpdate(d *pluginsdk.ResourceData, meta inte } } + if v, ok := d.GetOk("blob_uri"); ok { + version.GalleryImageVersionProperties.StorageProfile.OsDiskImage = &compute.GalleryOSDiskImage{ + Source: &compute.GalleryArtifactVersionSource{ + ID: utils.String(d.Get("storage_account_id").(string)), + URI: utils.String(v.(string)), + }, + } + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.GalleryName, id.ImageName, id.VersionName, version) if err != nil { return fmt.Errorf("creating %s: %+v", id, err) @@ -292,11 +319,24 @@ func resourceSharedImageVersionRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("managed_image_id", source.ID) } + blobURI := "" + if profile.OsDiskImage != nil && profile.OsDiskImage.Source != nil && profile.OsDiskImage.Source.URI != nil { + blobURI = *profile.OsDiskImage.Source.URI + } + d.Set("blob_uri", blobURI) + osDiskSnapShotID := "" + storageAccountID := "" if profile.OsDiskImage != nil && profile.OsDiskImage.Source != nil && profile.OsDiskImage.Source.ID != nil { - osDiskSnapShotID = *profile.OsDiskImage.Source.ID + sourceID := *profile.OsDiskImage.Source.ID + if blobURI == "" { + osDiskSnapShotID = sourceID + } else { + storageAccountID = sourceID + } } d.Set("os_disk_snapshot_id", osDiskSnapShotID) + d.Set("storage_account_id", storageAccountID) } } diff --git a/internal/services/compute/shared_image_version_resource_test.go b/internal/services/compute/shared_image_version_resource_test.go index d06b1e2a899c..3dab9c7286f1 100644 --- a/internal/services/compute/shared_image_version_resource_test.go +++ b/internal/services/compute/shared_image_version_resource_test.go @@ -102,6 +102,21 @@ func TestAccSharedImageVersion_storageAccountTypeZrs(t *testing.T) { }) } +func TestAccSharedImageVersion_blobURI(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") + r := SharedImageVersionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.imageVersionBlobURI(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccSharedImageVersion_specializedImageVersionBySnapshot(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") r := SharedImageVersionResource{} @@ -330,6 +345,49 @@ resource "azurerm_shared_image_version" "test" { `, template) } +func (r SharedImageVersionResource) imageVersionBlobURI(data acceptance.TestData) string { + template := r.setup(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_shared_image_gallery" "test" { + name = "acctestsig%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_shared_image" "test" { + name = "acctestimg%[2]d" + gallery_name = azurerm_shared_image_gallery.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + os_type = "Linux" + + identifier { + publisher = "AccTesPublisher%[2]d" + offer = "AccTesOffer%[2]d" + sku = "AccTesSku%[2]d" + } +} + +resource "azurerm_shared_image_version" "test" { + name = "0.0.1" + gallery_name = azurerm_shared_image_gallery.test.name + image_name = azurerm_shared_image.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + blob_uri = azurerm_virtual_machine.testsource.storage_os_disk[0].vhd_uri + storage_account_id = azurerm_storage_account.test.id + + target_region { + name = azurerm_resource_group.test.location + regional_replica_count = 1 + } +} +`, template, data.RandomInteger) +} + func (r SharedImageVersionResource) provisionSpecialized(data acceptance.TestData) string { template := ImageResource{}.setupManagedDisks(data) return fmt.Sprintf(` diff --git a/internal/services/compute/validate/compute.go b/internal/services/compute/validate/compute.go index 3b23edd19c41..8889538ad3bb 100644 --- a/internal/services/compute/validate/compute.go +++ b/internal/services/compute/validate/compute.go @@ -3,6 +3,7 @@ package validate import ( "fmt" "regexp" + "strings" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -40,6 +41,25 @@ func SharedImageName(v interface{}, k string) (warnings []string, errors []error return warnings, errors } +func SharedImageIdentifierAttribute(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + length := len(value) + if length > 128 { + errors = append(errors, fmt.Errorf("%s can be up to 128 characters, currently %d.", k, length)) + } + + if strings.HasSuffix(value, ".") { + errors = append(errors, fmt.Errorf("%q can not end with a '.', got %q", k, value)) + } + + if !regexp.MustCompile(`^[A-Za-z0-9._-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%s can only contain alphanumeric, full stops, dashes and underscores. Got %q.", k, value)) + } + + return warnings, errors +} + func SharedImageVersionName(v interface{}, k string) (warnings []string, errors []error) { value := v.(string) diff --git a/internal/services/compute/validate/compute_test.go b/internal/services/compute/validate/compute_test.go index f7773a8c8221..25774f291b87 100644 --- a/internal/services/compute/validate/compute_test.go +++ b/internal/services/compute/validate/compute_test.go @@ -123,6 +123,69 @@ func TestSharedImageName(t *testing.T) { } } +func TestSharedImageIdentifierAttribute(t *testing.T) { + cases := []struct { + Input string + ShouldError bool + }{ + { + Input: "", + ShouldError: true, + }, + { + Input: "hello", + ShouldError: false, + }, + { + Input: "hello.", + ShouldError: true, + }, + { + Input: "hello123", + ShouldError: false, + }, + { + Input: "hello.123", + ShouldError: false, + }, + { + Input: "hello,123", + ShouldError: true, + }, + { + Input: "hello_123", + ShouldError: false, + }, + { + Input: "hello-123", + ShouldError: false, + }, + { + Input: strings.Repeat("a", 128), + ShouldError: false, + }, + { + Input: strings.Repeat("a", 129), + ShouldError: true, + }, + } + + for _, tc := range cases { + t.Run(tc.Input, func(t *testing.T) { + _, errors := SharedImageIdentifierAttribute(tc.Input, "test") + + 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)) + } + }) + } +} + func TestSharedImageVersionName(t *testing.T) { cases := []struct { Input string diff --git a/internal/services/compute/validate/dedicated_host_group_id.go b/internal/services/compute/validate/dedicated_host_group_id.go deleted file mode 100644 index 94f6c944dcca..000000000000 --- a/internal/services/compute/validate/dedicated_host_group_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" -) - -func DedicatedHostGroupID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.DedicatedHostGroupID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/compute/validate/dedicated_host_group_id_test.go b/internal/services/compute/validate/dedicated_host_group_id_test.go deleted file mode 100644 index 2c2af34a70c0..000000000000 --- a/internal/services/compute/validate/dedicated_host_group_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestDedicatedHostGroupID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", - Valid: false, - }, - - { - // missing value for HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/HOSTGROUPS/HOSTGROUP1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := DedicatedHostGroupID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/compute/validate/dedicated_host_id.go b/internal/services/compute/validate/dedicated_host_id.go deleted file mode 100644 index 2aaf7be23fd5..000000000000 --- a/internal/services/compute/validate/dedicated_host_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" -) - -func DedicatedHostID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.DedicatedHostID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/compute/validate/dedicated_host_id_test.go b/internal/services/compute/validate/dedicated_host_id_test.go deleted file mode 100644 index 359fdd9f7cad..000000000000 --- a/internal/services/compute/validate/dedicated_host_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestDedicatedHostID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", - Valid: false, - }, - - { - // missing value for HostGroupName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/", - Valid: false, - }, - - { - // missing HostName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/", - Valid: false, - }, - - { - // missing value for HostName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/hostGroups/hostGroup1/hosts/host1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/HOSTGROUPS/HOSTGROUP1/HOSTS/HOST1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := DedicatedHostID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/compute/validate/proximity_placement_group_id.go b/internal/services/compute/validate/proximity_placement_group_id.go deleted file mode 100644 index 2c48bb9ad7a8..000000000000 --- a/internal/services/compute/validate/proximity_placement_group_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" -) - -func ProximityPlacementGroupID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ProximityPlacementGroupID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/compute/validate/proximity_placement_group_id_test.go b/internal/services/compute/validate/proximity_placement_group_id_test.go deleted file mode 100644 index 413553dba3ff..000000000000 --- a/internal/services/compute/validate/proximity_placement_group_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestProximityPlacementGroupID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/proximityPlacementGroup1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.COMPUTE/PROXIMITYPLACEMENTGROUPS/PROXIMITYPLACEMENTGROUP1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ProximityPlacementGroupID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/compute/virtual_machine_extension_resource.go b/internal/services/compute/virtual_machine_extension_resource.go index 11300b08bef8..101c02955d6e 100644 --- a/internal/services/compute/virtual_machine_extension_resource.go +++ b/internal/services/compute/virtual_machine_extension_resource.go @@ -40,6 +40,10 @@ func resourceVirtualMachineExtension() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.All( + validation.StringIsNotEmpty, + validation.StringDoesNotContainAny("/"), + ), }, "virtual_machine_id": { diff --git a/internal/services/compute/virtual_machine_scale_set_data_source.go b/internal/services/compute/virtual_machine_scale_set_data_source.go index 014d91762794..9c6c88c6c9c8 100644 --- a/internal/services/compute/virtual_machine_scale_set_data_source.go +++ b/internal/services/compute/virtual_machine_scale_set_data_source.go @@ -1,13 +1,17 @@ package compute import ( + "context" "fmt" "time" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" + networkParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -36,12 +40,79 @@ func dataSourceVirtualMachineScaleSet() *pluginsdk.Resource { "network_interface": VirtualMachineScaleSetNetworkInterfaceSchemaForDataSource(), "identity": commonschema.SystemAssignedUserAssignedIdentityComputed(), + + "instances": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "computer_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "instance_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "latest_model_applied": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "private_ip_address": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "private_ip_addresses": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "public_ip_address": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "public_ip_addresses": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "virtual_machine_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "zone": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, }, } } func dataSourceVirtualMachineScaleSetRead(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Compute.VMScaleSetClient + instancesClient := meta.(*clients.Client).Compute.VMScaleSetVMsClient + networkInterfacesClient := meta.(*clients.Client).Network.InterfacesClient + publicIPAddressesClient := meta.(*clients.Client).Network.PublicIPsClient subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -82,5 +153,130 @@ func dataSourceVirtualMachineScaleSetRead(d *pluginsdk.ResourceData, meta interf return fmt.Errorf("setting `identity`: %+v", err) } + instances := make([]interface{}, 0) + result, err := instancesClient.ListComplete(ctx, id.ResourceGroup, id.Name, "", "", "") + if err != nil { + return fmt.Errorf("listing VM Instances for Virtual Machine Scale Set %q (Resource Group %q): %+v", id.ResourceGroup, id.Name, err) + } + + for result.NotDone() { + instance := result.Value() + if instance.InstanceID != nil { + nics, err := networkInterfacesClient.ListVirtualMachineScaleSetVMNetworkInterfacesComplete(ctx, id.ResourceGroup, id.Name, *instance.InstanceID) + if err != nil { + return fmt.Errorf("listing Network Interfaces for VM Instance %q for Virtual Machine Scale Set %q (Resource Group %q): %+v", *instance.InstanceID, id.ResourceGroup, id.Name, err) + } + + networkInterfaces := make([]network.Interface, 0) + for nics.NotDone() { + networkInterfaces = append(networkInterfaces, nics.Value()) + if err := nics.NextWithContext(ctx); err != nil { + return fmt.Errorf("listing next page of Network Interfaces for VM Instance %q of Virtual Machine Scale Set %q (Resource Group %q): %v", *instance.InstanceID, id.ResourceGroup, id.Name, err) + } + } + + connectionInfo, err := getVirtualMachineScaleSetVMConnectionInfo(ctx, networkInterfaces, id.ResourceGroup, id.Name, *instance.InstanceID, publicIPAddressesClient) + if err != nil { + return err + } + + flattenedInstances := flattenVirtualMachineScaleSetVM(instance, connectionInfo) + instances = append(instances, flattenedInstances) + } + + if err := result.NextWithContext(ctx); err != nil { + return fmt.Errorf("listing next page VM Instances for Virtual Machine Scale Set %q (Resource Group %q): %+v", id.ResourceGroup, id.Name, err) + } + } + if err := d.Set("instances", instances); err != nil { + return fmt.Errorf("setting `instances`: %+v", err) + } + return nil } + +func getVirtualMachineScaleSetVMConnectionInfo(ctx context.Context, networkInterfaces []network.Interface, resourceGroupName string, virtualMachineScaleSetName string, virtualmachineIndex string, publicIPAddressesClient *network.PublicIPAddressesClient) (*connectionInfo, error) { + if len(networkInterfaces) == 0 { + return nil, nil + } + + primaryPublicAddress := "" + primaryPrivateAddress := "" + publicIPAddresses := make([]string, 0) + privateIPAddresses := make([]string, 0) + + for _, nic := range networkInterfaces { + for _, config := range *nic.IPConfigurations { + if props := config.InterfaceIPConfigurationPropertiesFormat; props != nil { + if pip := props.PublicIPAddress; pip != nil { + pipID, err := networkParse.VirtualMachineScaleSetPublicIPAddressID(*pip.ID) + if err != nil { + return nil, err + } + + publicIPAddress, err := publicIPAddressesClient.GetVirtualMachineScaleSetPublicIPAddress(ctx, resourceGroupName, virtualMachineScaleSetName, virtualmachineIndex, pipID.NetworkInterfaceName, pipID.IpConfigurationName, pipID.PublicIPAddressName, "") + if err != nil { + return nil, fmt.Errorf("reading Public IP Address for VM Instance %q for Virtual Machine Scale Set %q (Resource Group %q): %+v", virtualmachineIndex, virtualMachineScaleSetName, resourceGroupName, err) + } + + if *nic.Primary && *props.Primary { + primaryPublicAddress = *publicIPAddress.IPAddress + } + publicIPAddresses = append(publicIPAddresses, *publicIPAddress.IPAddress) + } + + if props.PrivateIPAddress != nil { + if *nic.Primary && *props.Primary { + primaryPrivateAddress = *props.PrivateIPAddress + } + privateIPAddresses = append(privateIPAddresses, *props.PrivateIPAddress) + } + } + } + } + + if primaryPublicAddress == "" && len(publicIPAddresses) > 0 { + primaryPublicAddress = publicIPAddresses[0] + } + + if primaryPrivateAddress == "" && len(privateIPAddresses) > 0 { + primaryPrivateAddress = privateIPAddresses[0] + } + + return &connectionInfo{ + primaryPublicAddress: primaryPublicAddress, + publicAddresses: publicIPAddresses, + primaryPrivateAddress: primaryPrivateAddress, + privateAddresses: privateIPAddresses, + }, nil +} + +func flattenVirtualMachineScaleSetVM(input compute.VirtualMachineScaleSetVM, connectionInfo *connectionInfo) map[string]interface{} { + output := make(map[string]interface{}) + output["name"] = *input.Name + output["instance_id"] = *input.InstanceID + output["latest_model_applied"] = *input.LatestModelApplied + output["virtual_machine_id"] = *input.VMID + + props := *input.VirtualMachineScaleSetVMProperties + if profile := props.OsProfile; profile != nil { + output["computer_name"] = profile.ComputerName + } + + zone := "" + if input.Zones != nil { + if zones := *input.Zones; len(zones) > 0 { + zone = zones[0] + } + } + output["zone"] = zone + + if connectionInfo != nil { + output["private_ip_address"] = connectionInfo.primaryPrivateAddress + output["private_ip_addresses"] = connectionInfo.privateAddresses + output["public_ip_address"] = connectionInfo.primaryPublicAddress + output["public_ip_addresses"] = connectionInfo.publicAddresses + } + + return output +} diff --git a/internal/services/compute/virtual_machine_scale_set_data_source_test.go b/internal/services/compute/virtual_machine_scale_set_data_source_test.go index 385f5d0c66e9..539aaef08dff 100644 --- a/internal/services/compute/virtual_machine_scale_set_data_source_test.go +++ b/internal/services/compute/virtual_machine_scale_set_data_source_test.go @@ -21,6 +21,9 @@ func TestAccDataSourceVirtualMachineScaleSet_basicLinux(t *testing.T) { check.That(data.ResourceName).Key("identity.#").HasValue("1"), check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), + check.That(data.ResourceName).Key("instances.#").HasValue("1"), + check.That(data.ResourceName).Key("instances.0.instance_id").HasValue("0"), + check.That(data.ResourceName).Key("instances.0.private_ip_address").HasValue("10.0.2.4"), ), }, }) diff --git a/internal/services/compute/virtual_machine_scale_set_extension_resource.go b/internal/services/compute/virtual_machine_scale_set_extension_resource.go index 21f653ade4f2..d023c04909d3 100644 --- a/internal/services/compute/virtual_machine_scale_set_extension_resource.go +++ b/internal/services/compute/virtual_machine_scale_set_extension_resource.go @@ -39,10 +39,13 @@ func resourceVirtualMachineScaleSetExtension() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringIsNotEmpty, + validation.StringDoesNotContainAny("/"), + ), }, "virtual_machine_scale_set_id": { diff --git a/internal/services/compute/windows_virtual_machine_resource.go b/internal/services/compute/windows_virtual_machine_resource.go index 0a68f6a74a16..1bdd33e700ee 100644 --- a/internal/services/compute/windows_virtual_machine_resource.go +++ b/internal/services/compute/windows_virtual_machine_resource.go @@ -12,6 +12,9 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/availabilitysets" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhostgroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" @@ -151,7 +154,7 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { "dedicated_host_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.DedicatedHostID, + ValidateFunc: dedicatedhosts.ValidateHostID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE :shrug: // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, @@ -163,7 +166,7 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { "dedicated_host_group_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.DedicatedHostGroupID, + ValidateFunc: dedicatedhostgroups.ValidateHostGroupID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, @@ -193,8 +196,8 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - // NOTE: whilst Delete is an option here, it's only applicable for VMSS string(compute.VirtualMachineEvictionPolicyTypesDeallocate), + string(compute.VirtualMachineEvictionPolicyTypesDelete), }, false), }, @@ -271,7 +274,7 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { "proximity_placement_group_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: computeValidate.ProximityPlacementGroupID, + ValidateFunc: proximityplacementgroups.ValidateProximityPlacementGroupID, // the Compute/VM API is broken and returns the Resource Group name in UPPERCASE :shrug: // tracked by https://github.com/Azure/azure-rest-api-specs/issues/19424 DiffSuppressFunc: suppress.CaseDifference, diff --git a/internal/services/compute/windows_virtual_machine_resource_disk_os_test.go b/internal/services/compute/windows_virtual_machine_resource_disk_os_test.go index 16261a27a83c..8fa9c294d283 100644 --- a/internal/services/compute/windows_virtual_machine_resource_disk_os_test.go +++ b/internal/services/compute/windows_virtual_machine_resource_disk_os_test.go @@ -156,6 +156,21 @@ func TestAccWindowsVirtualMachine_diskOSEphemeralDefault(t *testing.T) { }) } +func TestAccWindowsVirtualMachine_diskOSEphemeralSpot(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + r := WindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskOSEphemeralSpot(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + }) +} + func TestAccWindowsVirtualMachine_diskOSEphemeralResourceDisk(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") r := WindowsVirtualMachineResource{} @@ -709,6 +724,43 @@ resource "azurerm_windows_virtual_machine" "test" { `, r.template(data)) } +func (r WindowsVirtualMachineResource) diskOSEphemeralSpot(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + # Message="OS disk of Ephemeral VM with size greater than 32 GB is not allowed for VM size Standard_F2s_v2" + size = "Standard_DS3_v2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + priority = "Spot" + eviction_policy = "Delete" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadOnly" + storage_account_type = "Standard_LRS" + + diff_disk_settings { + option = "Local" + } + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } +} +`, r.template(data)) +} + func (r WindowsVirtualMachineResource) diskOSEphemeralResourceDisk(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/consumption/client/client.go b/internal/services/consumption/client/client.go index 04040a0d9dd1..e867b6340157 100644 --- a/internal/services/consumption/client/client.go +++ b/internal/services/consumption/client/client.go @@ -1,16 +1,16 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/consumption/mgmt/2019-10-01/consumption" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - BudgetsClient *consumption.BudgetsClient + BudgetsClient *budgets.BudgetsClient } func NewClient(o *common.ClientOptions) *Client { - budgetsClient := consumption.NewBudgetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + budgetsClient := budgets.NewBudgetsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&budgetsClient.Client, o.ResourceManagerAuthorizer) return &Client{ diff --git a/internal/services/consumption/consumption_budget_base.go b/internal/services/consumption/consumption_budget_base.go index 25ac88c1d9c8..72ff97aa075b 100644 --- a/internal/services/consumption/consumption_budget_base.go +++ b/internal/services/consumption/consumption_budget_base.go @@ -5,16 +5,15 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/consumption/mgmt/2019-10-01/consumption" "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/utils" - "github.com/shopspring/decimal" ) type consumptionBudgetBaseResource struct{} @@ -227,10 +226,10 @@ func (br consumptionBudgetBaseResource) arguments(fields map[string]*pluginsdk.S "threshold_type": { Type: pluginsdk.TypeString, Optional: true, - Default: string(consumption.ThresholdTypeActual), + Default: string(budgets.ThresholdTypeActual), ForceNew: true, // TODO: remove this when the above issue is fixed ValidateFunc: validation.StringInSlice([]string{ - string(consumption.ThresholdTypeActual), + string(budgets.ThresholdTypeActual), "Forecasted", }, false), }, @@ -238,9 +237,9 @@ func (br consumptionBudgetBaseResource) arguments(fields map[string]*pluginsdk.S Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(consumption.OperatorTypeEqualTo), - string(consumption.OperatorTypeGreaterThan), - string(consumption.OperatorTypeGreaterThanOrEqualTo), + string(budgets.OperatorTypeEqualTo), + string(budgets.OperatorTypeGreaterThan), + string(budgets.OperatorTypeGreaterThanOrEqualTo), }, false), }, @@ -277,15 +276,15 @@ func (br consumptionBudgetBaseResource) arguments(fields map[string]*pluginsdk.S "time_grain": { Type: pluginsdk.TypeString, Optional: true, - Default: string(consumption.TimeGrainTypeMonthly), + Default: string(budgets.TimeGrainTypeMonthly), ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(consumption.TimeGrainTypeBillingAnnual), - string(consumption.TimeGrainTypeBillingMonth), - string(consumption.TimeGrainTypeBillingQuarter), - string(consumption.TimeGrainTypeAnnually), - string(consumption.TimeGrainTypeMonthly), - string(consumption.TimeGrainTypeQuarterly), + string(budgets.TimeGrainTypeBillingAnnual), + string(budgets.TimeGrainTypeBillingMonth), + string(budgets.TimeGrainTypeBillingQuarter), + string(budgets.TimeGrainTypeAnnually), + string(budgets.TimeGrainTypeMonthly), + string(budgets.TimeGrainTypeQuarterly), }, false), }, @@ -334,16 +333,16 @@ func (br consumptionBudgetBaseResource) createFunc(resourceName, scopeFieldName var err error scope := metadata.ResourceData.Get(scopeFieldName).(string) - id := parse.NewConsumptionBudgetId(scope, metadata.ResourceData.Get("name").(string)) + id := budgets.NewScopedBudgetID(scope, metadata.ResourceData.Get("name").(string)) - existing, err := client.Get(ctx, id.Scope, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError(resourceName, id.ID()) } @@ -362,38 +361,38 @@ func (br consumptionBudgetBaseResource) readFunc(scopeFieldName string) sdk.Reso Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.Consumption.BudgetsClient - id, err := parse.ConsumptionBudgetID(metadata.ResourceData.Id()) + id, err := budgets.ParseScopedBudgetID(metadata.ResourceData.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.Scope, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } return fmt.Errorf("reading %s, %+v", *id, err) } - metadata.ResourceData.Set("name", id.Name) + metadata.ResourceData.Set("name", id.BudgetName) //lintignore:R001 metadata.ResourceData.Set(scopeFieldName, id.Scope) - amount := 0.0 - if v := resp.Amount; v != nil { - amount, _ = v.Float64() - } - metadata.ResourceData.Set("amount", amount) - - eTag := "" - if v := resp.ETag; v != nil { - eTag = *v + if model := resp.Model; model != nil { + eTag := "" + if v := model.ETag; v != nil { + eTag = *v + } + metadata.ResourceData.Set("etag", eTag) + + if props := model.Properties; props != nil { + metadata.ResourceData.Set("amount", props.Amount) + metadata.ResourceData.Set("time_grain", string(props.TimeGrain)) + metadata.ResourceData.Set("time_period", flattenConsumptionBudgetTimePeriod(&props.TimePeriod)) + metadata.ResourceData.Set("notification", flattenConsumptionBudgetNotifications(props.Notifications, scopeFieldName)) + metadata.ResourceData.Set("filter", flattenConsumptionBudgetFilter(props.Filter)) + } } - metadata.ResourceData.Set("etag", eTag) - metadata.ResourceData.Set("time_grain", string(resp.TimeGrain)) - metadata.ResourceData.Set("time_period", flattenConsumptionBudgetTimePeriod(resp.TimePeriod)) - metadata.ResourceData.Set("notification", flattenConsumptionBudgetNotifications(resp.Notifications, scopeFieldName)) - metadata.ResourceData.Set("filter", flattenConsumptionBudgetFilter(resp.Filter)) return nil }, @@ -405,12 +404,12 @@ func (br consumptionBudgetBaseResource) deleteFunc() sdk.ResourceFunc { Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.Consumption.BudgetsClient - id, err := parse.ConsumptionBudgetID(metadata.ResourceData.Id()) + id, err := budgets.ParseScopedBudgetID(metadata.ResourceData.Id()) if err != nil { return err } - if _, err = client.Delete(ctx, id.Scope, id.Name); err != nil { + if _, err = client.Delete(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } @@ -425,7 +424,7 @@ func (br consumptionBudgetBaseResource) updateFunc() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.Consumption.BudgetsClient - id, err := parse.ConsumptionBudgetID(metadata.ResourceData.Id()) + id, err := budgets.ParseScopedBudgetID(metadata.ResourceData.Id()) if err != nil { return err } @@ -439,33 +438,18 @@ func (br consumptionBudgetBaseResource) updateFunc() sdk.ResourceFunc { } } -func (br consumptionBudgetBaseResource) importerFunc(expectScope string) sdk.ResourceRunFunc { +func (br consumptionBudgetBaseResource) importerFunc() sdk.ResourceRunFunc { return func(ctx context.Context, metadata sdk.ResourceMetaData) error { - var err error - id, err := parse.ConsumptionBudgetID(metadata.ResourceData.Id()) + _, err := budgets.ParseScopedBudgetID(metadata.ResourceData.Id()) if err != nil { return err } - switch expectScope { - case "subscription": - _, err = parse.ConsumptionBudgetSubscriptionID(metadata.ResourceData.Id()) - case "resource_group": - _, err = parse.ConsumptionBudgetResourceGroupID(metadata.ResourceData.Id()) - case "management_group": - _, err = parse.ConsumptionBudgetManagementGroupID(metadata.ResourceData.Id()) - } - - if err != nil { - return fmt.Errorf("budget has mismatched scope, expected a budget with %s scope, got %s", expectScope, id.Scope) - } - return nil } } -func createOrUpdateConsumptionBudget(ctx context.Context, client *consumption.BudgetsClient, metadata sdk.ResourceMetaData, id parse.ConsumptionBudgetId) error { - amount := decimal.NewFromFloat(metadata.ResourceData.Get("amount").(float64)) +func createOrUpdateConsumptionBudget(ctx context.Context, client *budgets.BudgetsClient, metadata sdk.ResourceMetaData, id budgets.ScopedBudgetId) error { timePeriod, err := expandConsumptionBudgetTimePeriod(metadata.ResourceData.Get("time_period").([]interface{})) if err != nil { return fmt.Errorf("expanding `time_period`: %+v", err) @@ -473,16 +457,16 @@ func createOrUpdateConsumptionBudget(ctx context.Context, client *consumption.Bu // The Consumption Budget API requires the category type field to be set in a budget's properties. // 'Cost' is the only valid Budget type today according to the API spec. - category := "Cost" - parameters := consumption.Budget{ - Name: utils.String(id.Name), - BudgetProperties: &consumption.BudgetProperties{ - Amount: &amount, - Category: &category, + + parameters := budgets.Budget{ + Name: utils.String(id.BudgetName), + Properties: &budgets.BudgetProperties{ + Amount: metadata.ResourceData.Get("amount").(float64), + Category: budgets.CategoryTypeCost, Filter: expandConsumptionBudgetFilter(metadata.ResourceData.Get("filter").([]interface{})), Notifications: expandConsumptionBudgetNotifications(metadata.ResourceData.Get("notification").(*pluginsdk.Set).List()), - TimeGrain: consumption.TimeGrainType(metadata.ResourceData.Get("time_grain").(string)), - TimePeriod: timePeriod, + TimeGrain: budgets.TimeGrainType(metadata.ResourceData.Get("time_grain").(string)), + TimePeriod: *timePeriod, }, } @@ -490,7 +474,7 @@ func createOrUpdateConsumptionBudget(ctx context.Context, client *consumption.Bu parameters.ETag = utils.String(v.(string)) } - _, err = client.CreateOrUpdate(ctx, id.Scope, id.Name, parameters) + _, err = client.CreateOrUpdate(ctx, id, parameters) if err != nil { return err } @@ -498,56 +482,48 @@ func createOrUpdateConsumptionBudget(ctx context.Context, client *consumption.Bu return nil } -func expandConsumptionBudgetTimePeriod(i []interface{}) (*consumption.BudgetTimePeriod, error) { +func expandConsumptionBudgetTimePeriod(i []interface{}) (*budgets.BudgetTimePeriod, error) { if len(i) == 0 || i[0] == nil { return nil, nil } input := i[0].(map[string]interface{}) - timePeriod := consumption.BudgetTimePeriod{} + timePeriod := budgets.BudgetTimePeriod{} if startDateInput, ok := input["start_date"].(string); ok { - startDate, err := date.ParseTime(time.RFC3339, startDateInput) + _, err := date.ParseTime(time.RFC3339, startDateInput) if err != nil { return nil, fmt.Errorf("start_date '%s' was not in the correct format: %+v", startDateInput, err) } - - timePeriod.StartDate = &date.Time{ - Time: startDate, - } + timePeriod.StartDate = input["start_date"].(string) } if endDateInput, ok := input["end_date"].(string); ok { if endDateInput != "" { - endDate, err := date.ParseTime(time.RFC3339, endDateInput) + _, err := date.ParseTime(time.RFC3339, endDateInput) if err != nil { return nil, fmt.Errorf("end_date '%s' was not in the correct format: %+v", endDateInput, err) } - timePeriod.EndDate = &date.Time{ - Time: endDate, - } + timePeriod.EndDate = utils.String(input["end_date"].(string)) } } return &timePeriod, nil } -func flattenConsumptionBudgetTimePeriod(input *consumption.BudgetTimePeriod) []interface{} { +func flattenConsumptionBudgetTimePeriod(input *budgets.BudgetTimePeriod) []interface{} { timePeriod := make([]interface{}, 0) if input == nil { return timePeriod } - startDate := "" - if v := input.StartDate; v != nil { - startDate = v.String() - } + startDate := input.StartDate endDate := "" if v := input.EndDate; v != nil { - endDate = v.String() + endDate = *v } return append(timePeriod, map[string]interface{}{ @@ -556,27 +532,28 @@ func flattenConsumptionBudgetTimePeriod(input *consumption.BudgetTimePeriod) []i }) } -func expandConsumptionBudgetNotifications(input []interface{}) map[string]*consumption.Notification { +func expandConsumptionBudgetNotifications(input []interface{}) *map[string]budgets.Notification { if len(input) == 0 { return nil } - notifications := make(map[string]*consumption.Notification) + notifications := make(map[string]budgets.Notification) for _, v := range input { if v != nil { notificationRaw := v.(map[string]interface{}) - notification := consumption.Notification{} + notification := budgets.Notification{} - notification.Enabled = utils.Bool(notificationRaw["enabled"].(bool)) - notification.Operator = consumption.OperatorType(notificationRaw["operator"].(string)) + notification.Enabled = notificationRaw["enabled"].(bool) + notification.Operator = budgets.OperatorType(notificationRaw["operator"].(string)) - thresholdDecimal := decimal.NewFromInt(int64(notificationRaw["threshold"].(int))) - notification.Threshold = &thresholdDecimal + notification.Threshold = float64(notificationRaw["threshold"].(int)) - notification.ThresholdType = consumption.ThresholdType(notificationRaw["threshold_type"].(string)) + thresholdType := budgets.ThresholdType(notificationRaw["threshold_type"].(string)) + notification.ThresholdType = &thresholdType - notification.ContactEmails = utils.ExpandStringSlice(notificationRaw["contact_emails"].([]interface{})) + contactEmails := utils.ExpandStringSlice(notificationRaw["contact_emails"].([]interface{})) + notification.ContactEmails = *contactEmails // contact_roles cannot be set on consumption budgets for management groups if _, ok := notificationRaw["contact_roles"]; ok { @@ -588,110 +565,98 @@ func expandConsumptionBudgetNotifications(input []interface{}) map[string]*consu notification.ContactGroups = utils.ExpandStringSlice(notificationRaw["contact_groups"].([]interface{})) } - notificationKey := fmt.Sprintf("actual_%s_%s_Percent", string(notification.Operator), notification.Threshold.StringFixed(0)) - notifications[notificationKey] = ¬ification + notificationKey := fmt.Sprintf("actual_%s_%f_Percent", string(notification.Operator), notification.Threshold) + notifications[notificationKey] = notification } } - return notifications + return ¬ifications } -func flattenConsumptionBudgetNotifications(input map[string]*consumption.Notification, scope string) []interface{} { +func flattenConsumptionBudgetNotifications(input *map[string]budgets.Notification, scope string) []interface{} { if input == nil { return []interface{}{} } notifications := make([]interface{}, 0) - for _, n := range input { - if n != nil { - block := make(map[string]interface{}) - - enabled := true - if v := n.Enabled; v != nil && !*v { - enabled = false - } - block["enabled"] = enabled + for _, n := range *input { + block := make(map[string]interface{}) - operator := "" - if v := n.Operator; v != "" { - operator = string(v) - } - block["operator"] = operator + block["enabled"] = n.Enabled - threshold := 0 - if v := n.Threshold; v != nil { - t, _ := v.Float64() - threshold = int(t) - } - block["threshold"] = threshold + operator := "" + if v := n.Operator; v != "" { + operator = string(v) + } + block["operator"] = operator - thresholdType := string(consumption.ThresholdTypeActual) - if v := n.ThresholdType; v != consumption.ThresholdTypeActual { - t := v - thresholdType = string(t) - } - block["threshold_type"] = thresholdType + block["threshold"] = n.Threshold - var emails []interface{} - if v := n.ContactEmails; v != nil { - emails = utils.FlattenStringSlice(v) - } - block["contact_emails"] = emails + thresholdType := string(budgets.ThresholdTypeActual) + if v := n.ThresholdType; v != nil { + thresholdType = string(*v) + } + block["threshold_type"] = thresholdType - if scope != "management_group_id" { - var roles []interface{} - if v := n.ContactRoles; v != nil { - roles = utils.FlattenStringSlice(v) - } - block["contact_roles"] = roles + var emails []interface{} + if v := n.ContactEmails; v != nil { + emails = utils.FlattenStringSlice(&v) + } + block["contact_emails"] = emails - var groups []interface{} - if v := n.ContactGroups; v != nil { - groups = utils.FlattenStringSlice(v) - } - block["contact_groups"] = groups + if scope != "management_group_id" { + var roles []interface{} + if v := n.ContactRoles; v != nil { + roles = utils.FlattenStringSlice(v) } + block["contact_roles"] = roles - notifications = append(notifications, block) + var groups []interface{} + if v := n.ContactGroups; v != nil { + groups = utils.FlattenStringSlice(v) + } + block["contact_groups"] = groups } + + notifications = append(notifications, block) } return notifications } -func expandConsumptionBudgetComparisonExpression(input interface{}) *consumption.BudgetComparisonExpression { +func expandConsumptionBudgetComparisonExpression(input interface{}) *budgets.BudgetComparisonExpression { if input == nil { return nil } v := input.(map[string]interface{}) - return &consumption.BudgetComparisonExpression{ - Name: utils.String(v["name"].(string)), - Operator: utils.String(v["operator"].(string)), - Values: utils.ExpandStringSlice(v["values"].([]interface{})), + return &budgets.BudgetComparisonExpression{ + Name: v["name"].(string), + Operator: budgets.BudgetOperatorType(v["operator"].(string)), + Values: *utils.ExpandStringSlice(v["values"].([]interface{})), } } -func flattenConsumptionBudgetComparisonExpression(input *consumption.BudgetComparisonExpression) *map[string]interface{} { +func flattenConsumptionBudgetComparisonExpression(input *budgets.BudgetComparisonExpression) *map[string]interface{} { consumptionBudgetComparisonExpression := make(map[string]interface{}) consumptionBudgetComparisonExpression["name"] = input.Name consumptionBudgetComparisonExpression["operator"] = input.Operator - consumptionBudgetComparisonExpression["values"] = utils.FlattenStringSlice(input.Values) + consumptionBudgetComparisonExpression["values"] = utils.FlattenStringSlice(&input.Values) return &consumptionBudgetComparisonExpression } -func expandConsumptionBudgetFilterDimensions(input []interface{}) []consumption.BudgetFilterProperties { +func expandConsumptionBudgetFilterDimensions(input []interface{}) []budgets.BudgetFilterProperties { if len(input) == 0 { return nil } - dimensions := make([]consumption.BudgetFilterProperties, 0) + dimensions := make([]budgets.BudgetFilterProperties, 0) for _, v := range input { - dimension := consumption.BudgetFilterProperties{ + dimension := budgets.BudgetFilterProperties{ Dimensions: expandConsumptionBudgetComparisonExpression(v), } dimensions = append(dimensions, dimension) @@ -700,15 +665,15 @@ func expandConsumptionBudgetFilterDimensions(input []interface{}) []consumption. return dimensions } -func expandConsumptionBudgetFilterTag(input []interface{}) []consumption.BudgetFilterProperties { +func expandConsumptionBudgetFilterTag(input []interface{}) []budgets.BudgetFilterProperties { if len(input) == 0 { return nil } - tags := make([]consumption.BudgetFilterProperties, 0) + tags := make([]budgets.BudgetFilterProperties, 0) for _, v := range input { - tag := consumption.BudgetFilterProperties{ + tag := budgets.BudgetFilterProperties{ Tags: expandConsumptionBudgetComparisonExpression(v), } @@ -718,13 +683,13 @@ func expandConsumptionBudgetFilterTag(input []interface{}) []consumption.BudgetF return tags } -func expandConsumptionBudgetFilter(i []interface{}) *consumption.BudgetFilter { +func expandConsumptionBudgetFilter(i []interface{}) *budgets.BudgetFilter { if len(i) == 0 || i[0] == nil { return nil } input := i[0].(map[string]interface{}) - filter := consumption.BudgetFilter{} + filter := budgets.BudgetFilter{} notBlock := input["not"].([]interface{}) if len(notBlock) != 0 && notBlock[0] != nil { @@ -768,7 +733,7 @@ func expandConsumptionBudgetFilter(i []interface{}) *consumption.BudgetFilter { return &filter } -func flattenConsumptionBudgetFilter(input *consumption.BudgetFilter) []interface{} { +func flattenConsumptionBudgetFilter(input *budgets.BudgetFilter) []interface{} { filter := make([]interface{}, 0) if input == nil { diff --git a/internal/services/consumption/consumption_budget_management_group_resource.go b/internal/services/consumption/consumption_budget_management_group_resource.go index 41a8f9e3f0ae..f9a150d4dfc5 100644 --- a/internal/services/consumption/consumption_budget_management_group_resource.go +++ b/internal/services/consumption/consumption_budget_management_group_resource.go @@ -1,9 +1,8 @@ package consumption import ( - "github.com/Azure/azure-sdk-for-go/services/consumption/mgmt/2019-10-01/consumption" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" validateManagementGroup "github.com/hashicorp/terraform-provider-azurerm/internal/services/managementgroup/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -58,10 +57,10 @@ func (r ManagementGroupConsumptionBudget) Arguments() map[string]*pluginsdk.Sche "threshold_type": { Type: pluginsdk.TypeString, Optional: true, - Default: string(consumption.ThresholdTypeActual), + Default: string(budgets.ThresholdTypeActual), ForceNew: true, // TODO: remove this when the above issue is fixed ValidateFunc: validation.StringInSlice([]string{ - string(consumption.ThresholdTypeActual), + string(budgets.ThresholdTypeActual), "Forecasted", }, false), }, @@ -69,9 +68,9 @@ func (r ManagementGroupConsumptionBudget) Arguments() map[string]*pluginsdk.Sche Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(consumption.OperatorTypeEqualTo), - string(consumption.OperatorTypeGreaterThan), - string(consumption.OperatorTypeGreaterThanOrEqualTo), + string(budgets.OperatorTypeEqualTo), + string(budgets.OperatorTypeGreaterThan), + string(budgets.OperatorTypeGreaterThanOrEqualTo), }, false), }, @@ -104,7 +103,7 @@ func (r ManagementGroupConsumptionBudget) ResourceType() string { } func (r ManagementGroupConsumptionBudget) IDValidationFunc() pluginsdk.SchemaValidateFunc { - return validate.ConsumptionBudgetManagementGroupID + return budgets.ValidateScopedBudgetID } func (r ManagementGroupConsumptionBudget) Create() sdk.ResourceFunc { @@ -124,5 +123,5 @@ func (r ManagementGroupConsumptionBudget) Update() sdk.ResourceFunc { } func (r ManagementGroupConsumptionBudget) CustomImporter() sdk.ResourceRunFunc { - return r.base.importerFunc("management_group") + return r.base.importerFunc() } diff --git a/internal/services/consumption/consumption_budget_management_group_resource_test.go b/internal/services/consumption/consumption_budget_management_group_resource_test.go index cd6b1e513d96..db677cc469f2 100644 --- a/internal/services/consumption/consumption_budget_management_group_resource_test.go +++ b/internal/services/consumption/consumption_budget_management_group_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -109,17 +109,17 @@ func TestAccConsumptionBudgetManagementGroup_completeUpdate(t *testing.T) { } func (ConsumptionBudgetManagementGroupResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ConsumptionBudgetID(state.ID) + id, err := budgets.ParseScopedBudgetID(state.ID) if err != nil { return nil, err } - resp, err := clients.Consumption.BudgetsClient.Get(ctx, id.Scope, id.Name) + resp, err := clients.Consumption.BudgetsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.BudgetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (ConsumptionBudgetManagementGroupResource) basic(data acceptance.TestData) string { diff --git a/internal/services/consumption/consumption_budget_resource_group_data_source.go b/internal/services/consumption/consumption_budget_resource_group_data_source.go index a72de7712429..b01c5f7f3b11 100644 --- a/internal/services/consumption/consumption_budget_resource_group_data_source.go +++ b/internal/services/consumption/consumption_budget_resource_group_data_source.go @@ -4,12 +4,12 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmConsumptionBudgetResourceGroupDataSource() *pluginsdk.Resource { @@ -223,28 +223,28 @@ func resourceArmConsumptionBudgetResourceGroupDataSourceRead(d *pluginsdk.Resour ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewConsumptionBudgetId(d.Get("resource_group_id").(string), d.Get("name").(string)) + id := budgets.NewScopedBudgetID(d.Get("resource_group_id").(string), d.Get("name").(string)) d.SetId(id.ID()) - resp, err := client.Get(ctx, id.Scope, id.Name) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } return fmt.Errorf("making read request on %s: %+v", id, err) } - d.Set("name", resp.Name) - if resp.Amount != nil { - amount, _ := resp.Amount.Float64() - d.Set("amount", amount) + d.Set("name", id.BudgetName) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("amount", props.Amount) + d.Set("time_grain", string(props.TimeGrain)) + d.Set("time_period", flattenConsumptionBudgetTimePeriod(&props.TimePeriod)) + d.Set("notification", flattenConsumptionBudgetNotifications(props.Notifications, id.Scope)) + d.Set("filter", flattenConsumptionBudgetFilter(props.Filter)) + } } - d.Set("resource_group_id", id.Scope) - d.Set("time_grain", string(resp.TimeGrain)) - d.Set("time_period", flattenConsumptionBudgetTimePeriod(resp.TimePeriod)) - d.Set("notification", flattenConsumptionBudgetNotifications(resp.Notifications, id.Scope)) - d.Set("filter", flattenConsumptionBudgetFilter(resp.Filter)) return nil } diff --git a/internal/services/consumption/consumption_budget_resource_group_resource.go b/internal/services/consumption/consumption_budget_resource_group_resource.go index af2cf8039740..6f22756a783d 100644 --- a/internal/services/consumption/consumption_budget_resource_group_resource.go +++ b/internal/services/consumption/consumption_budget_resource_group_resource.go @@ -1,8 +1,8 @@ package consumption import ( + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" validateResourceGroup "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -48,7 +48,7 @@ func (r ResourceGroupConsumptionBudget) ResourceType() string { } func (r ResourceGroupConsumptionBudget) IDValidationFunc() pluginsdk.SchemaValidateFunc { - return validate.ConsumptionBudgetResourceGroupID + return budgets.ValidateScopedBudgetID } func (r ResourceGroupConsumptionBudget) Create() sdk.ResourceFunc { @@ -68,5 +68,5 @@ func (r ResourceGroupConsumptionBudget) Update() sdk.ResourceFunc { } func (r ResourceGroupConsumptionBudget) CustomImporter() sdk.ResourceRunFunc { - return r.base.importerFunc("resource_group") + return r.base.importerFunc() } diff --git a/internal/services/consumption/consumption_budget_resource_group_resource_test.go b/internal/services/consumption/consumption_budget_resource_group_resource_test.go index 95061df52b20..ce902879d9e9 100644 --- a/internal/services/consumption/consumption_budget_resource_group_resource_test.go +++ b/internal/services/consumption/consumption_budget_resource_group_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -121,17 +121,17 @@ func TestAccConsumptionBudgetResourceGroup_disappears(t *testing.T) { } func (ConsumptionBudgetResourceGroupResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ConsumptionBudgetID(state.ID) + id, err := budgets.ParseScopedBudgetID(state.ID) if err != nil { return nil, err } - resp, err := clients.Consumption.BudgetsClient.Get(ctx, id.Scope, id.Name) + resp, err := clients.Consumption.BudgetsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.BudgetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (ConsumptionBudgetResourceGroupResource) basic(data acceptance.TestData) string { @@ -458,12 +458,12 @@ resource "azurerm_consumption_budget_resource_group" "test" { } func (t ConsumptionBudgetResourceGroupResource) Destroy(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ConsumptionBudgetID(state.ID) + id, err := budgets.ParseScopedBudgetID(state.ID) if err != nil { return nil, err } - if _, err = client.Consumption.BudgetsClient.Delete(ctx, id.Scope, id.Name); err != nil { + if _, err = client.Consumption.BudgetsClient.Delete(ctx, *id); err != nil { return nil, fmt.Errorf("deleting %s: %+v", *id, err) } diff --git a/internal/services/consumption/consumption_budget_subscription_data_source.go b/internal/services/consumption/consumption_budget_subscription_data_source.go index 4c4424caa9b8..0ff13b8d753c 100644 --- a/internal/services/consumption/consumption_budget_subscription_data_source.go +++ b/internal/services/consumption/consumption_budget_subscription_data_source.go @@ -4,12 +4,12 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmConsumptionBudgetSubscriptionDataSource() *pluginsdk.Resource { @@ -223,27 +223,27 @@ func resourceArmConsumptionBudgetSubscriptionDataSourceRead(d *pluginsdk.Resourc ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewConsumptionBudgetId(d.Get("subscription_id").(string), d.Get("name").(string)) + id := budgets.NewScopedBudgetID(d.Get("subscription_id").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.Scope, id.Name) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } return fmt.Errorf("retrieving %s: %+v", id, err) } d.SetId(id.ID()) - d.Set("name", id.Name) - d.Set("subscription_id", id.Scope) - if resp.Amount != nil { - amount, _ := resp.Amount.Float64() - d.Set("amount", amount) + d.Set("name", id.BudgetName) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("amount", props.Amount) + d.Set("time_grain", string(props.TimeGrain)) + d.Set("time_period", flattenConsumptionBudgetTimePeriod(&props.TimePeriod)) + d.Set("notification", flattenConsumptionBudgetNotifications(props.Notifications, id.Scope)) + d.Set("filter", flattenConsumptionBudgetFilter(props.Filter)) + } } - d.Set("time_grain", string(resp.TimeGrain)) - d.Set("time_period", flattenConsumptionBudgetTimePeriod(resp.TimePeriod)) - d.Set("notification", flattenConsumptionBudgetNotifications(resp.Notifications, id.Scope)) - d.Set("filter", flattenConsumptionBudgetFilter(resp.Filter)) return nil } diff --git a/internal/services/consumption/consumption_budget_subscription_resource.go b/internal/services/consumption/consumption_budget_subscription_resource.go index 3cd80093a024..ea8b696c6f87 100644 --- a/internal/services/consumption/consumption_budget_subscription_resource.go +++ b/internal/services/consumption/consumption_budget_subscription_resource.go @@ -2,9 +2,9 @@ package consumption import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/migration" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) @@ -50,7 +50,7 @@ func (r SubscriptionConsumptionBudget) ResourceType() string { } func (r SubscriptionConsumptionBudget) IDValidationFunc() pluginsdk.SchemaValidateFunc { - return validate.ConsumptionBudgetSubscriptionID + return budgets.ValidateScopedBudgetID } func (r SubscriptionConsumptionBudget) Create() sdk.ResourceFunc { @@ -70,7 +70,7 @@ func (r SubscriptionConsumptionBudget) Update() sdk.ResourceFunc { } func (r SubscriptionConsumptionBudget) CustomImporter() sdk.ResourceRunFunc { - return r.base.importerFunc("subscription") + return r.base.importerFunc() } func (r SubscriptionConsumptionBudget) StateUpgraders() sdk.StateUpgradeData { diff --git a/internal/services/consumption/consumption_budget_subscription_resource_test.go b/internal/services/consumption/consumption_budget_subscription_resource_test.go index 7617f005ef1f..2dcb87b63766 100644 --- a/internal/services/consumption/consumption_budget_subscription_resource_test.go +++ b/internal/services/consumption/consumption_budget_subscription_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -116,17 +116,17 @@ func TestAccConsumptionBudgetSubscription_completeUpdate(t *testing.T) { } func (ConsumptionBudgetSubscriptionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ConsumptionBudgetID(state.ID) + id, err := budgets.ParseScopedBudgetID(state.ID) if err != nil { return nil, err } - resp, err := clients.Consumption.BudgetsClient.Get(ctx, id.Scope, id.Name) + resp, err := clients.Consumption.BudgetsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.BudgetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (ConsumptionBudgetSubscriptionResource) basic(data acceptance.TestData) string { diff --git a/internal/services/consumption/migration/consumption_budget_subscription.go b/internal/services/consumption/migration/consumption_budget_subscription.go index c663acd5ea2c..93faacfb0b9d 100644 --- a/internal/services/consumption/migration/consumption_budget_subscription.go +++ b/internal/services/consumption/migration/consumption_budget_subscription.go @@ -6,7 +6,7 @@ import ( "log" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" + "github.com/hashicorp/go-azure-sdk/resource-manager/consumption/2019-10-01/budgets" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -241,13 +241,13 @@ func (SubscriptionConsumptionBudgetV1ToV2) UpgradeFunc() pluginsdk.StateUpgrader // since the `subscription_id` field gets incorrectly mutated in the V0 -> V1 upgrade // we have to parse it from the ID instead to correct this idRaw := rawState["id"].(string) - id, err := parse.ConsumptionBudgetSubscriptionID(idRaw) + id, err := budgets.ParseScopedBudgetID(idRaw) if err != nil { return nil, fmt.Errorf("parsing %q: %+v", idRaw, err) } oldSubscriptionId := rawState["subscription_id"].(commonids.SubscriptionId) - newSubscriptionId := commonids.NewSubscriptionID(id.SubscriptionId).ID() + newSubscriptionId := commonids.NewSubscriptionID(id.Scope).ID() log.Printf("[DEBUG] Updating subscription_id from %q to %q", oldSubscriptionId, newSubscriptionId) rawState["subscription_id"] = newSubscriptionId diff --git a/internal/services/consumption/parse/consumption_budget.go b/internal/services/consumption/parse/consumption_budget.go deleted file mode 100644 index baf347fbb41c..000000000000 --- a/internal/services/consumption/parse/consumption_budget.go +++ /dev/null @@ -1,59 +0,0 @@ -package parse - -import ( - "fmt" - "regexp" - "strings" -) - -type ConsumptionBudgetId struct { - Name string - Scope string -} - -func (id ConsumptionBudgetId) String() string { - segments := []string{ - fmt.Sprintf("Consumption Budget Name %q", id.Name), - fmt.Sprintf("Scope %q", id.Scope), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Consumption Budget ID", segmentsStr) -} - -func (id ConsumptionBudgetId) ID() string { - fmtString := "%s/providers/Microsoft.Consumption/budgets/%s" - return fmt.Sprintf(fmtString, id.Scope, id.Name) -} - -func NewConsumptionBudgetId(scope, name string) ConsumptionBudgetId { - return ConsumptionBudgetId{ - Name: name, - Scope: scope, - } -} - -func ConsumptionBudgetID(input string) (*ConsumptionBudgetId, error) { - // in general, the id of a budget should be: - // {scope}/providers/Microsoft.Consumption/budgets/{name} - regex := regexp.MustCompile(`/providers/Microsoft\.Consumption/budgets/`) - if !regex.MatchString(input) { - return nil, fmt.Errorf("unable to parse Consumption Budget ID %q", input) - } - - segments := regex.Split(input, -1) - - if len(segments) != 2 { - return nil, fmt.Errorf("unable to parse Consumption Budget ID %q: Expected 2 segments after split", input) - } - - scope := segments[0] - name := segments[1] - if name == "" { - return nil, fmt.Errorf("unable to parse Consumption Budget ID %q: budget name is empty", input) - } - - return &ConsumptionBudgetId{ - Name: name, - Scope: scope, - }, nil -} diff --git a/internal/services/consumption/parse/consumption_budget_management_group.go b/internal/services/consumption/parse/consumption_budget_management_group.go deleted file mode 100644 index aec534efaba8..000000000000 --- a/internal/services/consumption/parse/consumption_budget_management_group.go +++ /dev/null @@ -1,58 +0,0 @@ -package parse - -import ( - "fmt" - "strings" - - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" -) - -type ConsumptionBudgetManagementGroupId struct { - ManagementGroupName string - BudgetName string -} - -func NewConsumptionBudgetManagementGroupID(managementGroupName, budgetName string) ConsumptionBudgetManagementGroupId { - return ConsumptionBudgetManagementGroupId{ - ManagementGroupName: managementGroupName, - BudgetName: budgetName, - } -} - -func (id ConsumptionBudgetManagementGroupId) String() string { - segments := []string{ - fmt.Sprintf("Budget Name %q", id.BudgetName), - fmt.Sprintf("Management Group Name %q", id.ManagementGroupName), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Consumption Budget Management Group", segmentsStr) -} - -func (id ConsumptionBudgetManagementGroupId) ID() string { - fmtString := "/providers/Microsoft.Management/managementGroups/%s/providers/Microsoft.Consumption/budgets/%s" - return fmt.Sprintf(fmtString, id.ManagementGroupName, id.BudgetName) -} - -// ConsumptionBudgetManagementGroupID parses a ConsumptionBudgetManagementGroup ID into an ConsumptionBudgetManagementGroupId struct -func ConsumptionBudgetManagementGroupID(input string) (*ConsumptionBudgetManagementGroupId, error) { - id, err := azure.ParseAzureResourceIDWithoutSubscription(input) - if err != nil { - return nil, err - } - - resourceId := ConsumptionBudgetManagementGroupId{} - - if resourceId.ManagementGroupName, err = id.PopSegment("managementGroups"); err != nil { - return nil, err - } - - if resourceId.BudgetName, err = id.PopSegment("budgets"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/consumption/parse/consumption_budget_management_group_test.go b/internal/services/consumption/parse/consumption_budget_management_group_test.go deleted file mode 100644 index bb8f8db99d64..000000000000 --- a/internal/services/consumption/parse/consumption_budget_management_group_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package parse - -import ( - "testing" - - "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" -) - -var _ resourceid.Formatter = ConsumptionBudgetManagementGroupId{} - -func TestConsumptionBudgetManagementGroupIDFormatter(t *testing.T) { - actual := NewConsumptionBudgetManagementGroupID("12345678-1234-9876-4563-123456789012", "budget1").ID() - expected := "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestConsumptionBudgetManagementGroupID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ConsumptionBudgetManagementGroupId - }{ - { - // empty - Input: "", - Error: true, - }, - - { - // missing ManagementGroupName - Input: "/providers/Microsoft.Management/", - Error: true, - }, - - { - // missing value for ManagementGroupName - Input: "/providers/Microsoft.Management/managementGroups/", - Error: true, - }, - - { - // missing BudgetName - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/", - Error: true, - }, - - { - // missing value for BudgetName - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - - { - // valid - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetManagementGroupId{ - ManagementGroupName: "12345678-1234-9876-4563-123456789012", - BudgetName: "budget1", - }, - }, - - { - // upper-cased - Input: "/PROVIDERS/MICROSOFT.MANAGEMENT/MANAGEMENTGROUPS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ConsumptionBudgetManagementGroupID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.ManagementGroupName != v.Expected.ManagementGroupName { - t.Fatalf("Expected %q but got %q for ManagementGroupName", v.Expected.ManagementGroupName, actual.ManagementGroupName) - } - if actual.BudgetName != v.Expected.BudgetName { - t.Fatalf("Expected %q but got %q for BudgetName", v.Expected.BudgetName, actual.BudgetName) - } - } -} diff --git a/internal/services/consumption/parse/consumption_budget_resource_group.go b/internal/services/consumption/parse/consumption_budget_resource_group.go deleted file mode 100644 index df388b794ce4..000000000000 --- a/internal/services/consumption/parse/consumption_budget_resource_group.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ConsumptionBudgetResourceGroupId struct { - SubscriptionId string - ResourceGroup string - BudgetName string -} - -func NewConsumptionBudgetResourceGroupID(subscriptionId, resourceGroup, budgetName string) ConsumptionBudgetResourceGroupId { - return ConsumptionBudgetResourceGroupId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - BudgetName: budgetName, - } -} - -func (id ConsumptionBudgetResourceGroupId) String() string { - segments := []string{ - fmt.Sprintf("Budget Name %q", id.BudgetName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Consumption Budget Resource Group", segmentsStr) -} - -func (id ConsumptionBudgetResourceGroupId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Consumption/budgets/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.BudgetName) -} - -// ConsumptionBudgetResourceGroupID parses a ConsumptionBudgetResourceGroup ID into an ConsumptionBudgetResourceGroupId struct -func ConsumptionBudgetResourceGroupID(input string) (*ConsumptionBudgetResourceGroupId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ConsumptionBudgetResourceGroupId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.BudgetName, err = id.PopSegment("budgets"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/consumption/parse/consumption_budget_resource_group_test.go b/internal/services/consumption/parse/consumption_budget_resource_group_test.go deleted file mode 100644 index ede1b168497e..000000000000 --- a/internal/services/consumption/parse/consumption_budget_resource_group_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ConsumptionBudgetResourceGroupId{} - -func TestConsumptionBudgetResourceGroupIDFormatter(t *testing.T) { - actual := NewConsumptionBudgetResourceGroupID("12345678-1234-9876-4563-123456789012", "resGroup1", "budget1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/budget1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestConsumptionBudgetResourceGroupID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ConsumptionBudgetResourceGroupId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/", - Error: true, - }, - - { - // missing value for BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetResourceGroupId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - BudgetName: "budget1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ConsumptionBudgetResourceGroupID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.BudgetName != v.Expected.BudgetName { - t.Fatalf("Expected %q but got %q for BudgetName", v.Expected.BudgetName, actual.BudgetName) - } - } -} diff --git a/internal/services/consumption/parse/consumption_budget_subscription.go b/internal/services/consumption/parse/consumption_budget_subscription.go deleted file mode 100644 index db1d04240515..000000000000 --- a/internal/services/consumption/parse/consumption_budget_subscription.go +++ /dev/null @@ -1,61 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ConsumptionBudgetSubscriptionId struct { - SubscriptionId string - BudgetName string -} - -func NewConsumptionBudgetSubscriptionID(subscriptionId, budgetName string) ConsumptionBudgetSubscriptionId { - return ConsumptionBudgetSubscriptionId{ - SubscriptionId: subscriptionId, - BudgetName: budgetName, - } -} - -func (id ConsumptionBudgetSubscriptionId) String() string { - segments := []string{ - fmt.Sprintf("Budget Name %q", id.BudgetName), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Consumption Budget Subscription", segmentsStr) -} - -func (id ConsumptionBudgetSubscriptionId) ID() string { - fmtString := "/subscriptions/%s/providers/Microsoft.Consumption/budgets/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.BudgetName) -} - -// ConsumptionBudgetSubscriptionID parses a ConsumptionBudgetSubscription ID into an ConsumptionBudgetSubscriptionId struct -func ConsumptionBudgetSubscriptionID(input string) (*ConsumptionBudgetSubscriptionId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ConsumptionBudgetSubscriptionId{ - SubscriptionId: id.SubscriptionID, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.BudgetName, err = id.PopSegment("budgets"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/consumption/parse/consumption_budget_subscription_test.go b/internal/services/consumption/parse/consumption_budget_subscription_test.go deleted file mode 100644 index 117a25d64148..000000000000 --- a/internal/services/consumption/parse/consumption_budget_subscription_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ConsumptionBudgetSubscriptionId{} - -func TestConsumptionBudgetSubscriptionIDFormatter(t *testing.T) { - actual := NewConsumptionBudgetSubscriptionID("12345678-1234-9876-4563-123456789012", "budget1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestConsumptionBudgetSubscriptionID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ConsumptionBudgetSubscriptionId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/", - Error: true, - }, - - { - // missing value for BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetSubscriptionId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - BudgetName: "budget1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ConsumptionBudgetSubscriptionID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.BudgetName != v.Expected.BudgetName { - t.Fatalf("Expected %q but got %q for BudgetName", v.Expected.BudgetName, actual.BudgetName) - } - } -} diff --git a/internal/services/consumption/parse/consumption_budget_test.go b/internal/services/consumption/parse/consumption_budget_test.go deleted file mode 100644 index a420cd80f457..000000000000 --- a/internal/services/consumption/parse/consumption_budget_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package parse - -import ( - "testing" -) - -func TestCostManagementExportID(t *testing.T) { - testData := []struct { - Name string - Input string - Error bool - Expected *ConsumptionBudgetId - }{ - { - Name: "empty", - Input: "", - Error: true, - }, - { - Name: "resource group consumption budget", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetId{ - Name: "budget1", - Scope: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo", - }, - }, - { - Name: "resource group consumption budget but no name", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - { - Name: "subscription consumption budget", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetId{ - Name: "budget1", - Scope: "/subscriptions/00000000-0000-0000-0000-000000000000", - }, - }, - { - Name: "subscription consumption budget but no name", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - { - Name: "management group consumption budget", - Input: "/providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000/providers/Microsoft.Consumption/budgets/budget1", - Expected: &ConsumptionBudgetId{ - Name: "budget1", - Scope: "/providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000", - }, - }, - { - Name: "management group consumption budget but no name", - Input: "/providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000/providers/Microsoft.Consumption/budgets/", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Name) - - actual, err := ConsumptionBudgetID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expected a value but got an error: %+v", err) - } - - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q", v.Expected.Name, actual.Name) - } - - if v.Expected.Scope != actual.Scope { - t.Fatalf("Expected %+v but got %+v", v.Expected.Scope, actual.Scope) - } - } -} diff --git a/internal/services/consumption/resourceids.go b/internal/services/consumption/resourceids.go deleted file mode 100644 index 7628c18ee553..000000000000 --- a/internal/services/consumption/resourceids.go +++ /dev/null @@ -1,4 +0,0 @@ -package consumption - -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ConsumptionBudgetResourceGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/budget1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ConsumptionBudgetSubscription -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1 diff --git a/internal/services/consumption/validate/consumption_budget_management_group_id.go b/internal/services/consumption/validate/consumption_budget_management_group_id.go deleted file mode 100644 index eccf397dd0fe..000000000000 --- a/internal/services/consumption/validate/consumption_budget_management_group_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" -) - -func ConsumptionBudgetManagementGroupID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ConsumptionBudgetManagementGroupID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/consumption/validate/consumption_budget_management_group_id_test.go b/internal/services/consumption/validate/consumption_budget_management_group_id_test.go deleted file mode 100644 index cabe6fd1e8bb..000000000000 --- a/internal/services/consumption/validate/consumption_budget_management_group_id_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestConsumptionBudgetManagementGroupID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - { - // empty - Input: "", - Valid: false, - }, - - { - // missing ManagementGroupName - Input: "/providers/Microsoft.Management/", - Valid: false, - }, - - { - // missing value for ManagementGroupName - Input: "/providers/Microsoft.Management/managementGroups/", - Valid: false, - }, - - { - // missing BudgetName - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/", - Valid: false, - }, - - { - // missing value for BudgetName - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/", - Valid: false, - }, - - { - // valid - Input: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1", - Valid: true, - }, - - { - // upper-cased - Input: "/PROVIDERS/MICROSOFT.MANAGEMENT/MANAGEMENTGROUPS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ConsumptionBudgetManagementGroupID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/consumption/validate/consumption_budget_resource_group_id.go b/internal/services/consumption/validate/consumption_budget_resource_group_id.go deleted file mode 100644 index 1e5abf14fb46..000000000000 --- a/internal/services/consumption/validate/consumption_budget_resource_group_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" -) - -func ConsumptionBudgetResourceGroupID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ConsumptionBudgetResourceGroupID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/consumption/validate/consumption_budget_resource_group_id_test.go b/internal/services/consumption/validate/consumption_budget_resource_group_id_test.go deleted file mode 100644 index 9595b0fdc466..000000000000 --- a/internal/services/consumption/validate/consumption_budget_resource_group_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestConsumptionBudgetResourceGroupID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/", - Valid: false, - }, - - { - // missing value for BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Consumption/budgets/budget1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ConsumptionBudgetResourceGroupID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/consumption/validate/consumption_budget_subscription_id.go b/internal/services/consumption/validate/consumption_budget_subscription_id.go deleted file mode 100644 index 9998395d62d5..000000000000 --- a/internal/services/consumption/validate/consumption_budget_subscription_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" -) - -func ConsumptionBudgetSubscriptionID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ConsumptionBudgetSubscriptionID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/consumption/validate/consumption_budget_subscription_id_test.go b/internal/services/consumption/validate/consumption_budget_subscription_id_test.go deleted file mode 100644 index 94d51b2b635c..000000000000 --- a/internal/services/consumption/validate/consumption_budget_subscription_id_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestConsumptionBudgetSubscriptionID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/", - Valid: false, - }, - - { - // missing value for BudgetName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Consumption/budgets/budget1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.CONSUMPTION/BUDGETS/BUDGET1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ConsumptionBudgetSubscriptionID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/containers/client/client.go b/internal/services/containers/client/client.go index f9d963845a92..3a4b108f7bc3 100644 --- a/internal/services/containers/client/client.go +++ b/internal/services/containers/client/client.go @@ -2,8 +2,7 @@ package client import ( legacy "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-08-01/containerservice" - legacyacr "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2019-06-01-preview/containerregistry" - "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2021-08-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/Azure/azure-sdk-for-go/services/preview/containerservice/mgmt/2022-03-02-preview/containerservice" "github.com/Azure/go-autorest/autorest/azure" "github.com/hashicorp/go-azure-sdk/resource-manager/containerinstance/2021-03-01/containerinstance" @@ -22,7 +21,8 @@ type Client struct { WebhooksClient *containerregistry.WebhooksClient TokensClient *containerregistry.TokensClient ScopeMapsClient *containerregistry.ScopeMapsClient - TasksClient *legacyacr.TasksClient + TasksClient *containerregistry.TasksClient + RunsClient *containerregistry.RunsClient ConnectedRegistriesClient *containerregistry.ConnectedRegistriesClient Environment azure.Environment @@ -47,9 +47,12 @@ func NewClient(o *common.ClientOptions) *Client { scopeMapsClient := containerregistry.NewScopeMapsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&scopeMapsClient.Client, o.ResourceManagerAuthorizer) - tasksClient := legacyacr.NewTasksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + tasksClient := containerregistry.NewTasksClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&tasksClient.Client, o.ResourceManagerAuthorizer) + runsClient := containerregistry.NewRunsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&runsClient.Client, o.ResourceManagerAuthorizer) + containerInstanceClient := containerinstance.NewContainerInstanceClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&containerInstanceClient.Client, o.ResourceManagerAuthorizer) @@ -83,6 +86,7 @@ func NewClient(o *common.ClientOptions) *Client { TokensClient: &tokensClient, ScopeMapsClient: &scopeMapsClient, TasksClient: &tasksClient, + RunsClient: &runsClient, ConnectedRegistriesClient: &connectedRegistriesClient, } } diff --git a/internal/services/containers/container_connected_registry_resource.go b/internal/services/containers/container_connected_registry_resource.go index 5a3742c2afdd..3015ac92e7f5 100644 --- a/internal/services/containers/container_connected_registry_resource.go +++ b/internal/services/containers/container_connected_registry_resource.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2021-08-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/hashicorp/go-azure-helpers/lang/response" tfvalidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" diff --git a/internal/services/containers/container_group_data_source.go b/internal/services/containers/container_group_data_source.go index 4df21b832c1f..79b2373df6d6 100644 --- a/internal/services/containers/container_group_data_source.go +++ b/internal/services/containers/container_group_data_source.go @@ -76,8 +76,8 @@ func dataSourceContainerGroupRead(d *pluginsdk.ResourceData, meta interface{}) e return err } props := model.Properties - if address := props.IpAddress; address != nil { - d.Set("ip_address", address.Ip) + if address := props.IPAddress; address != nil { + d.Set("ip_address", address.IP) d.Set("fqdn", address.Fqdn) } } diff --git a/internal/services/containers/container_group_resource.go b/internal/services/containers/container_group_resource.go index e97f5dd8b2c6..8be496efa953 100644 --- a/internal/services/containers/container_group_resource.go +++ b/internal/services/containers/container_group_resource.go @@ -63,11 +63,11 @@ func resourceContainerGroup() *pluginsdk.Resource { "ip_address_type": { Type: pluginsdk.TypeString, Optional: true, - Default: string(containerinstance.ContainerGroupIpAddressTypePublic), + Default: string(containerinstance.ContainerGroupIPAddressTypePublic), ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(containerinstance.ContainerGroupIpAddressTypePublic), - string(containerinstance.ContainerGroupIpAddressTypePrivate), + string(containerinstance.ContainerGroupIPAddressTypePublic), + string(containerinstance.ContainerGroupIPAddressTypePrivate), "None", }, false), }, @@ -678,13 +678,13 @@ func resourceContainerGroupCreate(d *pluginsdk.ResourceData, meta interface{}) e } if IPAddressType != "None" { - containerGroup.Properties.IpAddress = &containerinstance.IpAddress{ + containerGroup.Properties.IPAddress = &containerinstance.IPAddress{ Ports: containerGroupPorts, - Type: containerinstance.ContainerGroupIpAddressType(IPAddressType), + Type: containerinstance.ContainerGroupIPAddressType(IPAddressType), } if dnsNameLabel := d.Get("dns_name_label").(string); dnsNameLabel != "" { - containerGroup.Properties.IpAddress.DnsNameLabel = &dnsNameLabel + containerGroup.Properties.IPAddress.DnsNameLabel = &dnsNameLabel } } @@ -800,9 +800,9 @@ func resourceContainerGroupRead(d *pluginsdk.ResourceData, meta interface{}) err return fmt.Errorf("setting `image_registry_credential`: %+v", err) } - if address := props.IpAddress; address != nil { + if address := props.IPAddress; address != nil { d.Set("ip_address_type", address.Type) - d.Set("ip_address", address.Ip) + d.Set("ip_address", address.IP) exposedPorts := make([]interface{}, len(address.Ports)) for i := range address.Ports { exposedPorts[i] = (address.Ports)[i] @@ -1475,11 +1475,11 @@ func expandContainerProbe(input interface{}) *containerinstance.ContainerProbe { scheme := x["scheme"].(string) httpGetScheme := containerinstance.Scheme(scheme) - probe.HttpGet = &containerinstance.ContainerHttpGet{ + probe.HTTPGet = &containerinstance.ContainerHTTPGet{ Path: pointer.FromString(path), Port: int64(port), Scheme: &httpGetScheme, - HttpHeaders: expandContainerProbeHttpHeaders(x["http_headers"].(map[string]interface{})), + HTTPHeaders: expandContainerProbeHttpHeaders(x["http_headers"].(map[string]interface{})), } } } @@ -1487,14 +1487,14 @@ func expandContainerProbe(input interface{}) *containerinstance.ContainerProbe { return &probe } -func expandContainerProbeHttpHeaders(input map[string]interface{}) *[]containerinstance.HttpHeader { +func expandContainerProbeHttpHeaders(input map[string]interface{}) *[]containerinstance.HTTPHeader { if len(input) == 0 { return nil } - headers := []containerinstance.HttpHeader{} + headers := []containerinstance.HTTPHeader{} for k, v := range input { - header := containerinstance.HttpHeader{ + header := containerinstance.HTTPHeader{ Name: pointer.FromString(k), Value: pointer.FromString(v.(string)), } @@ -1503,7 +1503,7 @@ func expandContainerProbeHttpHeaders(input map[string]interface{}) *[]containeri return &headers } -func flattenContainerProbeHttpHeaders(input *[]containerinstance.HttpHeader) map[string]interface{} { +func flattenContainerProbeHttpHeaders(input *[]containerinstance.HTTPHeader) map[string]interface{} { if input == nil { return nil } @@ -1815,14 +1815,14 @@ func flattenContainerProbes(input *containerinstance.ContainerProbe) []interface } httpGets := make([]interface{}, 0) - if get := input.HttpGet; get != nil { + if get := input.HTTPGet; get != nil { httpGet := make(map[string]interface{}) if v := get.Path; v != nil { httpGet["path"] = *v } httpGet["port"] = get.Port httpGet["scheme"] = get.Scheme - httpGet["http_headers"] = flattenContainerProbeHttpHeaders(get.HttpHeaders) + httpGet["http_headers"] = flattenContainerProbeHttpHeaders(get.HTTPHeaders) httpGets = append(httpGets, httpGet) } output["http_get"] = httpGets diff --git a/internal/services/containers/container_registry_agent_pool_resource.go b/internal/services/containers/container_registry_agent_pool_resource.go index a8d811ffbaf7..38db75cacf88 100644 --- a/internal/services/containers/container_registry_agent_pool_resource.go +++ b/internal/services/containers/container_registry_agent_pool_resource.go @@ -5,7 +5,7 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2021-08-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" diff --git a/internal/services/containers/container_registry_resource.go b/internal/services/containers/container_registry_resource.go index 32e03554666f..bbb5d7579614 100644 --- a/internal/services/containers/container_registry_resource.go +++ b/internal/services/containers/container_registry_resource.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2021-08-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" @@ -187,11 +187,16 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{} retentionPolicyRaw := d.Get("retention_policy").([]interface{}) retentionPolicy := expandRetentionPolicy(retentionPolicyRaw) + softDeletePolicyRaw := d.Get("soft_delete_policy").([]interface{}) + softDeletePolicy := expandSoftDeletePolicy(softDeletePolicyRaw) + trustPolicyRaw := d.Get("trust_policy").([]interface{}) trustPolicy := expandTrustPolicy(trustPolicyRaw) exportPolicy := expandExportPolicy(d.Get("export_policy_enabled").(bool)) + aadAuthAsArmPolicy := expandAadAuthAsArmPolicy(d.Get("azuread_authentication_as_arm_policy_enabled").(bool)) + encryptionRaw := d.Get("encryption").([]interface{}) encryption := expandEncryption(encryptionRaw) @@ -222,10 +227,12 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{} Encryption: encryption, NetworkRuleSet: networkRuleSet, Policies: &containerregistry.Policies{ - QuarantinePolicy: quarantinePolicy, - RetentionPolicy: retentionPolicy, - TrustPolicy: trustPolicy, - ExportPolicy: exportPolicy, + AzureADAuthenticationAsArmPolicy: aadAuthAsArmPolicy, + SoftDeletePolicy: softDeletePolicy, + QuarantinePolicy: quarantinePolicy, + RetentionPolicy: retentionPolicy, + TrustPolicy: trustPolicy, + ExportPolicy: exportPolicy, }, PublicNetworkAccess: publicNetworkAccess, ZoneRedundancy: zoneRedundancy, @@ -300,7 +307,9 @@ func resourceContainerRegistryUpdate(d *pluginsdk.ResourceData, meta interface{} } quarantinePolicy := expandQuarantinePolicy(d.Get("quarantine_policy_enabled").(bool)) + aadAuthAsArmPolicy := expandAadAuthAsArmPolicy(d.Get("azuread_authentication_as_arm_policy_enabled").(bool)) retentionPolicy := expandRetentionPolicy(d.Get("retention_policy").([]interface{})) + softDeletePolicy := expandSoftDeletePolicy(d.Get("soft_delete_policy").([]interface{})) trustPolicy := expandTrustPolicy(d.Get("trust_policy").([]interface{})) exportPolicy := expandExportPolicy(d.Get("export_policy_enabled").(bool)) @@ -322,10 +331,12 @@ func resourceContainerRegistryUpdate(d *pluginsdk.ResourceData, meta interface{} AdminUserEnabled: utils.Bool(adminUserEnabled), NetworkRuleSet: networkRuleSet, Policies: &containerregistry.Policies{ - QuarantinePolicy: quarantinePolicy, - RetentionPolicy: retentionPolicy, - TrustPolicy: trustPolicy, - ExportPolicy: exportPolicy, + AzureADAuthenticationAsArmPolicy: aadAuthAsArmPolicy, + SoftDeletePolicy: softDeletePolicy, + QuarantinePolicy: quarantinePolicy, + RetentionPolicy: retentionPolicy, + TrustPolicy: trustPolicy, + ExportPolicy: exportPolicy, }, PublicNetworkAccess: publicNetworkAccess, Encryption: encryption, @@ -607,7 +618,11 @@ func resourceContainerRegistryRead(d *pluginsdk.ResourceData, meta interface{}) if err := d.Set("trust_policy", flattenTrustPolicy(properties.Policies)); err != nil { return fmt.Errorf("setting `trust_policy`: %+v", err) } + if err := d.Set("soft_delete_policy", flattenSoftDeletePolicy(properties.Policies)); err != nil { + return fmt.Errorf("setting `soft_delete_policy`: %+v", err) + } d.Set("export_policy_enabled", flattenExportPolicy(properties.Policies)) + d.Set("azuread_authentication_as_arm_policy_enabled", flattenAadAuthAsArmPolicy(properties.Policies)) if err := d.Set("encryption", flattenEncryption(properties.Encryption)); err != nil { return fmt.Errorf("setting `encryption`: %+v", err) } @@ -709,21 +724,9 @@ func expandNetworkRuleSet(profiles []interface{}) *containerregistry.NetworkRule ipRules = append(ipRules, newIpRule) } - networkRuleConfigs := profile["virtual_network"].(*pluginsdk.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, + DefaultAction: containerregistry.DefaultAction(profile["default_action"].(string)), + IPRules: &ipRules, } return &networkRuleSet } @@ -758,6 +761,24 @@ func expandRetentionPolicy(p []interface{}) *containerregistry.RetentionPolicy { return &retentionPolicy } +func expandSoftDeletePolicy(p []interface{}) *containerregistry.SoftDeletePolicy { + policy := containerregistry.SoftDeletePolicy{ + Status: containerregistry.PolicyStatusDisabled, + } + + if len(p) > 0 { + v := p[0].(map[string]interface{}) + days := int32(v["retention_days"].(int)) + enabled := v["enabled"].(bool) + if enabled { + policy.Status = containerregistry.PolicyStatusEnabled + } + policy.RetentionDays = utils.Int32(days) + } + + return &policy +} + func expandTrustPolicy(p []interface{}) *containerregistry.TrustPolicy { trustPolicy := containerregistry.TrustPolicy{ Status: containerregistry.PolicyStatusDisabled, @@ -787,6 +808,18 @@ func expandExportPolicy(enabled bool) *containerregistry.ExportPolicy { return &exportPolicy } +func expandAadAuthAsArmPolicy(enabled bool) *containerregistry.AzureADAuthenticationAsArmPolicy { + policy := containerregistry.AzureADAuthenticationAsArmPolicy{ + Status: containerregistry.AzureADAuthenticationAsArmPolicyStatusDisabled, + } + + if enabled { + policy.Status = containerregistry.AzureADAuthenticationAsArmPolicyStatusEnabled + } + + return &policy +} + func expandReplicationsFromLocations(p []interface{}) []containerregistry.Replication { replications := make([]containerregistry.Replication, 0) for _, value := range p { @@ -930,20 +963,6 @@ func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []i 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} } @@ -968,6 +987,24 @@ func flattenRetentionPolicy(p *containerregistry.Policies) []interface{} { return []interface{}{retentionPolicy} } +func flattenSoftDeletePolicy(p *containerregistry.Policies) []interface{} { + if p == nil || p.SoftDeletePolicy == nil { + return nil + } + + r := *p.SoftDeletePolicy + days := 0 + if r.RetentionDays != nil { + days = int(*r.RetentionDays) + } + return []interface{}{ + map[string]interface{}{ + "retention_days": days, + "enabled": strings.EqualFold(string(r.Status), string(containerregistry.PolicyStatusEnabled)), + }, + } +} + func flattenTrustPolicy(p *containerregistry.Policies) []interface{} { if p == nil || p.TrustPolicy == nil { return nil @@ -988,6 +1025,14 @@ func flattenExportPolicy(p *containerregistry.Policies) bool { return p.ExportPolicy.Status == containerregistry.ExportPolicyStatusEnabled } +func flattenAadAuthAsArmPolicy(p *containerregistry.Policies) bool { + if p == nil || p.AzureADAuthenticationAsArmPolicy == nil { + return false + } + + return p.AzureADAuthenticationAsArmPolicy.Status == containerregistry.AzureADAuthenticationAsArmPolicyStatusEnabled +} + func resourceContainerRegistrySchema() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { @@ -1133,28 +1178,6 @@ func resourceContainerRegistrySchema() map[string]*pluginsdk.Schema { }, }, }, - - "virtual_network": { - Type: pluginsdk.TypeSet, - Optional: true, - ConfigMode: pluginsdk.SchemaConfigModeAttr, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "action": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(containerregistry.ActionAllow), - }, false), - }, - "subnet_id": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: azure.ValidateResourceID, - }, - }, - }, - }, }, }, }, @@ -1204,6 +1227,34 @@ func resourceContainerRegistrySchema() map[string]*pluginsdk.Schema { }, }, + "soft_delete_policy": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "retention_days": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 7, + }, + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + + "azuread_authentication_as_arm_policy_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + "export_policy_enabled": { Type: pluginsdk.TypeBool, Optional: true, diff --git a/internal/services/containers/container_registry_resource_test.go b/internal/services/containers/container_registry_resource_test.go index 624e4d0c4569..6e6b80f0d4a5 100644 --- a/internal/services/containers/container_registry_resource_test.go +++ b/internal/services/containers/container_registry_resource_test.go @@ -338,41 +338,6 @@ func TestAccContainerRegistry_networkAccessProfile_update(t *testing.T) { ), }, data.ImportStep(), - { - Config: r.networkAccessProfile_vnet(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("network_rule_set.0.default_action").HasValue("Deny"), - check.That(data.ResourceName).Key("network_rule_set.0.virtual_network.#").HasValue("1"), - ), - }, - data.ImportStep(), - { - Config: r.networkAccessProfile_both(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("network_rule_set.0.default_action").HasValue("Deny"), - check.That(data.ResourceName).Key("network_rule_set.0.ip_rule.#").HasValue("1"), - ), - }, - data.ImportStep(), - }) -} - -func TestAccContainerRegistry_networkAccessProfileVnet(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_container_registry", "test") - r := ContainerRegistryResource{} - - data.ResourceTest(t, r, []acceptance.TestStep{ - { - Config: r.networkAccessProfile_vnet(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("network_rule_set.0.default_action").HasValue("Deny"), - check.That(data.ResourceName).Key("network_rule_set.0.virtual_network.#").HasValue("1"), - ), - }, - data.ImportStep(), }) } @@ -394,6 +359,7 @@ func TestAccContainerRegistry_policies(t *testing.T) { check.That(data.ResourceName).Key("export_policy_enabled").HasValue("false"), ), }, + data.ImportStep(), { Config: r.policies(data, 20), Check: acceptance.ComposeTestCheckFunc( @@ -407,6 +373,7 @@ func TestAccContainerRegistry_policies(t *testing.T) { check.That(data.ResourceName).Key("export_policy_enabled").HasValue("false"), ), }, + data.ImportStep(), { Config: r.policies_downgradeUpdate(data), Check: acceptance.ComposeTestCheckFunc( @@ -901,108 +868,6 @@ resource "azurerm_container_registry" "test" { `, data.RandomInteger, data.Locations.Primary, sku) } -func (ContainerRegistryResource) networkAccessProfile_vnet(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -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_prefixes = ["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" - - ip_rule { - action = "Allow" - ip_range = "8.8.8.8/32" - } - - virtual_network { - action = "Allow" - subnet_id = azurerm_subnet.test.id - } - } -} -`, data.RandomInteger, data.Locations.Primary) -} - -func (ContainerRegistryResource) networkAccessProfile_both(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -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_prefixes = ["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" - - ip_rule { - action = "Allow" - ip_range = "8.8.8.8/32" - } - - virtual_network { - action = "Allow" - subnet_id = azurerm_subnet.test.id - } - } -} -`, data.RandomInteger, data.Locations.Primary) -} - func (ContainerRegistryResource) policies(data acceptance.TestData, days int) string { return fmt.Sprintf(` provider "azurerm" { @@ -1028,18 +893,24 @@ resource "azurerm_container_registry" "test" { enabled = true } + soft_delete_policy { + retention_days = %d + enabled = true + } + trust_policy { enabled = true } - export_policy_enabled = false - public_network_access_enabled = false + export_policy_enabled = false + azuread_authentication_as_arm_policy_enabled = false + public_network_access_enabled = false tags = { Environment = "Production" } } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, days) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, days, days) } func (ContainerRegistryResource) policies_downgradeUpdate(data acceptance.TestData) string { @@ -1063,6 +934,7 @@ resource "azurerm_container_registry" "test" { retention_policy {} trust_policy {} + soft_delete_policy {} tags = { Environment = "Production" diff --git a/internal/services/containers/container_registry_scope_map_resource.go b/internal/services/containers/container_registry_scope_map_resource.go index 62a2df365a16..5c633f0135ee 100644 --- a/internal/services/containers/container_registry_scope_map_resource.go +++ b/internal/services/containers/container_registry_scope_map_resource.go @@ -5,7 +5,7 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2021-08-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/containers/container_registry_task_resource.go b/internal/services/containers/container_registry_task_resource.go index f3038b6d6a9c..479113a820bd 100644 --- a/internal/services/containers/container_registry_task_resource.go +++ b/internal/services/containers/container_registry_task_resource.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - legacyacr "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2019-06-01-preview/containerregistry" + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" @@ -171,28 +171,28 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.Windows), - string(legacyacr.Linux), + string(containerregistry.OSWindows), + string(containerregistry.OSLinux), }, false), }, "architecture": { Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.Amd64), - string(legacyacr.Arm), - string(legacyacr.Arm64), - string(legacyacr.ThreeEightSix), - string(legacyacr.X86), + string(containerregistry.ArchitectureAmd64), + string(containerregistry.ArchitectureArm), + string(containerregistry.ArchitectureArm64), + string(containerregistry.ArchitectureThreeEightSix), + string(containerregistry.ArchitectureX86), }, false), }, "variant": { Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.V6), - string(legacyacr.V7), - string(legacyacr.V8), + string(containerregistry.VariantV6), + string(containerregistry.VariantV7), + string(containerregistry.VariantV8), }, false), }, }, @@ -370,8 +370,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.All), - string(legacyacr.Runtime), + string(containerregistry.BaseImageTriggerTypeAll), + string(containerregistry.BaseImageTriggerTypeRuntime), }, false), }, "enabled": { @@ -389,8 +389,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.UpdateTriggerPayloadTypeDefault), - string(legacyacr.UpdateTriggerPayloadTypeToken), + string(containerregistry.UpdateTriggerPayloadTypeDefault), + string(containerregistry.UpdateTriggerPayloadTypeToken), }, false), }, }, @@ -412,8 +412,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.Commit), - string(legacyacr.Pullrequest), + string(containerregistry.SourceTriggerEventCommit), + string(containerregistry.SourceTriggerEventPullrequest), }, false), }, }, @@ -421,8 +421,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.Github), - string(legacyacr.VisualStudioTeamService), + string(containerregistry.SourceControlTypeGithub), + string(containerregistry.SourceControlTypeVisualStudioTeamService), }, false), }, "repository_url": { @@ -444,8 +444,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.PAT), - string(legacyacr.OAuth), + string(containerregistry.TokenTypePAT), + string(containerregistry.TokenTypeOAuth), }, false), }, "token": { @@ -521,8 +521,8 @@ func (r ContainerRegistryTaskResource) Arguments() map[string]*pluginsdk.Schema Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(legacyacr.SourceRegistryLoginModeNone), - string(legacyacr.SourceRegistryLoginModeDefault), + string(containerregistry.SourceRegistryLoginModeNone), + string(containerregistry.SourceRegistryLoginModeDefault), }, false), }, }, @@ -687,9 +687,9 @@ func (r ContainerRegistryTaskResource) Create() sdk.ResourceFunc { return metadata.ResourceRequiresImport(r.ResourceType(), id) } - status := legacyacr.TaskStatusDisabled + status := containerregistry.TaskStatusDisabled if model.Enabled { - status = legacyacr.TaskStatusEnabled + status = containerregistry.TaskStatusEnabled } expandedIdentity, err := expandRegistryTaskIdentity(metadata.ResourceData.Get("identity").([]interface{})) @@ -697,8 +697,8 @@ func (r ContainerRegistryTaskResource) Create() sdk.ResourceFunc { return fmt.Errorf("expanding `identity`: %+v", err) } - params := legacyacr.Task{ - TaskProperties: &legacyacr.TaskProperties{ + params := containerregistry.Task{ + TaskProperties: &containerregistry.TaskProperties{ Platform: expandRegistryTaskPlatform(model.Platform), Step: expandRegistryTaskStep(model), Trigger: expandRegistryTaskTrigger(model), @@ -789,7 +789,7 @@ func (r ContainerRegistryTaskResource) Read() sdk.ResourceFunc { logTemplate = *props.LogTemplate } platform = flattenRegistryTaskPlatform(props.Platform) - enabled = props.Status == legacyacr.TaskStatusEnabled + enabled = props.Status == containerregistry.TaskStatusEnabled if props.Timeout != nil { timeoutInSec = int(*props.Timeout) } @@ -926,9 +926,9 @@ func (r ContainerRegistryTaskResource) Update() sdk.ResourceFunc { existing.TaskProperties.AgentPoolName = &model.AgentPoolName } if metadata.ResourceData.HasChange("enabled") { - status := legacyacr.TaskStatusDisabled + status := containerregistry.TaskStatusDisabled if model.Enabled { - status = legacyacr.TaskStatusEnabled + status = containerregistry.TaskStatusEnabled } existing.TaskProperties.Status = status } @@ -957,52 +957,52 @@ func (r ContainerRegistryTaskResource) Update() sdk.ResourceFunc { } } -func expandRegistryTaskTrigger(model ContainerRegistryTaskModel) *legacyacr.TriggerProperties { +func expandRegistryTaskTrigger(model ContainerRegistryTaskModel) *containerregistry.TriggerProperties { baseImageTrigger := expandRegistryTaskBaseImageTrigger(model.BaseImageTrigger) sourceTriggers := expandRegistryTaskSourceTriggers(model.SourceTrigger) timerTriggers := expandRegistryTaskTimerTriggers(model.TimerTrigger) if baseImageTrigger == nil && sourceTriggers == nil && timerTriggers == nil { return nil } - return &legacyacr.TriggerProperties{ + return &containerregistry.TriggerProperties{ BaseImageTrigger: baseImageTrigger, SourceTriggers: sourceTriggers, TimerTriggers: timerTriggers, } } -func expandRegistryTaskBaseImageTrigger(triggers []BaseImageTrigger) *legacyacr.BaseImageTrigger { +func expandRegistryTaskBaseImageTrigger(triggers []BaseImageTrigger) *containerregistry.BaseImageTrigger { if len(triggers) == 0 { return nil } trigger := triggers[0] - status := legacyacr.TriggerStatusDisabled + status := containerregistry.TriggerStatusDisabled if trigger.Enabled { - status = legacyacr.TriggerStatusEnabled + status = containerregistry.TriggerStatusEnabled } - out := &legacyacr.BaseImageTrigger{ + out := &containerregistry.BaseImageTrigger{ Name: &trigger.Name, - BaseImageTriggerType: legacyacr.BaseImageTriggerType(trigger.Type), + BaseImageTriggerType: containerregistry.BaseImageTriggerType(trigger.Type), Status: status, } if trigger.UpdateTriggerEndpoint != "" { out.UpdateTriggerEndpoint = &trigger.UpdateTriggerEndpoint } if trigger.UpdateTriggerPayloadType != "" { - out.UpdateTriggerPayloadType = legacyacr.UpdateTriggerPayloadType(trigger.UpdateTriggerPayloadType) + out.UpdateTriggerPayloadType = containerregistry.UpdateTriggerPayloadType(trigger.UpdateTriggerPayloadType) } return out } -func flattenRegistryTaskBaseImageTrigger(trigger *legacyacr.BaseImageTrigger, model ContainerRegistryTaskModel) []BaseImageTrigger { +func flattenRegistryTaskBaseImageTrigger(trigger *containerregistry.BaseImageTrigger, model ContainerRegistryTaskModel) []BaseImageTrigger { if trigger == nil { return nil } obj := BaseImageTrigger{ Type: string(trigger.BaseImageTriggerType), - Enabled: trigger.Status == legacyacr.TriggerStatusEnabled, + Enabled: trigger.Status == containerregistry.TriggerStatusEnabled, UpdateTriggerPayloadType: string(trigger.UpdateTriggerPayloadType), } @@ -1018,28 +1018,28 @@ func flattenRegistryTaskBaseImageTrigger(trigger *legacyacr.BaseImageTrigger, mo return []BaseImageTrigger{obj} } -func expandRegistryTaskSourceTriggers(triggers []SourceTrigger) *[]legacyacr.SourceTrigger { +func expandRegistryTaskSourceTriggers(triggers []SourceTrigger) *[]containerregistry.SourceTrigger { if len(triggers) == 0 { return nil } - out := make([]legacyacr.SourceTrigger, 0, len(triggers)) + out := make([]containerregistry.SourceTrigger, 0, len(triggers)) for _, trigger := range triggers { - status := legacyacr.TriggerStatusDisabled + status := containerregistry.TriggerStatusDisabled if trigger.Enabled { - status = legacyacr.TriggerStatusEnabled + status = containerregistry.TriggerStatusEnabled } - sourceTrigger := legacyacr.SourceTrigger{ + sourceTrigger := containerregistry.SourceTrigger{ Name: &trigger.Name, Status: status, - SourceRepository: &legacyacr.SourceProperties{ - SourceControlType: legacyacr.SourceControlType(trigger.SourceType), + SourceRepository: &containerregistry.SourceProperties{ + SourceControlType: containerregistry.SourceControlType(trigger.SourceType), RepositoryURL: &trigger.RepositoryURL, }, } if len(trigger.Events) != 0 { - events := make([]legacyacr.SourceTriggerEvent, 0, len(trigger.Events)) + events := make([]containerregistry.SourceTriggerEvent, 0, len(trigger.Events)) for _, event := range trigger.Events { - events = append(events, legacyacr.SourceTriggerEvent(event)) + events = append(events, containerregistry.SourceTriggerEvent(event)) } sourceTrigger.SourceTriggerEvents = &events } @@ -1055,14 +1055,14 @@ func expandRegistryTaskSourceTriggers(triggers []SourceTrigger) *[]legacyacr.Sou return &out } -func flattenRegistryTaskSourceTriggers(triggers *[]legacyacr.SourceTrigger, model ContainerRegistryTaskModel) []SourceTrigger { +func flattenRegistryTaskSourceTriggers(triggers *[]containerregistry.SourceTrigger, model ContainerRegistryTaskModel) []SourceTrigger { if triggers == nil { return nil } out := make([]SourceTrigger, 0, len(*triggers)) for i, trigger := range *triggers { obj := SourceTrigger{ - Enabled: trigger.Status == legacyacr.TriggerStatusEnabled, + Enabled: trigger.Status == containerregistry.TriggerStatusEnabled, } if trigger.Name != nil { obj.Name = *trigger.Name @@ -1094,9 +1094,9 @@ func flattenRegistryTaskSourceTriggers(triggers *[]legacyacr.SourceTrigger, mode return out } -func expandRegistryTaskAuthInfo(auth Auth) *legacyacr.AuthInfo { - out := legacyacr.AuthInfo{ - TokenType: legacyacr.TokenType(auth.TokenType), +func expandRegistryTaskAuthInfo(auth Auth) *containerregistry.AuthInfo { + out := containerregistry.AuthInfo{ + TokenType: containerregistry.TokenType(auth.TokenType), Token: &auth.Token, } if auth.RefreshToken != "" { @@ -1111,17 +1111,17 @@ func expandRegistryTaskAuthInfo(auth Auth) *legacyacr.AuthInfo { return &out } -func expandRegistryTaskTimerTriggers(triggers []TimerTrigger) *[]legacyacr.TimerTrigger { +func expandRegistryTaskTimerTriggers(triggers []TimerTrigger) *[]containerregistry.TimerTrigger { if len(triggers) == 0 { return nil } - out := make([]legacyacr.TimerTrigger, 0, len(triggers)) + out := make([]containerregistry.TimerTrigger, 0, len(triggers)) for _, trigger := range triggers { - status := legacyacr.TriggerStatusDisabled + status := containerregistry.TriggerStatusDisabled if trigger.Enabled { - status = legacyacr.TriggerStatusEnabled + status = containerregistry.TriggerStatusEnabled } - timerTrigger := legacyacr.TimerTrigger{ + timerTrigger := containerregistry.TimerTrigger{ Name: &trigger.Name, Schedule: &trigger.Schedule, Status: status, @@ -1131,14 +1131,14 @@ func expandRegistryTaskTimerTriggers(triggers []TimerTrigger) *[]legacyacr.Timer return &out } -func flattenRegistryTaskTimerTriggers(triggers *[]legacyacr.TimerTrigger) []TimerTrigger { +func flattenRegistryTaskTimerTriggers(triggers *[]containerregistry.TimerTrigger) []TimerTrigger { if triggers == nil { return nil } out := make([]TimerTrigger, 0, len(*triggers)) for _, trigger := range *triggers { obj := TimerTrigger{ - Enabled: trigger.Status == legacyacr.TriggerStatusEnabled, + Enabled: trigger.Status == containerregistry.TriggerStatusEnabled, } if trigger.Name != nil { obj.Name = *trigger.Name @@ -1151,7 +1151,7 @@ func flattenRegistryTaskTimerTriggers(triggers *[]legacyacr.TimerTrigger) []Time return out } -func expandRegistryTaskStep(model ContainerRegistryTaskModel) legacyacr.BasicTaskStepProperties { +func expandRegistryTaskStep(model ContainerRegistryTaskModel) containerregistry.BasicTaskStepProperties { switch { case len(model.DockerStep) != 0: return expandRegistryTaskDockerStep(model.DockerStep[0]) @@ -1163,9 +1163,9 @@ func expandRegistryTaskStep(model ContainerRegistryTaskModel) legacyacr.BasicTas return nil } -func expandRegistryTaskDockerStep(step DockerStep) legacyacr.DockerBuildStep { - out := legacyacr.DockerBuildStep{ - Type: legacyacr.TypeDocker, +func expandRegistryTaskDockerStep(step DockerStep) containerregistry.DockerBuildStep { + out := containerregistry.DockerBuildStep{ + Type: containerregistry.TypeBasicTaskStepPropertiesTypeDocker, DockerFilePath: &step.DockerfilePath, IsPushEnabled: &step.IsPushEnabled, NoCache: utils.Bool(!step.IsCacheEnabled), @@ -1187,7 +1187,7 @@ func expandRegistryTaskDockerStep(step DockerStep) legacyacr.DockerBuildStep { return out } -func flattenRegistryTaskDockerStep(step legacyacr.BasicTaskStepProperties, model ContainerRegistryTaskModel) []DockerStep { +func flattenRegistryTaskDockerStep(step containerregistry.BasicTaskStepProperties, model ContainerRegistryTaskModel) []DockerStep { if step == nil { return nil } @@ -1231,9 +1231,9 @@ func flattenRegistryTaskDockerStep(step legacyacr.BasicTaskStepProperties, model return []DockerStep{obj} } -func expandRegistryTaskFileTaskStep(step FileTaskStep) legacyacr.FileTaskStep { - out := legacyacr.FileTaskStep{ - Type: legacyacr.TypeFileTask, +func expandRegistryTaskFileTaskStep(step FileTaskStep) containerregistry.FileTaskStep { + out := containerregistry.FileTaskStep{ + Type: containerregistry.TypeBasicTaskStepPropertiesTypeFileTask, TaskFilePath: &step.TaskFilePath, Values: expandRegistryTaskValues(step.Values, step.SecretValues), } @@ -1249,7 +1249,7 @@ func expandRegistryTaskFileTaskStep(step FileTaskStep) legacyacr.FileTaskStep { return out } -func flattenRegistryTaskFileTaskStep(step legacyacr.BasicTaskStepProperties, model ContainerRegistryTaskModel) []FileTaskStep { +func flattenRegistryTaskFileTaskStep(step containerregistry.BasicTaskStepProperties, model ContainerRegistryTaskModel) []FileTaskStep { if step == nil { return nil } @@ -1284,9 +1284,9 @@ func flattenRegistryTaskFileTaskStep(step legacyacr.BasicTaskStepProperties, mod return []FileTaskStep{obj} } -func expandRegistryTaskEncodedTaskStep(step EncodedTaskStep) legacyacr.EncodedTaskStep { - out := legacyacr.EncodedTaskStep{ - Type: legacyacr.TypeEncodedTask, +func expandRegistryTaskEncodedTaskStep(step EncodedTaskStep) containerregistry.EncodedTaskStep { + out := containerregistry.EncodedTaskStep{ + Type: containerregistry.TypeBasicTaskStepPropertiesTypeEncodedTask, EncodedTaskContent: utils.String(utils.Base64EncodeIfNot(step.TaskContent)), Values: expandRegistryTaskValues(step.Values, step.SecretValues), } @@ -1302,7 +1302,7 @@ func expandRegistryTaskEncodedTaskStep(step EncodedTaskStep) legacyacr.EncodedTa return out } -func flattenRegistryTaskEncodedTaskStep(step legacyacr.BasicTaskStepProperties, model ContainerRegistryTaskModel) []EncodedTaskStep { +func flattenRegistryTaskEncodedTaskStep(step containerregistry.BasicTaskStepProperties, model ContainerRegistryTaskModel) []EncodedTaskStep { if step == nil { return nil } @@ -1340,20 +1340,20 @@ func flattenRegistryTaskEncodedTaskStep(step legacyacr.BasicTaskStepProperties, return []EncodedTaskStep{obj} } -func expandRegistryTaskArguments(arguments map[string]string, secretArguments map[string]string) *[]legacyacr.Argument { +func expandRegistryTaskArguments(arguments map[string]string, secretArguments map[string]string) *[]containerregistry.Argument { if len(arguments) == 0 && len(secretArguments) == 0 { return nil } - out := make([]legacyacr.Argument, 0, len(arguments)+len(secretArguments)) + out := make([]containerregistry.Argument, 0, len(arguments)+len(secretArguments)) for k, v := range arguments { - out = append(out, legacyacr.Argument{ + out = append(out, containerregistry.Argument{ Name: utils.String(k), Value: utils.String(v), IsSecret: utils.Bool(false), }) } for k, v := range secretArguments { - out = append(out, legacyacr.Argument{ + out = append(out, containerregistry.Argument{ Name: utils.String(k), Value: utils.String(v), IsSecret: utils.Bool(true), @@ -1362,7 +1362,7 @@ func expandRegistryTaskArguments(arguments map[string]string, secretArguments ma return &out } -func flattenRegistryTaskArguments(arguments *[]legacyacr.Argument) map[string]string { +func flattenRegistryTaskArguments(arguments *[]containerregistry.Argument) map[string]string { if arguments == nil { return nil } @@ -1396,20 +1396,20 @@ func flattenRegistryTaskArguments(arguments *[]legacyacr.Argument) map[string]st return args } -func expandRegistryTaskValues(values map[string]string, secretValues map[string]string) *[]legacyacr.SetValue { +func expandRegistryTaskValues(values map[string]string, secretValues map[string]string) *[]containerregistry.SetValue { if len(values) == 0 && len(secretValues) == 0 { return nil } - out := make([]legacyacr.SetValue, 0, len(values)+len(secretValues)) + out := make([]containerregistry.SetValue, 0, len(values)+len(secretValues)) for k, v := range values { - out = append(out, legacyacr.SetValue{ + out = append(out, containerregistry.SetValue{ Name: utils.String(k), Value: utils.String(v), IsSecret: utils.Bool(false), }) } for k, v := range secretValues { - out = append(out, legacyacr.SetValue{ + out = append(out, containerregistry.SetValue{ Name: utils.String(k), Value: utils.String(v), IsSecret: utils.Bool(true), @@ -1418,7 +1418,7 @@ func expandRegistryTaskValues(values map[string]string, secretValues map[string] return &out } -func flattenRegistryTaskValues(values *[]legacyacr.SetValue) map[string]string { +func flattenRegistryTaskValues(values *[]containerregistry.SetValue) map[string]string { if values == nil { return nil } @@ -1451,19 +1451,19 @@ func flattenRegistryTaskValues(values *[]legacyacr.SetValue) map[string]string { return vals } -func expandRegistryTaskIdentity(input []interface{}) (*legacyacr.IdentityProperties, error) { +func expandRegistryTaskIdentity(input []interface{}) (*containerregistry.IdentityProperties, error) { expanded, err := identity.ExpandSystemAndUserAssignedMap(input) if err != nil { return nil, err } - out := legacyacr.IdentityProperties{ - Type: legacyacr.ResourceIdentityType(string(expanded.Type)), + out := containerregistry.IdentityProperties{ + Type: containerregistry.ResourceIdentityType(string(expanded.Type)), } if len(expanded.IdentityIds) > 0 { - out.UserAssignedIdentities = map[string]*legacyacr.UserIdentityProperties{} + out.UserAssignedIdentities = map[string]*containerregistry.UserIdentityProperties{} for k := range expanded.IdentityIds { - out.UserAssignedIdentities[k] = &legacyacr.UserIdentityProperties{ + out.UserAssignedIdentities[k] = &containerregistry.UserIdentityProperties{ // intentionally empty } } @@ -1471,7 +1471,7 @@ func expandRegistryTaskIdentity(input []interface{}) (*legacyacr.IdentityPropert return &out, nil } -func flattenRegistryTaskIdentity(input *legacyacr.IdentityProperties) (*[]interface{}, error) { +func flattenRegistryTaskIdentity(input *containerregistry.IdentityProperties) (*[]interface{}, error) { var transform *identity.SystemAndUserAssignedMap if input != nil { @@ -1496,24 +1496,24 @@ func flattenRegistryTaskIdentity(input *legacyacr.IdentityProperties) (*[]interf return identity.FlattenSystemAndUserAssignedMap(transform) } -func expandRegistryTaskPlatform(input []Platform) *legacyacr.PlatformProperties { +func expandRegistryTaskPlatform(input []Platform) *containerregistry.PlatformProperties { if len(input) == 0 { return nil } platform := input[0] - out := &legacyacr.PlatformProperties{ - Os: legacyacr.OS(platform.OS), + out := &containerregistry.PlatformProperties{ + Os: containerregistry.OS(platform.OS), } if arch := platform.Architecture; arch != "" { - out.Architecture = legacyacr.Architecture(arch) + out.Architecture = containerregistry.Architecture(arch) } if variant := platform.Variant; variant != "" { - out.Variant = legacyacr.Variant(variant) + out.Variant = containerregistry.Variant(variant) } return out } -func flattenRegistryTaskPlatform(platform *legacyacr.PlatformProperties) []Platform { +func flattenRegistryTaskPlatform(platform *containerregistry.PlatformProperties) []Platform { if platform == nil { return nil } @@ -1524,18 +1524,18 @@ func flattenRegistryTaskPlatform(platform *legacyacr.PlatformProperties) []Platf }} } -func expandRegistryTaskCredentials(input []RegistryCredential) *legacyacr.Credentials { +func expandRegistryTaskCredentials(input []RegistryCredential) *containerregistry.Credentials { if len(input) == 0 { return nil } - return &legacyacr.Credentials{ + return &containerregistry.Credentials{ SourceRegistry: expandSourceRegistryCredential(input[0].Source), CustomRegistries: expandCustomRegistryCredential(input[0].Custom), } } -func flattenRegistryTaskCredentials(input *legacyacr.Credentials, model ContainerRegistryTaskModel) []RegistryCredential { +func flattenRegistryTaskCredentials(input *containerregistry.Credentials, model ContainerRegistryTaskModel) []RegistryCredential { if input == nil { return nil } @@ -1554,15 +1554,15 @@ func flattenRegistryTaskCredentials(input *legacyacr.Credentials, model Containe } } -func expandSourceRegistryCredential(input []SourceRegistryCredential) *legacyacr.SourceRegistryCredentials { +func expandSourceRegistryCredential(input []SourceRegistryCredential) *containerregistry.SourceRegistryCredentials { if len(input) == 0 { return nil } - return &legacyacr.SourceRegistryCredentials{LoginMode: legacyacr.SourceRegistryLoginMode(input[0].LoginMode)} + return &containerregistry.SourceRegistryCredentials{LoginMode: containerregistry.SourceRegistryLoginMode(input[0].LoginMode)} } -func flattenSourceRegistryCredential(input *legacyacr.SourceRegistryCredentials) []SourceRegistryCredential { +func flattenSourceRegistryCredential(input *containerregistry.SourceRegistryCredentials) []SourceRegistryCredential { if input == nil { return nil } @@ -1570,31 +1570,31 @@ func flattenSourceRegistryCredential(input *legacyacr.SourceRegistryCredentials) return []SourceRegistryCredential{{LoginMode: string(input.LoginMode)}} } -func expandCustomRegistryCredential(input []CustomRegistryCredential) map[string]*legacyacr.CustomRegistryCredentials { +func expandCustomRegistryCredential(input []CustomRegistryCredential) map[string]*containerregistry.CustomRegistryCredentials { if len(input) == 0 { return nil } - out := map[string]*legacyacr.CustomRegistryCredentials{} + out := map[string]*containerregistry.CustomRegistryCredentials{} for _, credential := range input { - cred := &legacyacr.CustomRegistryCredentials{} + cred := &containerregistry.CustomRegistryCredentials{} if credential.UserName != "" { - usernameType := legacyacr.Opaque + usernameType := containerregistry.SecretObjectTypeOpaque if _, err := keyVaultParse.ParseNestedItemID(credential.UserName); err == nil { - usernameType = legacyacr.Vaultsecret + usernameType = containerregistry.SecretObjectTypeVaultsecret } - cred.UserName = &legacyacr.SecretObject{ + cred.UserName = &containerregistry.SecretObject{ Value: utils.String(credential.UserName), Type: usernameType, } } if credential.Password != "" { - passwordType := legacyacr.Opaque + passwordType := containerregistry.SecretObjectTypeOpaque if _, err := keyVaultParse.ParseNestedItemID(credential.Password); err == nil { - passwordType = legacyacr.Vaultsecret + passwordType = containerregistry.SecretObjectTypeVaultsecret } - cred.Password = &legacyacr.SecretObject{ + cred.Password = &containerregistry.SecretObject{ Value: utils.String(credential.Password), Type: passwordType, } @@ -1607,16 +1607,16 @@ func expandCustomRegistryCredential(input []CustomRegistryCredential) map[string return out } -func expandRegistryTaskAgentProperties(input []AgentConfig) *legacyacr.AgentProperties { +func expandRegistryTaskAgentProperties(input []AgentConfig) *containerregistry.AgentProperties { if len(input) == 0 { return nil } agentConfig := input[0] - return &legacyacr.AgentProperties{CPU: utils.Int32(int32(agentConfig.CPU))} + return &containerregistry.AgentProperties{CPU: utils.Int32(int32(agentConfig.CPU))} } -func flattenRegistryTaskAgentProperties(input *legacyacr.AgentProperties) []AgentConfig { +func flattenRegistryTaskAgentProperties(input *containerregistry.AgentProperties) []AgentConfig { if input == nil { return nil } @@ -1628,12 +1628,12 @@ func flattenRegistryTaskAgentProperties(input *legacyacr.AgentProperties) []Agen return []AgentConfig{{CPU: cpu}} } -func patchRegistryTaskTriggerSourceTrigger(triggers []legacyacr.SourceTrigger, model ContainerRegistryTaskModel) *[]legacyacr.SourceTrigger { +func patchRegistryTaskTriggerSourceTrigger(triggers []containerregistry.SourceTrigger, model ContainerRegistryTaskModel) *[]containerregistry.SourceTrigger { if len(triggers) != len(model.SourceTrigger) { return &triggers } - result := make([]legacyacr.SourceTrigger, len(triggers)) + result := make([]containerregistry.SourceTrigger, len(triggers)) for i, trigger := range model.SourceTrigger { t := (triggers)[i] if len(trigger.Auth) == 0 { diff --git a/internal/services/containers/container_registry_task_schedule_run_now_resource.go b/internal/services/containers/container_registry_task_schedule_run_now_resource.go new file mode 100644 index 000000000000..07ffa8b44d01 --- /dev/null +++ b/internal/services/containers/container_registry_task_schedule_run_now_resource.go @@ -0,0 +1,164 @@ +package containers + +import ( + "context" + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ContainerRegistryTaskScheduleResource struct{} + +var _ sdk.Resource = ContainerRegistryTaskScheduleResource{} + +type ContainerRegistryTaskScheduleModel struct { + TaskId string `tfschema:"container_registry_task_id"` +} + +func (r ContainerRegistryTaskScheduleResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "container_registry_task_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ContainerRegistryTaskID, + }, + } +} + +func (r ContainerRegistryTaskScheduleResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r ContainerRegistryTaskScheduleResource) ResourceType() string { + return "azurerm_container_registry_task_schedule_run_now" +} + +func (r ContainerRegistryTaskScheduleResource) ModelObject() interface{} { + return &ContainerRegistryTaskScheduleModel{} +} + +func (r ContainerRegistryTaskScheduleResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ContainerRegistryTaskScheduleID +} + +func (r ContainerRegistryTaskScheduleResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + taskClient := metadata.Client.Containers.TasksClient + + var model ContainerRegistryTaskScheduleModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding %+v", err) + } + + taskId, err := parse.ContainerRegistryTaskID(model.TaskId) + if err != nil { + return fmt.Errorf("parsing container registry task ID: %v", err) + } + + resp, err := taskClient.Get(ctx, taskId.ResourceGroup, taskId.RegistryName, taskId.TaskName) + if err != nil { + return fmt.Errorf("retrieving %q: %+v", taskId, err) + } + if resp.TaskProperties == nil { + return fmt.Errorf("unexpected nil `taskProperties` of %q", taskId) + } + if resp.TaskProperties.Step == nil { + return fmt.Errorf("unexpected nil `taskProperties.step` of %q", taskId) + } + + req := containerregistry.TaskRunRequest{ + TaskID: utils.String(taskId.ID()), + } + switch resp.TaskProperties.Step.(type) { + case containerregistry.DockerBuildStep: + req.Type = containerregistry.TypeDockerBuildRequest + case containerregistry.FileTaskStep: + req.Type = containerregistry.TypeFileTaskRunRequest + case containerregistry.EncodedTaskStep: + req.Type = containerregistry.TypeEncodedTaskRunRequest + default: + return fmt.Errorf("unexpected container registry task step type: %T", resp.TaskProperties.Step) + } + + registryClient := metadata.Client.Containers.RegistriesClient + future, err := registryClient.ScheduleRun(ctx, taskId.ResourceGroup, taskId.RegistryName, req) + if err != nil { + return fmt.Errorf("scheduling the task: %v", err) + } + if err := future.WaitForCompletionRef(ctx, registryClient.Client); err != nil { + return fmt.Errorf("waiting for schedule: %v", err) + } + + run, err := future.Result(*registryClient) + if err != nil { + return fmt.Errorf("getting the scheduled run: %v", err) + } + + if run.Name == nil { + return fmt.Errorf("unexpected nil scheduled run name") + } + + runsClient := metadata.Client.Containers.RunsClient + + timeout, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{string(containerregistry.RunStatusQueued), string(containerregistry.RunStatusStarted), string(containerregistry.RunStatusRunning)}, + Target: []string{string(containerregistry.RunStatusSucceeded)}, + Refresh: func() (interface{}, string, error) { + resp, err := runsClient.Get(ctx, taskId.ResourceGroup, taskId.RegistryName, *run.Name) + if err != nil { + return nil, "", fmt.Errorf("getting the scheduled run: %v", err) + } + + if resp.RunProperties == nil { + return nil, "", fmt.Errorf("unexpected nil properties of the scheduled run") + } + + return run, string(resp.RunProperties.Status), nil + }, + ContinuousTargetOccurence: 1, + PollInterval: 5 * time.Second, + Timeout: time.Until(timeout), + } + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for scheduled task to finish: %+v", err) + } + + metadata.SetID(parse.NewContainerRegistryTaskScheduleID(taskId.SubscriptionId, taskId.ResourceGroup, taskId.RegistryName, taskId.TaskName, "schedule")) + return nil + }, + } +} + +func (r ContainerRegistryTaskScheduleResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + id, err := parse.ContainerRegistryTaskScheduleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + model := ContainerRegistryTaskScheduleModel{TaskId: parse.NewContainerRegistryTaskID(id.SubscriptionId, id.ResourceGroup, id.RegistryName, id.TaskName).ID()} + return metadata.Encode(&model) + }, + } +} + +func (r ContainerRegistryTaskScheduleResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + return nil + }, + } +} diff --git a/internal/services/containers/container_registry_task_schedule_run_now_resource_test.go b/internal/services/containers/container_registry_task_schedule_run_now_resource_test.go new file mode 100644 index 000000000000..317c5bcc8232 --- /dev/null +++ b/internal/services/containers/container_registry_task_schedule_run_now_resource_test.go @@ -0,0 +1,149 @@ +package containers_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type ContainerRegistryTaskScheduleResource struct { + githubRepo +} + +func TestAccContainerRegistryTaskSchedule_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_registry_task_schedule_run_now", "test") + + preCheckGithubRepo(t) + + r := ContainerRegistryTaskScheduleResource{ + githubRepo: githubRepo{ + url: os.Getenv("ARM_TEST_ACR_TASK_GITHUB_REPO_URL"), + token: os.Getenv("ARM_TEST_ACR_TASK_GITHUB_USER_TOKEN"), + }, + } + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data, r.dockerTaskStep), + }, + data.ImportStep(), + { + Config: r.basic(data, r.fileTaskStep), + }, + data.ImportStep(), + { + Config: r.basic(data, r.encodedTaskStep), + }, + data.ImportStep(), + }) +} + +func (r ContainerRegistryTaskScheduleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + ret := false + return &ret, nil +} + +func (r ContainerRegistryTaskScheduleResource) basic(data acceptance.TestData, tpl func(data acceptance.TestData) string) string { + template := tpl(data) + return fmt.Sprintf(` +%s + +resource "azurerm_container_registry_task_schedule_run_now" "test" { + container_registry_task_id = azurerm_container_registry_task.test.id +} +`, template) +} + +func (r ContainerRegistryTaskScheduleResource) dockerTaskStep(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_container_registry_task" "test" { + name = "testacccrTask%d" + container_registry_id = azurerm_container_registry.test.id + platform { + os = "Linux" + } + docker_step { + dockerfile_path = "Dockerfile" + context_path = "%s" + context_access_token = "%s" + image_names = ["helloworld:{{.Run.ID}}"] + } +} +`, template, data.RandomInteger, r.githubRepo.url, r.githubRepo.token) +} + +func (r ContainerRegistryTaskScheduleResource) fileTaskStep(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_container_registry_task" "test" { + name = "testacccrTask%d" + container_registry_id = azurerm_container_registry.test.id + platform { + os = "Linux" + } + file_step { + task_file_path = "taskmulti.yaml" + context_path = "%s" + context_access_token = "%s" + } +} +`, template, data.RandomInteger, r.githubRepo.url, r.githubRepo.token) +} + +func (r ContainerRegistryTaskScheduleResource) encodedTaskStep(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_container_registry_task" "test" { + name = "testacccrTask%d" + container_registry_id = azurerm_container_registry.test.id + platform { + os = "Linux" + } + encoded_step { + task_content = < You can't upgrade a spot node pool since spot node pools can't guarantee cordon and drain. - // > You must replace your existing spot node pool with a new one to do operations such as upgrading - // > the Kubernetes version. To replace a spot node pool, create a new spot node pool with a different - // > version of Kubernetes, wait until its status is Ready, then remove the old node pool. - if strings.EqualFold(string(props.ScaleSetPriority), string(containerservice.ScaleSetPrioritySpot)) { - // ^ the Scale Set Priority isn't returned when Regular - return fmt.Errorf("the Orchestrator Version cannot be updated when using a Spot Node Pool") - } - existingNodePool, err := client.Get(ctx, id.ResourceGroup, id.ManagedClusterName, id.AgentPoolName) if err != nil { return fmt.Errorf("retrieving Node Pool %s: %+v", *id, err) @@ -917,7 +907,7 @@ func upgradeSettingsForDataSourceSchema() *pluginsdk.Schema { func expandUpgradeSettings(input []interface{}) *containerservice.AgentPoolUpgradeSettings { setting := &containerservice.AgentPoolUpgradeSettings{} - if len(input) == 0 { + if len(input) == 0 || input[0] == nil { return setting } diff --git a/internal/services/containers/kubernetes_cluster_other_resource_test.go b/internal/services/containers/kubernetes_cluster_other_resource_test.go index e319eb8d09bb..b36176c0d2d3 100644 --- a/internal/services/containers/kubernetes_cluster_other_resource_test.go +++ b/internal/services/containers/kubernetes_cluster_other_resource_test.go @@ -166,7 +166,7 @@ func TestAccKubernetesCluster_nodeLabels(t *testing.T) { Config: r.nodeLabelsConfig(data, labels3), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - acceptance.TestCheckNoResourceAttr(data.ResourceName, "default_node_pool.0.node_labels"), + check.That(data.ResourceName).Key("default_node_pool.0.node_labels.%").HasValue("0"), ), }, }) diff --git a/internal/services/containers/kubernetes_cluster_resource.go b/internal/services/containers/kubernetes_cluster_resource.go index 9c796f5e9d05..10c06c79a33d 100644 --- a/internal/services/containers/kubernetes_cluster_resource.go +++ b/internal/services/containers/kubernetes_cluster_resource.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/privatezones" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" @@ -24,7 +25,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/parse" containerValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/validate" - logAnalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" @@ -396,7 +396,7 @@ func resourceKubernetesCluster() *pluginsdk.Resource { "log_analytics_workspace_id": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: logAnalyticsValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, }, }, }, @@ -1925,7 +1925,7 @@ func flattenKubernetesClusterAccessProfile(profile containerservice.ManagedClust rawConfig := string(*kubeConfigRaw) var flattenedKubeConfig []interface{} - if strings.Contains(rawConfig, "apiserver-id:") { + if strings.Contains(rawConfig, "apiserver-id:") || strings.Contains(rawConfig, "exec") { kubeConfigAAD, err := kubernetes.ParseKubeConfigAAD(rawConfig) if err != nil { return utils.String(rawConfig), []interface{}{} diff --git a/internal/services/containers/kubernetes_cluster_resource_test.go b/internal/services/containers/kubernetes_cluster_resource_test.go index 08c986c1fcef..8e885685f710 100644 --- a/internal/services/containers/kubernetes_cluster_resource_test.go +++ b/internal/services/containers/kubernetes_cluster_resource_test.go @@ -17,8 +17,8 @@ import ( type KubernetesClusterResource struct{} var ( - olderKubernetesVersion = "1.21.7" - currentKubernetesVersion = "1.22.4" + olderKubernetesVersion = "1.22.11" + currentKubernetesVersion = "1.23.5" olderKubernetesVersionAlias = "1.22" currentKubernetesVersionAlias = "1.23" ) @@ -286,3 +286,18 @@ resource "azurerm_kubernetes_cluster" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, maxSurge) } + +func TestAccResourceKubernetesCluster_roleBasedAccessControlAAD_VOneDotTwoFourDotThree(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.roleBasedAccessControlAADManagedConfigVOneDotTwoFourDotThree(data, ""), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("kube_config.#").HasValue("1"), + check.That(data.ResourceName).Key("kube_config.0.host").IsSet(), + ), + }, + }) +} diff --git a/internal/services/containers/kubernetes_cluster_upgrade_test.go b/internal/services/containers/kubernetes_cluster_upgrade_test.go index fbff4a10149b..0491ec91a2ac 100644 --- a/internal/services/containers/kubernetes_cluster_upgrade_test.go +++ b/internal/services/containers/kubernetes_cluster_upgrade_test.go @@ -239,6 +239,37 @@ func TestAccKubernetesCluster_upgradeControlPlaneAndAllPoolsTogetherVersionAlias }) } +func TestAccKubernetesCluster_upgradeControlPlaneAndAllPoolsTogetherSpot(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + nodePoolName := "azurerm_kubernetes_cluster_node_pool.test" + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + // all on the older version + Config: r.upgradeVersionsConfigSpot(data, olderKubernetesVersion, olderKubernetesVersion, olderKubernetesVersion), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kubernetes_version").HasValue(olderKubernetesVersion), + check.That(data.ResourceName).Key("default_node_pool.0.orchestrator_version").HasValue(olderKubernetesVersion), + check.That(nodePoolName).Key("orchestrator_version").HasValue(olderKubernetesVersion), + ), + }, + data.ImportStep(), + { + // upgrade control plane, default and custom node pools + Config: r.upgradeVersionsConfigSpot(data, currentKubernetesVersion, currentKubernetesVersion, currentKubernetesVersion), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("kubernetes_version").HasValue(currentKubernetesVersion), + check.That(data.ResourceName).Key("default_node_pool.0.orchestrator_version").HasValue(currentKubernetesVersion), + check.That(nodePoolName).Key("orchestrator_version").HasValue(currentKubernetesVersion), + ), + }, + data.ImportStep(), + }) +} + func TestAccKubernetesCluster_upgradeSettings(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") r := KubernetesClusterResource{} @@ -350,6 +381,29 @@ resource "azurerm_kubernetes_cluster_node_pool" "test" { `, r.upgradeControlPlaneDefaultNodePoolConfig(data, controlPlaneVersion, defaultNodePoolVersion), customNodePoolVersion) } +func (r KubernetesClusterResource) upgradeVersionsConfigSpot(data acceptance.TestData, controlPlaneVersion, defaultNodePoolVersion, customNodePoolVersion string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_kubernetes_cluster_node_pool" "test" { + name = "internal" + kubernetes_cluster_id = azurerm_kubernetes_cluster.test.id + vm_size = "Standard_DS2_v2" + node_count = 1 + orchestrator_version = %q + priority = "Spot" + eviction_policy = "Delete" + spot_max_price = 0.5 # high, but this is a maximum (we pay less) so ensures this won't fail + node_labels = { + "kubernetes.azure.com/scalesetpriority" = "spot" + } + node_taints = [ + "kubernetes.azure.com/scalesetpriority=spot:NoSchedule" + ] +} +`, r.upgradeControlPlaneDefaultNodePoolConfig(data, controlPlaneVersion, defaultNodePoolVersion), customNodePoolVersion) +} + func (KubernetesClusterResource) upgradeAutoScaleMinCountConfig(data acceptance.TestData, controlPlaneVersion string, minCount int, maxCount int) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/containers/kubernetes_nodepool.go b/internal/services/containers/kubernetes_nodepool.go index eb8a5a576a10..3171c321bc19 100644 --- a/internal/services/containers/kubernetes_nodepool.go +++ b/internal/services/containers/kubernetes_nodepool.go @@ -6,6 +6,8 @@ import ( "strconv" "strings" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/proximityplacementgroups" + "github.com/Azure/azure-sdk-for-go/services/preview/containerservice/mgmt/2022-03-02-preview/containerservice" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/zones" @@ -216,7 +218,7 @@ func SchemaDefaultNodePool() *pluginsdk.Schema { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, - ValidateFunc: computeValidate.ProximityPlacementGroupID, + ValidateFunc: proximityplacementgroups.ValidateProximityPlacementGroupID, }, "only_critical_addons_enabled": { Type: pluginsdk.TypeBool, diff --git a/internal/services/containers/parse/container_registry_task_schedule.go b/internal/services/containers/parse/container_registry_task_schedule.go new file mode 100644 index 000000000000..56ac6e41db0b --- /dev/null +++ b/internal/services/containers/parse/container_registry_task_schedule.go @@ -0,0 +1,81 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type ContainerRegistryTaskScheduleId struct { + SubscriptionId string + ResourceGroup string + RegistryName string + TaskName string + ScheduleName string +} + +func NewContainerRegistryTaskScheduleID(subscriptionId, resourceGroup, registryName, taskName, scheduleName string) ContainerRegistryTaskScheduleId { + return ContainerRegistryTaskScheduleId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + RegistryName: registryName, + TaskName: taskName, + ScheduleName: scheduleName, + } +} + +func (id ContainerRegistryTaskScheduleId) String() string { + segments := []string{ + fmt.Sprintf("Schedule Name %q", id.ScheduleName), + fmt.Sprintf("Task Name %q", id.TaskName), + fmt.Sprintf("Registry Name %q", id.RegistryName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Container Registry Task Schedule", segmentsStr) +} + +func (id ContainerRegistryTaskScheduleId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerRegistry/registries/%s/tasks/%s/schedule/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.RegistryName, id.TaskName, id.ScheduleName) +} + +// ContainerRegistryTaskScheduleID parses a ContainerRegistryTaskSchedule ID into an ContainerRegistryTaskScheduleId struct +func ContainerRegistryTaskScheduleID(input string) (*ContainerRegistryTaskScheduleId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ContainerRegistryTaskScheduleId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.RegistryName, err = id.PopSegment("registries"); err != nil { + return nil, err + } + if resourceId.TaskName, err = id.PopSegment("tasks"); err != nil { + return nil, err + } + if resourceId.ScheduleName, err = id.PopSegment("schedule"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/containers/parse/container_registry_task_schedule_test.go b/internal/services/containers/parse/container_registry_task_schedule_test.go new file mode 100644 index 000000000000..3066737ff56c --- /dev/null +++ b/internal/services/containers/parse/container_registry_task_schedule_test.go @@ -0,0 +1,144 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = ContainerRegistryTaskScheduleId{} + +func TestContainerRegistryTaskScheduleIDFormatter(t *testing.T) { + actual := NewContainerRegistryTaskScheduleID("12345678-1234-9876-4563-123456789012", "group1", "registry1", "task1", "schedule1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/schedule1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestContainerRegistryTaskScheduleID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ContainerRegistryTaskScheduleId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing RegistryName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/", + Error: true, + }, + + { + // missing value for RegistryName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/", + Error: true, + }, + + { + // missing TaskName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/", + Error: true, + }, + + { + // missing value for TaskName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/", + Error: true, + }, + + { + // missing ScheduleName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/", + Error: true, + }, + + { + // missing value for ScheduleName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/schedule1", + Expected: &ContainerRegistryTaskScheduleId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "group1", + RegistryName: "registry1", + TaskName: "task1", + ScheduleName: "schedule1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.CONTAINERREGISTRY/REGISTRIES/REGISTRY1/TASKS/TASK1/SCHEDULE/SCHEDULE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ContainerRegistryTaskScheduleID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.RegistryName != v.Expected.RegistryName { + t.Fatalf("Expected %q but got %q for RegistryName", v.Expected.RegistryName, actual.RegistryName) + } + if actual.TaskName != v.Expected.TaskName { + t.Fatalf("Expected %q but got %q for TaskName", v.Expected.TaskName, actual.TaskName) + } + if actual.ScheduleName != v.Expected.ScheduleName { + t.Fatalf("Expected %q but got %q for ScheduleName", v.Expected.ScheduleName, actual.ScheduleName) + } + } +} diff --git a/internal/services/containers/registration.go b/internal/services/containers/registration.go index 837f680913fd..ac80b28dd481 100644 --- a/internal/services/containers/registration.go +++ b/internal/services/containers/registration.go @@ -53,6 +53,7 @@ func (r Registration) DataSources() []sdk.DataSource { func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ ContainerRegistryTaskResource{}, + ContainerRegistryTaskScheduleResource{}, ContainerConnectedRegistryResource{}, } } diff --git a/internal/services/containers/resourceids.go b/internal/services/containers/resourceids.go index 6e9486c55413..783dd89761c7 100644 --- a/internal/services/containers/resourceids.go +++ b/internal/services/containers/resourceids.go @@ -5,6 +5,7 @@ package containers //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NodePool -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1/agentPools/pool1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ContainerRegistryScopeMap -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ContainerRegistry/registries/registry1/scopeMaps/scopeMap1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ContainerRegistryTask -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ContainerRegistryTaskSchedule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/schedule1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ContainerRegistryToken -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ContainerRegistry/registries/registry1/tokens/token1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Registry -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Webhook -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/webhooks/webhook1 diff --git a/internal/services/containers/validate/container_registry_task_schedule_id.go b/internal/services/containers/validate/container_registry_task_schedule_id.go new file mode 100644 index 000000000000..417723db6e2a --- /dev/null +++ b/internal/services/containers/validate/container_registry_task_schedule_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/containers/parse" +) + +func ContainerRegistryTaskScheduleID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ContainerRegistryTaskScheduleID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/containers/validate/container_registry_task_schedule_id_test.go b/internal/services/containers/validate/container_registry_task_schedule_id_test.go new file mode 100644 index 000000000000..a28599f40e1a --- /dev/null +++ b/internal/services/containers/validate/container_registry_task_schedule_id_test.go @@ -0,0 +1,100 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestContainerRegistryTaskScheduleID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing RegistryName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/", + Valid: false, + }, + + { + // missing value for RegistryName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/", + Valid: false, + }, + + { + // missing TaskName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/", + Valid: false, + }, + + { + // missing value for TaskName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/", + Valid: false, + }, + + { + // missing ScheduleName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/", + Valid: false, + }, + + { + // missing value for ScheduleName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.ContainerRegistry/registries/registry1/tasks/task1/schedule/schedule1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.CONTAINERREGISTRY/REGISTRIES/REGISTRY1/TASKS/TASK1/SCHEDULE/SCHEDULE1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ContainerRegistryTaskScheduleID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/cosmos/client/client.go b/internal/services/cosmos/client/client.go index 3e6245a290ab..2a705c55ca69 100644 --- a/internal/services/cosmos/client/client.go +++ b/internal/services/cosmos/client/client.go @@ -2,6 +2,7 @@ package client import ( "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2021-10-15/documentdb" + "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2022-05-15/sqldedicatedgateway" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) @@ -14,6 +15,7 @@ type Client struct { MongoDbClient *documentdb.MongoDBResourcesClient NotebookWorkspaceClient *documentdb.NotebookWorkspacesClient RestorableDatabaseAccountsClient *documentdb.RestorableDatabaseAccountsClient + SqlDedicatedGatewayClient *sqldedicatedgateway.SqlDedicatedGatewayClient SqlClient *documentdb.SQLResourcesClient SqlResourceClient *documentdb.SQLResourcesClient TableClient *documentdb.TableResourcesClient @@ -44,6 +46,9 @@ func NewClient(o *common.ClientOptions) *Client { restorableDatabaseAccountsClient := documentdb.NewRestorableDatabaseAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&restorableDatabaseAccountsClient.Client, o.ResourceManagerAuthorizer) + sqlDedicatedGatewayClient := sqldedicatedgateway.NewSqlDedicatedGatewayClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&sqlDedicatedGatewayClient.Client, o.ResourceManagerAuthorizer) + sqlClient := documentdb.NewSQLResourcesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&sqlClient.Client, o.ResourceManagerAuthorizer) @@ -62,6 +67,7 @@ func NewClient(o *common.ClientOptions) *Client { MongoDbClient: &mongoDbClient, NotebookWorkspaceClient: ¬ebookWorkspaceClient, RestorableDatabaseAccountsClient: &restorableDatabaseAccountsClient, + SqlDedicatedGatewayClient: &sqlDedicatedGatewayClient, SqlClient: &sqlClient, SqlResourceClient: &sqlResourceClient, TableClient: &tableClient, diff --git a/internal/services/cosmos/common/autoscale_settings.go b/internal/services/cosmos/common/autoscale_settings.go index 9d8eded444db..c8e3644b21bb 100644 --- a/internal/services/cosmos/common/autoscale_settings.go +++ b/internal/services/cosmos/common/autoscale_settings.go @@ -55,8 +55,12 @@ func FlattenCosmosDbAutoscaleSettings(throughputResponse documentdb.ThroughputSe func ExpandCosmosDbAutoscaleSettingsResource(d *pluginsdk.ResourceData) *documentdb.AutoscaleSettingsResource { autoscaleSettings := ExpandCosmosDbAutoscaleSettings(d) - autoscaleSettingResource := documentdb.AutoscaleSettingsResource{} - autoscaleSettingResource.MaxThroughput = autoscaleSettings.MaxThroughput - return &autoscaleSettingResource + if autoscaleSettings == nil { + return nil + } + + return &documentdb.AutoscaleSettingsResource{ + MaxThroughput: autoscaleSettings.MaxThroughput, + } } diff --git a/internal/services/cosmos/cosmosdb_cassandra_cluster_resource.go b/internal/services/cosmos/cosmosdb_cassandra_cluster_resource.go index 2a988f2f21f8..eef97f9e3518 100644 --- a/internal/services/cosmos/cosmosdb_cassandra_cluster_resource.go +++ b/internal/services/cosmos/cosmosdb_cassandra_cluster_resource.go @@ -292,10 +292,13 @@ func resourceCassandraClusterUpdate(d *pluginsdk.ResourceData, meta interface{}) } // Though there is update method but Service API complains it isn't implemented - _, err = client.CreateUpdate(ctx, id.ResourceGroup, id.Name, body) + future, err := client.CreateUpdate(ctx, id.ResourceGroup, id.Name, body) if err != nil { return fmt.Errorf("updating %q: %+v", id, err) } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for update of %q: %+v", id, err) + } // Issue: https://github.com/Azure/azure-rest-api-specs/issues/19021 // There is a long running issue on updating this resource. diff --git a/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource.go b/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource.go new file mode 100644 index 000000000000..865c1d443f08 --- /dev/null +++ b/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource.go @@ -0,0 +1,224 @@ +package cosmos + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2022-05-15/cosmosdb" + "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2022-05-15/sqldedicatedgateway" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type CosmosDbSqlDedicatedGatewayModel struct { + CosmosDbAccountId string `tfschema:"cosmosdb_account_id"` + InstanceCount int64 `tfschema:"instance_count"` + InstanceSize sqldedicatedgateway.ServiceSize `tfschema:"instance_size"` +} + +type CosmosDbSqlDedicatedGatewayResource struct{} + +var _ sdk.ResourceWithUpdate = CosmosDbSqlDedicatedGatewayResource{} + +func (r CosmosDbSqlDedicatedGatewayResource) ResourceType() string { + return "azurerm_cosmosdb_sql_dedicated_gateway" +} + +func (r CosmosDbSqlDedicatedGatewayResource) ModelObject() interface{} { + return &CosmosDbSqlDedicatedGatewayModel{} +} + +func (r CosmosDbSqlDedicatedGatewayResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return sqldedicatedgateway.ValidateServiceID +} + +func (r CosmosDbSqlDedicatedGatewayResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "cosmosdb_account_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: cosmosdb.ValidateDatabaseAccountID, + }, + + "instance_size": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sqldedicatedgateway.ServiceSizeCosmosPointDFours), + string(sqldedicatedgateway.ServiceSizeCosmosPointDEights), + string(sqldedicatedgateway.ServiceSizeCosmosPointDOneSixs), + }, false), + }, + + "instance_count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 5), + }, + } +} + +func (r CosmosDbSqlDedicatedGatewayResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r CosmosDbSqlDedicatedGatewayResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model CosmosDbSqlDedicatedGatewayModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.Cosmos.SqlDedicatedGatewayClient + cosmosdbAccountId, err := cosmosdb.ParseDatabaseAccountID(model.CosmosDbAccountId) + if err != nil { + return err + } + + id := sqldedicatedgateway.NewServiceID(cosmosdbAccountId.SubscriptionId, cosmosdbAccountId.ResourceGroupName, cosmosdbAccountId.AccountName, string(sqldedicatedgateway.ServiceTypeSqlDedicatedGateway)) + existing, err := client.ServiceGet(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + serviceType := sqldedicatedgateway.ServiceTypeSqlDedicatedGateway + + parameters := &sqldedicatedgateway.ServiceResourceCreateUpdateParameters{ + Properties: &sqldedicatedgateway.ServiceResourceCreateUpdateProperties{ + ServiceType: &serviceType, + InstanceCount: &model.InstanceCount, + InstanceSize: &model.InstanceSize, + }, + } + + if err := client.ServiceCreateThenPoll(ctx, id, *parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r CosmosDbSqlDedicatedGatewayResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Cosmos.SqlDedicatedGatewayClient + + id, err := sqldedicatedgateway.ParseServiceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model CosmosDbSqlDedicatedGatewayModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.ServiceGet(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := resp.Model + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + serviceType := sqldedicatedgateway.ServiceTypeSqlDedicatedGateway + + parameters := &sqldedicatedgateway.ServiceResourceCreateUpdateParameters{ + Properties: &sqldedicatedgateway.ServiceResourceCreateUpdateProperties{ + ServiceType: &serviceType, + InstanceCount: &model.InstanceCount, + InstanceSize: &model.InstanceSize, + }, + } + + if err := client.ServiceCreateThenPoll(ctx, *id, *parameters); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r CosmosDbSqlDedicatedGatewayResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Cosmos.SqlDedicatedGatewayClient + + id, err := sqldedicatedgateway.ParseServiceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.ServiceGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + state := CosmosDbSqlDedicatedGatewayModel{ + CosmosDbAccountId: cosmosdb.NewDatabaseAccountID(id.SubscriptionId, id.ResourceGroupName, id.AccountName).ID(), + } + + if props := model.Properties; props != nil { + existing := props.(sqldedicatedgateway.SqlDedicatedGatewayServiceResourceProperties) + + if existing.InstanceCount != nil { + state.InstanceCount = *existing.InstanceCount + } + + if existing.InstanceSize != nil { + state.InstanceSize = *existing.InstanceSize + } + } + + return metadata.Encode(&state) + }, + } +} + +func (r CosmosDbSqlDedicatedGatewayResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Cosmos.SqlDedicatedGatewayClient + + id, err := sqldedicatedgateway.ParseServiceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if err := client.ServiceDeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} diff --git a/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource_test.go b/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource_test.go new file mode 100644 index 000000000000..0e3ccbce5bb3 --- /dev/null +++ b/internal/services/cosmos/cosmosdb_sql_dedicated_gateway_resource_test.go @@ -0,0 +1,158 @@ +package cosmos_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2022-05-15/sqldedicatedgateway" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type CosmosDbSqlDedicatedGatewayResource struct{} + +func TestAccCosmosDbSqlDedicatedGateway_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_dedicated_gateway", "test") + r := CosmosDbSqlDedicatedGatewayResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCosmosDbSqlDedicatedGateway_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_dedicated_gateway", "test") + r := CosmosDbSqlDedicatedGatewayResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccCosmosDbSqlDedicatedGateway_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_sql_dedicated_gateway", "test") + r := CosmosDbSqlDedicatedGatewayResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r CosmosDbSqlDedicatedGatewayResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := sqldedicatedgateway.ParseServiceID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Cosmos.SqlDedicatedGatewayClient + resp, err := client.ServiceGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (r CosmosDbSqlDedicatedGatewayResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%d" + location = "%s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + + consistency_policy { + consistency_level = "BoundedStaleness" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (r CosmosDbSqlDedicatedGatewayResource) basic(data acceptance.TestData) string { + template := r.template(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_cosmosdb_sql_dedicated_gateway" "test" { + cosmosdb_account_id = azurerm_cosmosdb_account.test.id + instance_size = "Cosmos.D4s" + instance_count = 1 +} +`, template) +} + +func (r CosmosDbSqlDedicatedGatewayResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_cosmosdb_sql_dedicated_gateway" "import" { + cosmosdb_account_id = azurerm_cosmosdb_sql_dedicated_gateway.test.cosmosdb_account_id + instance_count = azurerm_cosmosdb_sql_dedicated_gateway.test.instance_count + instance_size = azurerm_cosmosdb_sql_dedicated_gateway.test.instance_size +} +`, config) +} + +func (r CosmosDbSqlDedicatedGatewayResource) update(data acceptance.TestData) string { + template := r.template(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_cosmosdb_sql_dedicated_gateway" "test" { + cosmosdb_account_id = azurerm_cosmosdb_account.test.id + instance_size = "Cosmos.D4s" + instance_count = 2 +} +`, template) +} diff --git a/internal/services/cosmos/registration.go b/internal/services/cosmos/registration.go index 92cca6ee1851..7e19826f18f1 100644 --- a/internal/services/cosmos/registration.go +++ b/internal/services/cosmos/registration.go @@ -7,12 +7,25 @@ import ( type Registration struct{} -var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +var ( + _ sdk.TypedServiceRegistration = Registration{} + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +) func (r Registration) AssociatedGitHubLabel() string { return "service/cosmosdb" } +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{} +} + +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + CosmosDbSqlDedicatedGatewayResource{}, + } +} + // Name is the name of this Service func (r Registration) Name() string { return "CosmosDB" diff --git a/internal/services/costmanagement/client/client.go b/internal/services/costmanagement/client/client.go index 7aad7f59b174..a72e8e4735eb 100644 --- a/internal/services/costmanagement/client/client.go +++ b/internal/services/costmanagement/client/client.go @@ -1,16 +1,16 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/costmanagement/mgmt/2020-06-01/costmanagement" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2021-10-01/exports" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - ExportClient *costmanagement.ExportsClient + ExportClient *exports.ExportsClient } func NewClient(o *common.ClientOptions) *Client { - ExportClient := costmanagement.NewExportsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + ExportClient := exports.NewExportsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&ExportClient.Client, o.ResourceManagerAuthorizer) return &Client{ diff --git a/internal/services/costmanagement/export_base_resource.go b/internal/services/costmanagement/export_base_resource.go index cb8ea970ab31..5595512d9900 100644 --- a/internal/services/costmanagement/export_base_resource.go +++ b/internal/services/costmanagement/export_base_resource.go @@ -5,11 +5,10 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/costmanagement/mgmt/2020-06-01/costmanagement" - "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2021-10-01/exports" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/parse" storageParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -31,10 +30,10 @@ func (br costManagementExportBaseResource) arguments(fields map[string]*pluginsd Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(costmanagement.RecurrenceTypeDaily), - string(costmanagement.RecurrenceTypeWeekly), - string(costmanagement.RecurrenceTypeMonthly), - string(costmanagement.RecurrenceTypeAnnually), + string(exports.RecurrenceTypeDaily), + string(exports.RecurrenceTypeWeekly), + string(exports.RecurrenceTypeMonthly), + string(exports.RecurrenceTypeAnnually), }, false), }, @@ -82,9 +81,9 @@ func (br costManagementExportBaseResource) arguments(fields map[string]*pluginsd Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(costmanagement.ExportTypeActualCost), - string(costmanagement.ExportTypeAmortizedCost), - string(costmanagement.ExportTypeUsage), + string(exports.ExportTypeActualCost), + string(exports.ExportTypeAmortizedCost), + string(exports.ExportTypeUsage), }, false), }, @@ -92,12 +91,12 @@ func (br costManagementExportBaseResource) arguments(fields map[string]*pluginsd Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(costmanagement.Custom), - string(costmanagement.BillingMonthToDate), - string(costmanagement.TheLastBillingMonth), - string(costmanagement.TheLastMonth), - string(costmanagement.WeekToDate), - string(costmanagement.MonthToDate), + string(exports.TimeframeTypeCustom), + string(exports.TimeframeTypeBillingMonthToDate), + string(exports.TimeframeTypeTheLastBillingMonth), + string(exports.TimeframeTypeTheLastMonth), + string(exports.TimeframeTypeWeekToDate), + string(exports.TimeframeTypeMonthToDate), }, false), }, }, @@ -121,15 +120,16 @@ func (br costManagementExportBaseResource) createFunc(resourceName, scopeFieldNa Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.CostManagement.ExportClient - id := parse.NewCostManagementExportId(metadata.ResourceData.Get(scopeFieldName).(string), metadata.ResourceData.Get("name").(string)) - existing, err := client.Get(ctx, id.Scope, id.Name, "") + id := exports.NewScopedExportID(metadata.ResourceData.Get(scopeFieldName).(string), metadata.ResourceData.Get("name").(string)) + var opts exports.GetOperationOptions + existing, err := client.Get(ctx, id, opts) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError(resourceName, id.ID()) } @@ -149,46 +149,48 @@ func (br costManagementExportBaseResource) readFunc(scopeFieldName string) sdk.R Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.CostManagement.ExportClient - id, err := parse.CostManagementExportID(metadata.ResourceData.Id()) + id, err := exports.ParseScopedExportID(metadata.ResourceData.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.Scope, id.Name, "") + var opts exports.GetOperationOptions + resp, err := client.Get(ctx, *id, opts) if err != nil { - if !utils.ResponseWasNotFound(resp.Response) { + if !response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } return fmt.Errorf("reading %s: %+v", *id, err) } - metadata.ResourceData.Set("name", id.Name) + metadata.ResourceData.Set("name", id.ExportName) //lintignore:R001 metadata.ResourceData.Set(scopeFieldName, id.Scope) - if schedule := resp.Schedule; schedule != nil { - if recurrencePeriod := schedule.RecurrencePeriod; recurrencePeriod != nil { - metadata.ResourceData.Set("recurrence_period_start_date", recurrencePeriod.From.Format(time.RFC3339)) - metadata.ResourceData.Set("recurrence_period_end_date", recurrencePeriod.To.Format(time.RFC3339)) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if schedule := props.Schedule; schedule != nil { + if recurrencePeriod := schedule.RecurrencePeriod; recurrencePeriod != nil { + metadata.ResourceData.Set("recurrence_period_start_date", recurrencePeriod.From) + metadata.ResourceData.Set("recurrence_period_end_date", recurrencePeriod.To) + } + status := *schedule.Status == exports.StatusTypeActive + + metadata.ResourceData.Set("active", status) + metadata.ResourceData.Set("recurrence_type", schedule.Recurrence) + } + + exportDeliveryInfo, err := flattenExportDataStorageLocation(&props.DeliveryInfo) + if err != nil { + return fmt.Errorf("flattening `export_data_storage_location`: %+v", err) + } + if err := metadata.ResourceData.Set("export_data_storage_location", exportDeliveryInfo); err != nil { + return fmt.Errorf("setting `export_data_storage_location`: %+v", err) + } + if err := metadata.ResourceData.Set("export_data_options", flattenExportDefinition(&props.Definition)); err != nil { + return fmt.Errorf("setting `export_data_options`: %+v", err) + } } - - status := schedule.Status == costmanagement.Active - - metadata.ResourceData.Set("active", status) - metadata.ResourceData.Set("recurrence_type", schedule.Recurrence) - } - - exportDeliveryInfo, err := flattenExportDataStorageLocation(resp.DeliveryInfo) - if err != nil { - return fmt.Errorf("flattening `export_data_storage_location`: %+v", err) - } - - if err := metadata.ResourceData.Set("export_data_storage_location", exportDeliveryInfo); err != nil { - return fmt.Errorf("setting `export_data_storage_location`: %+v", err) - } - - if err := metadata.ResourceData.Set("export_data_options", flattenExportDefinition(resp.Definition)); err != nil { - return fmt.Errorf("setting `export_data_options`: %+v", err) } return nil @@ -202,12 +204,12 @@ func (br costManagementExportBaseResource) deleteFunc() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.CostManagement.ExportClient - id, err := parse.CostManagementExportID(metadata.ResourceData.Id()) + id, err := exports.ParseScopedExportID(metadata.ResourceData.Id()) if err != nil { return err } - if _, err = client.Delete(ctx, id.Scope, id.Name); err != nil { + if _, err = client.Delete(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } @@ -222,20 +224,25 @@ func (br costManagementExportBaseResource) updateFunc() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.CostManagement.ExportClient - id, err := parse.CostManagementExportID(metadata.ResourceData.Id()) + id, err := exports.ParseScopedExportID(metadata.ResourceData.Id()) if err != nil { return err } // Update operation requires latest eTag to be set in the request. - resp, err := client.Get(ctx, id.Scope, id.Name, "") + var opts exports.GetOperationOptions + resp, err := client.Get(ctx, *id, opts) if err != nil { return fmt.Errorf("reading %s: %+v", *id, err) } - if resp.ETag == nil { - return fmt.Errorf("add %s: etag was nil", *id) + + if model := resp.Model; model != nil { + if model.ETag == nil { + return fmt.Errorf("add %s: etag was nil", *id) + } } - if err := createOrUpdateCostManagementExport(ctx, client, metadata, *id, resp.ETag); err != nil { + + if err := createOrUpdateCostManagementExport(ctx, client, metadata, *id, resp.Model.ETag); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } @@ -244,13 +251,10 @@ func (br costManagementExportBaseResource) updateFunc() sdk.ResourceFunc { } } -func createOrUpdateCostManagementExport(ctx context.Context, client *costmanagement.ExportsClient, metadata sdk.ResourceMetaData, id parse.CostManagementExportId, etag *string) error { - from, _ := time.Parse(time.RFC3339, metadata.ResourceData.Get("recurrence_period_start_date").(string)) - to, _ := time.Parse(time.RFC3339, metadata.ResourceData.Get("recurrence_period_end_date").(string)) - - status := costmanagement.Active +func createOrUpdateCostManagementExport(ctx context.Context, client *exports.ExportsClient, metadata sdk.ResourceMetaData, id exports.ScopedExportId, etag *string) error { + status := exports.StatusTypeActive if v := metadata.ResourceData.Get("active"); !v.(bool) { - status = costmanagement.Inactive + status = exports.StatusTypeInactive } deliveryInfo, err := expandExportDataStorageLocation(metadata.ResourceData.Get("export_data_storage_location").([]interface{})) @@ -258,29 +262,31 @@ func createOrUpdateCostManagementExport(ctx context.Context, client *costmanagem return fmt.Errorf("expanding `export_data_storage_location`: %+v", err) } - props := costmanagement.Export{ + format := exports.FormatTypeCsv + recurrenceType := exports.RecurrenceType(metadata.ResourceData.Get("recurrence_type").(string)) + props := exports.Export{ ETag: etag, - ExportProperties: &costmanagement.ExportProperties{ - Schedule: &costmanagement.ExportSchedule{ - Recurrence: costmanagement.RecurrenceType(metadata.ResourceData.Get("recurrence_type").(string)), - RecurrencePeriod: &costmanagement.ExportRecurrencePeriod{ - From: &date.Time{Time: from}, - To: &date.Time{Time: to}, + Properties: &exports.ExportProperties{ + Schedule: &exports.ExportSchedule{ + Recurrence: &recurrenceType, + RecurrencePeriod: &exports.ExportRecurrencePeriod{ + From: metadata.ResourceData.Get("recurrence_period_start_date").(string), + To: utils.String(metadata.ResourceData.Get("recurrence_period_end_date").(string)), }, - Status: status, + Status: &status, }, - DeliveryInfo: deliveryInfo, - Format: costmanagement.Csv, - Definition: expandExportDefinition(metadata.ResourceData.Get("export_data_options").([]interface{})), + DeliveryInfo: *deliveryInfo, + Format: &format, + Definition: *expandExportDefinition(metadata.ResourceData.Get("export_data_options").([]interface{})), }, } - _, err = client.CreateOrUpdate(ctx, id.Scope, id.Name, props) + _, err = client.CreateOrUpdate(ctx, id, props) return err } -func expandExportDataStorageLocation(input []interface{}) (*costmanagement.ExportDeliveryInfo, error) { +func expandExportDataStorageLocation(input []interface{}) (*exports.ExportDeliveryInfo, error) { if len(input) == 0 || input[0] == nil { return nil, nil } @@ -293,10 +299,10 @@ func expandExportDataStorageLocation(input []interface{}) (*costmanagement.Expor storageId := storageParse.NewStorageAccountID(containerId.SubscriptionId, containerId.ResourceGroup, containerId.StorageAccountName) - deliveryInfo := &costmanagement.ExportDeliveryInfo{ - Destination: &costmanagement.ExportDeliveryDestination{ - ResourceID: utils.String(storageId.ID()), - Container: utils.String(containerId.ContainerName), + deliveryInfo := &exports.ExportDeliveryInfo{ + Destination: exports.ExportDeliveryDestination{ + ResourceId: utils.String(storageId.ID()), + Container: containerId.ContainerName, RootFolderPath: utils.String(attrs["root_folder_path"].(string)), }, } @@ -304,22 +310,22 @@ func expandExportDataStorageLocation(input []interface{}) (*costmanagement.Expor return deliveryInfo, nil } -func expandExportDefinition(input []interface{}) *costmanagement.ExportDefinition { +func expandExportDefinition(input []interface{}) *exports.ExportDefinition { if len(input) == 0 || input[0] == nil { return nil } attrs := input[0].(map[string]interface{}) - definitionInfo := &costmanagement.ExportDefinition{ - Type: costmanagement.ExportType(attrs["type"].(string)), - Timeframe: costmanagement.TimeframeType(attrs["time_frame"].(string)), + definitionInfo := &exports.ExportDefinition{ + Type: exports.ExportType(attrs["type"].(string)), + Timeframe: exports.TimeframeType(attrs["time_frame"].(string)), } return definitionInfo } -func flattenExportDataStorageLocation(input *costmanagement.ExportDeliveryInfo) ([]interface{}, error) { - if input == nil || input.Destination == nil { +func flattenExportDataStorageLocation(input *exports.ExportDeliveryInfo) ([]interface{}, error) { + if input == nil { return []interface{}{}, nil } @@ -327,7 +333,7 @@ func flattenExportDataStorageLocation(input *costmanagement.ExportDeliveryInfo) var err error var storageAccountId *storageParse.StorageAccountId - if v := destination.ResourceID; v != nil { + if v := destination.ResourceId; v != nil { storageAccountId, err = storageParse.StorageAccountID(*v) if err != nil { return nil, err @@ -335,8 +341,8 @@ func flattenExportDataStorageLocation(input *costmanagement.ExportDeliveryInfo) } containerId := "" - if v := destination.Container; v != nil && storageAccountId != nil { - containerId = storageParse.NewStorageContainerResourceManagerID(storageAccountId.SubscriptionId, storageAccountId.ResourceGroup, storageAccountId.Name, "default", *v).ID() + if v := destination.Container; v != "" && storageAccountId != nil { + containerId = storageParse.NewStorageContainerResourceManagerID(storageAccountId.SubscriptionId, storageAccountId.ResourceGroup, storageAccountId.Name, "default", v).ID() } rootFolderPath := "" @@ -352,7 +358,7 @@ func flattenExportDataStorageLocation(input *costmanagement.ExportDeliveryInfo) }, nil } -func flattenExportDefinition(input *costmanagement.ExportDefinition) []interface{} { +func flattenExportDefinition(input *exports.ExportDefinition) []interface{} { if input == nil { return []interface{}{} } diff --git a/internal/services/costmanagement/export_resource_group_resource_test.go b/internal/services/costmanagement/export_resource_group_resource_test.go index ef412cef48f6..650a40b50852 100644 --- a/internal/services/costmanagement/export_resource_group_resource_test.go +++ b/internal/services/costmanagement/export_resource_group_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2021-10-01/exports" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -79,17 +79,18 @@ func TestAccResourceGroupCostManagementExport_requiresImport(t *testing.T) { } func (t ResourceGroupCostManagementExport) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.CostManagementExportID(state.ID) + id, err := exports.ParseScopedExportID(state.ID) if err != nil { return nil, err } - resp, err := clients.CostManagement.ExportClient.Get(ctx, id.Scope, id.Name, "") + var opts exports.GetOperationOptions + resp, err := clients.CostManagement.ExportClient.Get(ctx, *id, opts) if err != nil { return nil, fmt.Errorf("retrieving (%s): %+v", *id, err) } - return utils.Bool(resp.ExportProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (ResourceGroupCostManagementExport) basic(data acceptance.TestData) string { diff --git a/internal/services/costmanagement/export_subscription_resource_test.go b/internal/services/costmanagement/export_subscription_resource_test.go index f4cd5e6460ca..905ab865b551 100644 --- a/internal/services/costmanagement/export_subscription_resource_test.go +++ b/internal/services/costmanagement/export_subscription_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2021-10-01/exports" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -79,17 +79,18 @@ func TestAccSubscriptionCostManagementExport_requiresImport(t *testing.T) { } func (t SubscriptionCostManagementExport) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.CostManagementExportID(state.ID) + id, err := exports.ParseScopedExportID(state.ID) if err != nil { return nil, err } - resp, err := clients.CostManagement.ExportClient.Get(ctx, id.Scope, id.Name, "") + var opts exports.GetOperationOptions + resp, err := clients.CostManagement.ExportClient.Get(ctx, *id, opts) if err != nil { return nil, fmt.Errorf("retrieving (%s): %+v", *id, err) } - return utils.Bool(resp.ExportProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (SubscriptionCostManagementExport) basic(data acceptance.TestData) string { diff --git a/internal/services/dashboard/client/client.go b/internal/services/dashboard/client/client.go new file mode 100644 index 000000000000..e6ade515621c --- /dev/null +++ b/internal/services/dashboard/client/client.go @@ -0,0 +1,20 @@ +package client + +import ( + "github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2022-08-01/grafanaresource" + "github.com/hashicorp/terraform-provider-azurerm/internal/common" +) + +type Client struct { + GrafanaResourceClient *grafanaresource.GrafanaResourceClient +} + +func NewClient(o *common.ClientOptions) *Client { + + grafanaResourceClient := grafanaresource.NewGrafanaResourceClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&grafanaResourceClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + GrafanaResourceClient: &grafanaResourceClient, + } +} diff --git a/internal/services/dashboard/dashboard_grafana_resource.go b/internal/services/dashboard/dashboard_grafana_resource.go new file mode 100644 index 000000000000..3ae860e973da --- /dev/null +++ b/internal/services/dashboard/dashboard_grafana_resource.go @@ -0,0 +1,433 @@ +package dashboard + +import ( + "context" + "fmt" + "regexp" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + + "github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2022-08-01/grafanaresource" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DashboardGrafanaModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + ApiKeyEnabled bool `tfschema:"api_key_enabled"` + AutoGeneratedDomainNameLabelScope grafanaresource.AutoGeneratedDomainNameLabelScope `tfschema:"auto_generated_domain_name_label_scope"` + DeterministicOutboundIPEnabled bool `tfschema:"deterministic_outbound_ip_enabled"` + Location string `tfschema:"location"` + PublicNetworkAccessEnabled bool `tfschema:"public_network_access_enabled"` + Sku string `tfschema:"sku"` + Tags map[string]string `tfschema:"tags"` + ZoneRedundancyEnabled bool `tfschema:"zone_redundancy_enabled"` + Endpoint string `tfschema:"endpoint"` + GrafanaVersion string `tfschema:"grafana_version"` + OutboundIPs []string `tfschema:"outbound_ip"` +} + +type DashboardGrafanaResource struct{} + +var _ sdk.ResourceWithUpdate = DashboardGrafanaResource{} + +func (r DashboardGrafanaResource) ResourceType() string { + return "azurerm_dashboard_grafana" +} + +func (r DashboardGrafanaResource) ModelObject() interface{} { + return &DashboardGrafanaModel{} +} + +func (r DashboardGrafanaResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return grafanaresource.ValidateGrafanaID +} + +func (r DashboardGrafanaResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[a-zA-Z][-a-zA-Z\d]{0,21}[a-zA-Z\d]$`), + `The name length must be from 2 to 23 characters. The name can only contain letters, numbers and dashes, and it must begin with a letter and end with a letter or digit.`, + ), + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "api_key_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "auto_generated_domain_name_label_scope": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(grafanaresource.AutoGeneratedDomainNameLabelScopeTenantReuse), + ValidateFunc: validation.StringInSlice([]string{ + string(grafanaresource.AutoGeneratedDomainNameLabelScopeTenantReuse), + }, false), + }, + + "deterministic_outbound_ip_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "identity": commonschema.SystemAssignedIdentityOptionalForceNew(), + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "sku": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Default: "Standard", + ValidateFunc: validation.StringInSlice([]string{ + "Standard", + }, false), + }, + + "tags": commonschema.Tags(), + + "zone_redundancy_enabled": { + Type: pluginsdk.TypeBool, + ForceNew: true, + Optional: true, + Default: false, + }, + } +} + +func (r DashboardGrafanaResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "grafana_version": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "outbound_ip": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + } +} + +func (r DashboardGrafanaResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model DashboardGrafanaModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.Dashboard.GrafanaResourceClient + subscriptionId := metadata.Client.Account.SubscriptionId + id := grafanaresource.NewGrafanaID(subscriptionId, model.ResourceGroupName, model.Name) + existing, err := client.GrafanaGet(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + identityValue, err := expandLegacySystemAndUserAssignedMap(metadata.ResourceData.Get("identity").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + apiKey := grafanaresource.ApiKeyDisabled + if model.ApiKeyEnabled { + apiKey = grafanaresource.ApiKeyEnabled + } + + deterministicOutboundIP := grafanaresource.DeterministicOutboundIPDisabled + if model.DeterministicOutboundIPEnabled { + deterministicOutboundIP = grafanaresource.DeterministicOutboundIPEnabled + } + + publicNetworkAccess := grafanaresource.PublicNetworkAccessDisabled + if model.PublicNetworkAccessEnabled { + publicNetworkAccess = grafanaresource.PublicNetworkAccessEnabled + } + + zoneRedundancy := grafanaresource.ZoneRedundancyDisabled + if model.ZoneRedundancyEnabled { + zoneRedundancy = grafanaresource.ZoneRedundancyEnabled + } + + properties := &grafanaresource.ManagedGrafana{ + Identity: identityValue, + Location: utils.String(location.Normalize(model.Location)), + Properties: &grafanaresource.ManagedGrafanaProperties{ + ApiKey: &apiKey, + AutoGeneratedDomainNameLabelScope: &model.AutoGeneratedDomainNameLabelScope, + DeterministicOutboundIP: &deterministicOutboundIP, + PublicNetworkAccess: &publicNetworkAccess, + ZoneRedundancy: &zoneRedundancy, + }, + Sku: &grafanaresource.ResourceSku{ + Name: model.Sku, + }, + Tags: &model.Tags, + } + + if err := client.GrafanaCreateThenPoll(ctx, id, *properties); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r DashboardGrafanaResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dashboard.GrafanaResourceClient + + id, err := grafanaresource.ParseGrafanaID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model DashboardGrafanaModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.GrafanaGet(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := resp.Model + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + if metadata.ResourceData.HasChange("api_key_enabled") { + apiKey := grafanaresource.ApiKeyDisabled + if model.ApiKeyEnabled { + apiKey = grafanaresource.ApiKeyEnabled + } + + properties.Properties.ApiKey = &apiKey + } + + if metadata.ResourceData.HasChange("auto_generated_domain_name_label_scope") { + properties.Properties.AutoGeneratedDomainNameLabelScope = &model.AutoGeneratedDomainNameLabelScope + } + + if metadata.ResourceData.HasChange("deterministic_outbound_ip_enabled") { + deterministicOutboundIP := grafanaresource.DeterministicOutboundIPDisabled + if model.DeterministicOutboundIPEnabled { + deterministicOutboundIP = grafanaresource.DeterministicOutboundIPEnabled + } + + properties.Properties.DeterministicOutboundIP = &deterministicOutboundIP + } + + if metadata.ResourceData.HasChange("public_network_access_enabled") { + publicNetworkAccess := grafanaresource.PublicNetworkAccessDisabled + if model.PublicNetworkAccessEnabled { + publicNetworkAccess = grafanaresource.PublicNetworkAccessEnabled + } + + properties.Properties.PublicNetworkAccess = &publicNetworkAccess + } + + properties.SystemData = nil + + if metadata.ResourceData.HasChange("tags") { + properties.Tags = &model.Tags + } + + if err := client.GrafanaCreateThenPoll(ctx, *id, *properties); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r DashboardGrafanaResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dashboard.GrafanaResourceClient + + id, err := grafanaresource.ParseGrafanaID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.GrafanaGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + state := DashboardGrafanaModel{ + Name: id.WorkspaceName, + ResourceGroupName: id.ResourceGroupName, + Location: location.NormalizeNilable(model.Location), + } + + identityValue, err := flattenLegacySystemAndUserAssignedMap(model.Identity) + if err != nil { + return fmt.Errorf("flattening `identity`: %+v", err) + } + + if err := metadata.ResourceData.Set("identity", identityValue); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if properties := model.Properties; properties != nil { + if properties.ApiKey != nil { + if *properties.ApiKey == grafanaresource.ApiKeyEnabled { + state.ApiKeyEnabled = true + } else { + state.ApiKeyEnabled = false + } + } + + if properties.AutoGeneratedDomainNameLabelScope != nil { + state.AutoGeneratedDomainNameLabelScope = *properties.AutoGeneratedDomainNameLabelScope + } + + if properties.DeterministicOutboundIP != nil { + if *properties.DeterministicOutboundIP == grafanaresource.DeterministicOutboundIPEnabled { + state.DeterministicOutboundIPEnabled = true + } else { + state.DeterministicOutboundIPEnabled = false + } + } + + if properties.Endpoint != nil { + state.Endpoint = *properties.Endpoint + } + + if properties.GrafanaVersion != nil { + state.GrafanaVersion = *properties.GrafanaVersion + } + + if properties.OutboundIPs != nil { + state.OutboundIPs = *properties.OutboundIPs + } + + if properties.PublicNetworkAccess != nil { + if *properties.PublicNetworkAccess == grafanaresource.PublicNetworkAccessEnabled { + state.PublicNetworkAccessEnabled = true + } else { + state.PublicNetworkAccessEnabled = false + } + } + + if properties.ZoneRedundancy != nil { + if *properties.ZoneRedundancy == grafanaresource.ZoneRedundancyEnabled { + state.ZoneRedundancyEnabled = true + } else { + state.ZoneRedundancyEnabled = false + } + } + } + + if model.Sku != nil { + state.Sku = model.Sku.Name + } + + if model.Tags != nil { + state.Tags = *model.Tags + } + + return metadata.Encode(&state) + }, + } +} + +func (r DashboardGrafanaResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dashboard.GrafanaResourceClient + + id, err := grafanaresource.ParseGrafanaID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if err := client.GrafanaDeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} + +func expandLegacySystemAndUserAssignedMap(input []interface{}) (*identity.LegacySystemAndUserAssignedMap, error) { + identityValue, err := identity.ExpandSystemAssigned(input) + if err != nil { + return nil, err + } + + return &identity.LegacySystemAndUserAssignedMap{ + Type: identityValue.Type, + }, nil +} + +func flattenLegacySystemAndUserAssignedMap(input *identity.LegacySystemAndUserAssignedMap) (*[]interface{}, error) { + if input == nil { + return &[]interface{}{}, nil + } + + identityValue := &identity.SystemAssigned{ + Type: input.Type, + PrincipalId: input.PrincipalId, + TenantId: input.TenantId, + } + + output := identity.FlattenSystemAssigned(identityValue) + return &output, nil +} diff --git a/internal/services/dashboard/dashboard_grafana_resource_test.go b/internal/services/dashboard/dashboard_grafana_resource_test.go new file mode 100644 index 000000000000..775f0fa9466e --- /dev/null +++ b/internal/services/dashboard/dashboard_grafana_resource_test.go @@ -0,0 +1,182 @@ +package dashboard_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2022-08-01/grafanaresource" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DashboardGrafanaResource struct{} + +func TestAccDashboardGrafana_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dashboard_grafana", "test") + r := DashboardGrafanaResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDashboardGrafana_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dashboard_grafana", "test") + r := DashboardGrafanaResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDashboardGrafana_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dashboard_grafana", "test") + r := DashboardGrafanaResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDashboardGrafana_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dashboard_grafana", "test") + r := DashboardGrafanaResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r DashboardGrafanaResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := grafanaresource.ParseGrafanaID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Dashboard.GrafanaResourceClient + resp, err := client.GrafanaGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (r DashboardGrafanaResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%d" + location = "%s" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r DashboardGrafanaResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_dashboard_grafana" "test" { + name = "a-dg-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} +`, template, data.RandomInteger) +} + +func (r DashboardGrafanaResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` + %s + +resource "azurerm_dashboard_grafana" "import" { + name = azurerm_dashboard_grafana.test.name + resource_group_name = azurerm_dashboard_grafana.test.resource_group_name + location = azurerm_dashboard_grafana.test.location +} +`, config) +} + +func (r DashboardGrafanaResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_dashboard_grafana" "test" { + name = "a-dg-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + api_key_enabled = true + deterministic_outbound_ip_enabled = true + public_network_access_enabled = false + + identity { + type = "SystemAssigned" + } + + tags = { + key = "value" + } +} +`, template, data.RandomInteger) +} + +func (r DashboardGrafanaResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_dashboard_grafana" "test" { + name = "a-dg-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + identity { + type = "SystemAssigned" + } + + tags = { + key2 = "value2" + } +} +`, template, data.RandomInteger) +} diff --git a/internal/services/dashboard/registration.go b/internal/services/dashboard/registration.go new file mode 100644 index 000000000000..8ee47c4e3879 --- /dev/null +++ b/internal/services/dashboard/registration.go @@ -0,0 +1,51 @@ +package dashboard + +import ( + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type Registration struct{} + +var ( + _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{} + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +) + +func (r Registration) AssociatedGitHubLabel() string { + return "service/dashboard" +} + +// Name is the name of this Service +func (r Registration) Name() string { + return "Dashboard" +} + +// WebsiteCategories returns a list of categories which can be used for the sidebar +func (r Registration) WebsiteCategories() []string { + return []string{ + "Dashboard", + } +} + +// SupportedDataSources returns the supported Data Sources supported by this Service +func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { + return map[string]*pluginsdk.Resource{} +} + +// SupportedResources returns the supported Resources supported by this Service +func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { + return map[string]*pluginsdk.Resource{} +} + +// DataSources returns a list of Data Sources supported by this Service +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{} +} + +// Resources returns a list of Resources supported by this Service +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + DashboardGrafanaResource{}, + } +} diff --git a/internal/services/databricks/databricks_workspace_resource.go b/internal/services/databricks/databricks_workspace_resource.go index 3a930d0e060a..01284e50000b 100644 --- a/internal/services/databricks/databricks_workspace_resource.go +++ b/internal/services/databricks/databricks_workspace_resource.go @@ -684,7 +684,7 @@ func flattenWorkspaceCustomParameters(input *workspaces.WorkspaceCustomParameter parameters["nat_gateway_name"] = v.Value } - if v := input.PublicIpName; v != nil { + if v := input.PublicIPName; v != nil { parameters["public_ip_name"] = v.Value } @@ -704,7 +704,7 @@ func flattenWorkspaceCustomParameters(input *workspaces.WorkspaceCustomParameter parameters["machine_learning_workspace_id"] = v.Value } - if v := input.EnableNoPublicIp; v != nil { + if v := input.EnableNoPublicIP; v != nil { parameters["no_public_ip"] = v.Value } @@ -779,7 +779,7 @@ func expandWorkspaceCustomParameters(input []interface{}, customerManagedKeyEnab } if v, ok := config["public_ip_name"].(string); ok && v != "" { - parameters.PublicIpName = &workspaces.WorkspaceCustomStringParameter{ + parameters.PublicIPName = &workspaces.WorkspaceCustomStringParameter{ Value: v, } } @@ -809,7 +809,7 @@ func expandWorkspaceCustomParameters(input []interface{}, customerManagedKeyEnab } if v, ok := config["no_public_ip"].(bool); ok { - parameters.EnableNoPublicIp = &workspaces.WorkspaceCustomBooleanParameter{ + parameters.EnableNoPublicIP = &workspaces.WorkspaceCustomBooleanParameter{ Value: v, } } diff --git a/internal/services/datadog/client/client.go b/internal/services/datadog/client/client.go new file mode 100644 index 000000000000..8cc3beea0e73 --- /dev/null +++ b/internal/services/datadog/client/client.go @@ -0,0 +1,19 @@ +package client + +import ( + "github.com/Azure/azure-sdk-for-go/services/datadog/mgmt/2021-03-01/datadog" + "github.com/hashicorp/terraform-provider-azurerm/internal/common" +) + +type Client struct { + MonitorsClient *datadog.MonitorsClient +} + +func NewClient(o *common.ClientOptions) *Client { + monitorsClient := datadog.NewMonitorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&monitorsClient.Client, o.ResourceManagerAuthorizer) + + return &Client{ + MonitorsClient: &monitorsClient, + } +} diff --git a/internal/services/datadog/datadog_monitors_resource.go b/internal/services/datadog/datadog_monitors_resource.go new file mode 100644 index 000000000000..9c38057eaa02 --- /dev/null +++ b/internal/services/datadog/datadog_monitors_resource.go @@ -0,0 +1,429 @@ +package datadog + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/datadog/mgmt/2021-03-01/datadog" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceDatadogMonitor() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceDatadogMonitorCreate, + Read: resourceDatadogMonitorRead, + Update: resourceDatadogMonitorUpdate, + Delete: resourceDatadogMonitorDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.DatadogMonitorID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DatadogMonitorsName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "datadog_organization": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "api_key": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + }, + + "application_key": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + }, + + "enterprise_app_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + }, + + "linking_auth_code": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + }, + + "linking_client_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + }, + + "redirect_uri": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + }, + + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "identity": commonschema.SystemAssignedIdentityOptional(), + + "sku_name": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: SkuNameDiffSuppress, + }, + + "user": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DatadogUsersName, + }, + + "email": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DatadogMonitorsEmailAddress, + }, + + "phone_number": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.DatadogMonitorsPhoneNumber, + }, + }, + }, + }, + + "monitoring_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "marketplace_subscription_status": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceDatadogMonitorCreate(d *pluginsdk.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).Datadog.MonitorsClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + id := parse.NewDatadogMonitorID(subscriptionId, resourceGroup, name) + + existing, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_datadog_monitor", id.ID()) + } + + monitoringStatus := datadog.MonitoringStatusDisabled + if d.Get("monitoring_enabled").(bool) { + monitoringStatus = datadog.MonitoringStatusEnabled + } + + body := datadog.MonitorResource{ + Location: utils.String(location.Normalize(d.Get("location").(string))), + Identity: expandMonitorIdentityProperties(d.Get("identity").([]interface{})), + Sku: &datadog.ResourceSku{ + Name: utils.String(d.Get("sku_name").(string)), + }, + Properties: &datadog.MonitorProperties{ + DatadogOrganizationProperties: expandMonitorOrganizationProperties(d.Get("datadog_organization").([]interface{})), + UserInfo: expandMonitorUserInfo(d.Get("user").([]interface{})), + MonitoringStatus: monitoringStatus, + }, + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + future, err := client.Create(ctx, resourceGroup, name, &body) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceDatadogMonitorRead(d, meta) +} + +func resourceDatadogMonitorRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Datadog.MonitorsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DatadogMonitorID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.MonitorName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Datadog monitor %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.Set("name", id.MonitorName) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("location", location.NormalizeNilable(resp.Location)) + if err := d.Set("identity", flattenMonitorIdentityProperties(resp.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + if props := resp.Properties; props != nil { + if err := d.Set("datadog_organization", flattenMonitorOrganizationProperties(props.DatadogOrganizationProperties, d)); err != nil { + return fmt.Errorf("setting `datadog_organization`: %+v", err) + } + d.Set("monitoring_enabled", props.MonitoringStatus == datadog.MonitoringStatusEnabled) + d.Set("marketplace_subscription_status", props.MarketplaceSubscriptionStatus) + } + if resp.Sku != nil { + d.Set("sku_name", *resp.Sku.Name) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceDatadogMonitorUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Datadog.MonitorsClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DatadogMonitorID(d.Id()) + if err != nil { + return err + } + + body := datadog.MonitorResourceUpdateParameters{ + Properties: &datadog.MonitorUpdateProperties{}, + } + if d.HasChange("sku_name") { + body.Sku = &datadog.ResourceSku{Name: utils.String(d.Get("sku_name").(string))} + } + if d.HasChange("monitoring_enabled") { + monitoringStatus := datadog.MonitoringStatusDisabled + if d.Get("monitoring_enabled").(bool) { + monitoringStatus = datadog.MonitoringStatusEnabled + } + body.Properties.MonitoringStatus = monitoringStatus + } + if d.HasChange("tags") { + body.Tags = tags.Expand(d.Get("tags").(map[string]interface{})) + } + + future, err := client.Update(ctx, id.ResourceGroup, id.MonitorName, &body) + if err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for updating of %s: %+v", id, err) + } + return resourceDatadogMonitorRead(d, meta) +} + +func resourceDatadogMonitorDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Datadog.MonitorsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DatadogMonitorID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.MonitorName) + if err != nil { + return fmt.Errorf("deleting of %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", id, err) + } + return nil +} + +func SkuNameDiffSuppress(_, old, new string, _ *pluginsdk.ResourceData) bool { + // During creating, accepts any sku name. + if old == "" { + return false + } + // Sku name of the datadog monitor has two kinds: + // - Concrete sku: E.g. "payg_v2_Monthly". These will be returned unchanged by API. + // - Linked sku: The value is named "Linked". This will be accepted and changed by the API, which will then return you the linked concrete sku. + if new == "Linked" { + return true + } + return old == new +} + +func expandMonitorIdentityProperties(input []interface{}) *datadog.IdentityProperties { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + return &datadog.IdentityProperties{ + Type: datadog.ManagedIdentityTypes(v["type"].(string)), + } +} + +func expandMonitorOrganizationProperties(input []interface{}) *datadog.OrganizationProperties { + if len(input) == 0 || input[0] == nil { + return nil + } + v := input[0].(map[string]interface{}) + return &datadog.OrganizationProperties{ + LinkingAuthCode: utils.String(v["linking_auth_code"].(string)), + LinkingClientID: utils.String(v["linking_client_id"].(string)), + RedirectURI: utils.String(v["redirect_uri"].(string)), + APIKey: utils.String(v["api_key"].(string)), + ApplicationKey: utils.String(v["application_key"].(string)), + EnterpriseAppID: utils.String(v["enterprise_app_id"].(string)), + } +} + +func expandMonitorUserInfo(input []interface{}) *datadog.UserInfo { + if len(input) == 0 || input[0] == nil { + return nil + } + v := input[0].(map[string]interface{}) + return &datadog.UserInfo{ + Name: utils.String(v["name"].(string)), + EmailAddress: utils.String(v["email"].(string)), + PhoneNumber: utils.String(v["phone_number"].(string)), + } +} + +func flattenMonitorIdentityProperties(input *datadog.IdentityProperties) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + var t datadog.ManagedIdentityTypes + if input.Type != "" { + t = input.Type + } + var principalId string + if input.PrincipalID != nil { + principalId = *input.PrincipalID + } + var tenantId string + if input.TenantID != nil { + tenantId = *input.TenantID + } + return []interface{}{ + map[string]interface{}{ + "type": t, + "principal_id": principalId, + "tenant_id": tenantId, + }, + } +} + +func flattenMonitorOrganizationProperties(input *datadog.OrganizationProperties, d *pluginsdk.ResourceData) []interface{} { + + organisationProperties := d.Get("datadog_organization").([]interface{}) + if len(organisationProperties) == 0 { + return make([]interface{}, 0) + } + v := organisationProperties[0].(map[string]interface{}) + + var name string + if input.Name != nil { + name = *input.Name + } + var id string + if input.ID != nil { + id = *input.ID + } + var redirectUri string + if input.RedirectURI != nil { + redirectUri = *input.RedirectURI + } + var enterpriseAppID string + if input.EnterpriseAppID != nil { + enterpriseAppID = *input.EnterpriseAppID + } + return []interface{}{ + map[string]interface{}{ + "name": name, + "api_key": utils.String(v["api_key"].(string)), + "application_key": utils.String(v["application_key"].(string)), + "enterprise_app_id": enterpriseAppID, + "linking_auth_code": utils.String(v["linking_auth_code"].(string)), + "linking_client_id": utils.String(v["linking_client_id"].(string)), + "redirect_uri": redirectUri, + "id": id, + }, + } +} diff --git a/internal/services/datadog/datadog_monitors_resource_test.go b/internal/services/datadog/datadog_monitors_resource_test.go new file mode 100644 index 000000000000..f46bd1405be0 --- /dev/null +++ b/internal/services/datadog/datadog_monitors_resource_test.go @@ -0,0 +1,303 @@ +package datadog_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DatadogMonitorResource struct{} + +func TestAccDatadogMonitor_basic(t *testing.T) { + if os.Getenv("ARM_TEST_DATADOG_API_KEY") == "" || os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY") == "" { + t.Skip("Skipping as ARM_TEST_DATADOG_API_KEY and/or ARM_TEST_DATADOG_APPLICATION_KEY are not specified") + return + } + data := acceptance.BuildTestData(t, "azurerm_datadog_monitor", "test") + r := DatadogMonitorResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user", + "user.0.name", + "user.0.email", + "datadog_organization", + "datadog_organization.0", + "datadog_organization.0.id", + "datadog_organization.0.name", + "datadog_organization.0.api_key", + "datadog_organization.0.application_key", + "datadog_organization.0.enterprise_app_id", + "datadog_organization.0.linking_auth_code", + "datadog_organization.0.linking_client_id", + "datadog_organization.0.redirect_uri"), + }) +} + +func TestAccDatadogMonitor_requiresImport(t *testing.T) { + if os.Getenv("ARM_TEST_DATADOG_API_KEY") == "" || os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY") == "" { + t.Skip("Skipping as ARM_TEST_DATADOG_API_KEY and/or ARM_TEST_DATADOG_APPLICATION_KEY are not specified") + return + } + data := acceptance.BuildTestData(t, "azurerm_datadog_monitor", "test") + r := DatadogMonitorResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDatadogMonitor_complete(t *testing.T) { + if os.Getenv("ARM_TEST_DATADOG_API_KEY") == "" || os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY") == "" { + t.Skip("Skipping as ARM_TEST_DATADOG_API_KEY and/or ARM_TEST_DATADOG_APPLICATION_KEY are not specified") + return + } + data := acceptance.BuildTestData(t, "azurerm_datadog_monitor", "test") + r := DatadogMonitorResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user", + "user.0.name", + "user.0.email", + "datadog_organization", + "datadog_organization.0", + "datadog_organization.0.id", + "datadog_organization.0.name", + "datadog_organization.0.api_key", + "datadog_organization.0.application_key", + "datadog_organization.0.enterprise_app_id", + "datadog_organization.0.linking_auth_code", + "datadog_organization.0.linking_client_id", + "datadog_organization.0.redirect_uri"), + }) +} + +func TestAccDatadogMonitor_update(t *testing.T) { + if os.Getenv("ARM_TEST_DATADOG_API_KEY") == "" || os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY") == "" { + t.Skip("Skipping as ARM_TEST_DATADOG_API_KEY and/or ARM_TEST_DATADOG_APPLICATION_KEY are not specified") + return + } + data := acceptance.BuildTestData(t, "azurerm_datadog_monitor", "test") + r := DatadogMonitorResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user", + "user.0.name", + "user.0.email", + "datadog_organization", + "datadog_organization.0", + "datadog_organization.0.id", + "datadog_organization.0.name", + "datadog_organization.0.api_key", + "datadog_organization.0.application_key", + "datadog_organization.0.enterprise_app_id", + "datadog_organization.0.linking_auth_code", + "datadog_organization.0.linking_client_id", + "datadog_organization.0.redirect_uri"), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user", + "user.0.name", + "user.0.email", + "datadog_organization", + "datadog_organization.0", + "datadog_organization.0.id", + "datadog_organization.0.name", + "datadog_organization.0.api_key", + "datadog_organization.0.application_key", + "datadog_organization.0.enterprise_app_id", + "datadog_organization.0.linking_auth_code", + "datadog_organization.0.linking_client_id", + "datadog_organization.0.redirect_uri"), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user", + "user.0.name", + "user.0.email", + "datadog_organization", + "datadog_organization.0", + "datadog_organization.0.id", + "datadog_organization.0.name", + "datadog_organization.0.api_key", + "datadog_organization.0.application_key", + "datadog_organization.0.enterprise_app_id", + "datadog_organization.0.linking_auth_code", + "datadog_organization.0.linking_client_id", + "datadog_organization.0.redirect_uri"), + }) +} + +func (r DatadogMonitorResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.DatadogMonitorID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.Datadog.MonitorsClient.Get(ctx, id.ResourceGroup, id.MonitorName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving Datadog Monitor %q (Resource Group %q): %+v", id.MonitorName, id.ResourceGroup, err) + } + return utils.Bool(true), nil +} + +func (r DatadogMonitorResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +resource "azurerm_resource_group" "test" { + name = "acctest-datadog-%d" + location = "%s" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r DatadogMonitorResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` + %s + +resource "azurerm_datadog_monitor" "test" { + name = "acctest-datadog-%d" + resource_group_name = azurerm_resource_group.test.name + location = "WEST US 2" + datadog_organization { + api_key = %q + application_key = %q + } + user { + name = "Test Datadog" + email = "abc@xyz.com" + } + sku_name = "Linked" + identity { + type = "SystemAssigned" + } +} +`, r.template(data), data.RandomInteger%100, os.Getenv("ARM_TEST_DATADOG_API_KEY"), os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY")) +} + +func (r DatadogMonitorResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-datadog-%d" + location = "%s" +} +resource "azurerm_datadog_monitor" "test" { + name = "acctest-datadog-%d" + resource_group_name = azurerm_resource_group.test.name + location = "WEST US 2" + datadog_organization { + api_key = %q + application_key = %q + } + user { + name = "Test Datadog" + email = "abc@xyz.com" + } + sku_name = "Linked" + identity { + type = "SystemAssigned" + } + monitoring_enabled = false + tags = { + ENV = "Test" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger%100, os.Getenv("ARM_TEST_DATADOG_API_KEY"), os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY")) +} + +func (r DatadogMonitorResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` + %s +resource "azurerm_datadog_monitor" "import" { + name = azurerm_datadog_monitor.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_datadog_monitor.test.location + datadog_organization { + api_key = %q + application_key = %q + } + user { + name = "Test Datadog" + email = "abc@xyz.com" + } + sku_name = "Linked" + identity { + type = "SystemAssigned" + } +} +`, r.basic(data), os.Getenv("ARM_TEST_DATADOG_API_KEY"), os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY")) +} + +func (r DatadogMonitorResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` + %s +resource "azurerm_datadog_monitor" "test" { + name = "acctest-datadog-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + datadog_organization { + api_key = %q + application_key = %q + enterprise_app_id = "" + linking_auth_code = "" + linking_client_id = "" + redirect_uri = "" + } + identity { + type = "SystemAssigned" + } + sku_name = "Linked" + user { + name = "Test Datadog" + email = "abc@xyz.com" + phone_number = "" + } + monitoring_enabled = true + tags = { + ENV = "Test" + } +} +`, r.template(data), data.RandomInteger%100, os.Getenv("ARM_TEST_DATADOG_API_KEY"), os.Getenv("ARM_TEST_DATADOG_APPLICATION_KEY")) +} diff --git a/internal/services/datadog/parse/datadog_monitor.go b/internal/services/datadog/parse/datadog_monitor.go new file mode 100644 index 000000000000..9f0a8418058e --- /dev/null +++ b/internal/services/datadog/parse/datadog_monitor.go @@ -0,0 +1,69 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type DatadogMonitorId struct { + SubscriptionId string + ResourceGroup string + MonitorName string +} + +func NewDatadogMonitorID(subscriptionId, resourceGroup, monitorName string) DatadogMonitorId { + return DatadogMonitorId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + MonitorName: monitorName, + } +} + +func (id DatadogMonitorId) String() string { + segments := []string{ + fmt.Sprintf("Monitor Name %q", id.MonitorName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Datadog Monitor", segmentsStr) +} + +func (id DatadogMonitorId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Datadog/monitors/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.MonitorName) +} + +// DatadogMonitorID parses a DatadogMonitor ID into an DatadogMonitorId struct +func DatadogMonitorID(input string) (*DatadogMonitorId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := DatadogMonitorId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.MonitorName, err = id.PopSegment("monitors"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/datadog/parse/datadog_monitor_test.go b/internal/services/datadog/parse/datadog_monitor_test.go new file mode 100644 index 000000000000..8371889301a1 --- /dev/null +++ b/internal/services/datadog/parse/datadog_monitor_test.go @@ -0,0 +1,112 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = DatadogMonitorId{} + +func TestDatadogMonitorIDFormatter(t *testing.T) { + actual := NewDatadogMonitorID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "monitor1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/monitor1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestDatadogMonitorID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *DatadogMonitorId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing MonitorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/", + Error: true, + }, + + { + // missing value for MonitorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/monitor1", + Expected: &DatadogMonitorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + MonitorName: "monitor1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATADOG/MONITORS/MONITOR1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := DatadogMonitorID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.MonitorName != v.Expected.MonitorName { + t.Fatalf("Expected %q but got %q for MonitorName", v.Expected.MonitorName, actual.MonitorName) + } + } +} diff --git a/internal/services/datadog/registration.go b/internal/services/datadog/registration.go new file mode 100644 index 000000000000..d583e5fcc608 --- /dev/null +++ b/internal/services/datadog/registration.go @@ -0,0 +1,29 @@ +package datadog + +import "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + +type Registration struct{} + +// Name is the name of this Service +func (r Registration) Name() string { + return "Datadog" +} + +// WebsiteCategories returns a list of categories which can be used for the sidebar +func (r Registration) WebsiteCategories() []string { + return []string{ + "Datadog", + } +} + +// SupportedDataSources returns the supported Data Sources supported by this Service +func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { + return map[string]*pluginsdk.Resource{} +} + +// SupportedResources returns the supported Resources supported by this Service +func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { + return map[string]*pluginsdk.Resource{ + "azurerm_datadog_monitor": resourceDatadogMonitor(), + } +} diff --git a/internal/services/datadog/resourceids.go b/internal/services/datadog/resourceids.go new file mode 100644 index 000000000000..cfc4642478c5 --- /dev/null +++ b/internal/services/datadog/resourceids.go @@ -0,0 +1,3 @@ +package datadog + +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DatadogMonitor -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/monitor1 diff --git a/internal/services/datadog/validate/datadog_email_address.go b/internal/services/datadog/validate/datadog_email_address.go new file mode 100644 index 000000000000..acbe29fea3c1 --- /dev/null +++ b/internal/services/datadog/validate/datadog_email_address.go @@ -0,0 +1,19 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func DatadogMonitorsEmailAddress(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 + } + if !regexp.MustCompile(`^[A-Za-z0-9._%+-]+@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}$`).MatchString(v) { + errors = append(errors, fmt.Errorf("expected value of %s not match regular expression, got %v", k, v)) + return + } + return +} diff --git a/internal/services/datadog/validate/datadog_email_address_test.go b/internal/services/datadog/validate/datadog_email_address_test.go new file mode 100644 index 000000000000..c84f3bb07d97 --- /dev/null +++ b/internal/services/datadog/validate/datadog_email_address_test.go @@ -0,0 +1,38 @@ +package validate + +import "testing" + +func TestDatadogMonitorsEmailAddress(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "abc@xyz.com", + Expected: true, + }, + { + Input: "abc@dyg@jad.com", + Expected: false, + }, + { + Input: "abc@", + Expected: false, + }, + { + Input: "abc@xyz", + Expected: false, + }, + { + Input: "abcdyg@jad.com.cdhc", + Expected: true, + }, + } + for _, v := range testCases { + _, errors := DatadogMonitorsEmailAddress(v.Input, "email_address") + 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/internal/services/datadog/validate/datadog_monitor_id.go b/internal/services/datadog/validate/datadog_monitor_id.go new file mode 100644 index 000000000000..7626bf021d70 --- /dev/null +++ b/internal/services/datadog/validate/datadog_monitor_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/datadog/parse" +) + +func DatadogMonitorID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.DatadogMonitorID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/datadog/validate/datadog_monitor_id_test.go b/internal/services/datadog/validate/datadog_monitor_id_test.go new file mode 100644 index 000000000000..c4c4b0415347 --- /dev/null +++ b/internal/services/datadog/validate/datadog_monitor_id_test.go @@ -0,0 +1,76 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestDatadogMonitorID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing MonitorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/", + Valid: false, + }, + + { + // missing value for MonitorName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Datadog/monitors/monitor1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATADOG/MONITORS/MONITOR1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := DatadogMonitorID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/datadog/validate/datadog_monitor_name.go b/internal/services/datadog/validate/datadog_monitor_name.go new file mode 100644 index 000000000000..593d2ee6f7c0 --- /dev/null +++ b/internal/services/datadog/validate/datadog_monitor_name.go @@ -0,0 +1,20 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func DatadogMonitorsName(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 + } + + if !regexp.MustCompile(`^[a-zA-Z0-9_-]{2,32}$`).MatchString(v) { + errors = append(errors, fmt.Errorf("%q must be between 2 and 32 characters in length, can only contain alphanumeric characters, underscore and hyphen symbols", k)) + } + + return +} diff --git a/internal/services/datadog/validate/datadog_monitor_name_test.go b/internal/services/datadog/validate/datadog_monitor_name_test.go new file mode 100644 index 000000000000..9d109bf03795 --- /dev/null +++ b/internal/services/datadog/validate/datadog_monitor_name_test.go @@ -0,0 +1,61 @@ +package validate + +import "testing" + +func TestDatadogMonitorsName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // proper string + input: "hello", + expected: true, + }, + { + // end with exclamation + input: "hello!", + expected: false, + }, + { + // with hypen + input: "malcolm-in-the-middle", + expected: true, + }, + { + // end with fullstop + input: "hello.", + expected: false, + }, + { + // less than 32 + input: "qwertyuioplkjhgfdsazxcv", + expected: true, + }, + { + // with underscore + input: "qwertyuiop_jhgfdsazxcva", + expected: true, + }, + { + // more than 32 + input: "qwertyuioplkjhgfdsazxcvggeitofkjhkt", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := DatadogMonitorsName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/internal/services/datadog/validate/datadog_phone_number.go b/internal/services/datadog/validate/datadog_phone_number.go new file mode 100644 index 000000000000..6f178ada2dec --- /dev/null +++ b/internal/services/datadog/validate/datadog_phone_number.go @@ -0,0 +1,18 @@ +package validate + +import ( + "fmt" +) + +func DatadogMonitorsPhoneNumber(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 + } + if len(v) > 40 { + errors = append(errors, fmt.Errorf("length should be less than %d", 40)) + return + } + return +} diff --git a/internal/services/datadog/validate/datadog_phone_number_test.go b/internal/services/datadog/validate/datadog_phone_number_test.go new file mode 100644 index 000000000000..a88bbace5a6f --- /dev/null +++ b/internal/services/datadog/validate/datadog_phone_number_test.go @@ -0,0 +1,31 @@ +package validate + +import "testing" + +func TestDatadogMonitorsPhoneNumber(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: true, + }, + { + Input: "1234567890", + Expected: true, + }, + { + Input: "12345678901234567890123456789012345678901234567890", + Expected: false, + }, + } + + for _, v := range testCases { + _, errors := DatadogMonitorsPhoneNumber(v.Input, "phone_number") + 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/internal/services/datadog/validate/datadog_users_name.go b/internal/services/datadog/validate/datadog_users_name.go new file mode 100644 index 000000000000..0359ed04094c --- /dev/null +++ b/internal/services/datadog/validate/datadog_users_name.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" +) + +func DatadogUsersName(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 + } + if len(v) == 0 { + errors = append(errors, fmt.Errorf("length cannot be %d", 0)) + return + } + if len(v) > 50 { + errors = append(errors, fmt.Errorf("length should be less than %d", 40)) + return + } + return +} diff --git a/internal/services/datadog/validate/datadog_users_name_test.go b/internal/services/datadog/validate/datadog_users_name_test.go new file mode 100644 index 000000000000..f73b8db8b39b --- /dev/null +++ b/internal/services/datadog/validate/datadog_users_name_test.go @@ -0,0 +1,31 @@ +package validate + +import "testing" + +func TestDatadogUsersName(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "Test", + Expected: true, + }, + { + Input: "qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm", + Expected: false, + }, + } + + for _, v := range testCases { + _, errors := DatadogUsersName(v.Input, "user_name") + 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/internal/services/datafactory/data_factory_data_flow.go b/internal/services/datafactory/data_factory_data_flow.go index 322036a4598e..38a35d2f48f9 100644 --- a/internal/services/datafactory/data_factory_data_flow.go +++ b/internal/services/datafactory/data_factory_data_flow.go @@ -48,6 +48,35 @@ func SchemaForDataFlowSourceAndSink() *pluginsdk.Schema { }, }, + "flowlet": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "parameters": { + Type: pluginsdk.TypeMap, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "dataset_parameters": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + "linked_service": { Type: pluginsdk.TypeList, Optional: true, @@ -139,6 +168,35 @@ func SchemaForDataFlowSourceTransformation() *pluginsdk.Schema { }, }, + "flowlet": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "parameters": { + Type: pluginsdk.TypeMap, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "dataset_parameters": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + "linked_service": { Type: pluginsdk.TypeList, Optional: true, @@ -180,6 +238,7 @@ func expandDataFactoryDataFlowSource(input []interface{}) *[]datafactory.DataFlo Dataset: expandDataFactoryDatasetReference(raw["dataset"].([]interface{})), LinkedService: expandDataFactoryLinkedServiceReference(raw["linked_service"].([]interface{})), SchemaLinkedService: expandDataFactoryLinkedServiceReference(raw["schema_linked_service"].([]interface{})), + Flowlet: expandDataFactoryDataFlowReference(raw["flowlet"].([]interface{})), }) } return &result @@ -199,6 +258,7 @@ func expandDataFactoryDataFlowSink(input []interface{}) *[]datafactory.DataFlowS Dataset: expandDataFactoryDatasetReference(raw["dataset"].([]interface{})), LinkedService: expandDataFactoryLinkedServiceReference(raw["linked_service"].([]interface{})), SchemaLinkedService: expandDataFactoryLinkedServiceReference(raw["schema_linked_service"].([]interface{})), + Flowlet: expandDataFactoryDataFlowReference(raw["flowlet"].([]interface{})), }) } return &result @@ -217,6 +277,7 @@ func expandDataFactoryDataFlowTransformation(input []interface{}) *[]datafactory Name: utils.String(raw["name"].(string)), Dataset: expandDataFactoryDatasetReference(raw["dataset"].([]interface{})), LinkedService: expandDataFactoryLinkedServiceReference(raw["linked_service"].([]interface{})), + Flowlet: expandDataFactoryDataFlowReference(raw["flowlet"].([]interface{})), }) } return &result @@ -248,6 +309,20 @@ func expandDataFactoryLinkedServiceReference(input []interface{}) *datafactory.L } } +func expandDataFactoryDataFlowReference(input []interface{}) *datafactory.DataFlowReference { + if len(input) == 0 || input[0] == nil { + return nil + } + + raw := input[0].(map[string]interface{}) + return &datafactory.DataFlowReference{ + Type: utils.String("DataFlowReference"), + ReferenceName: utils.String(raw["name"].(string)), + Parameters: raw["parameters"].(map[string]interface{}), + DatasetParameters: utils.String(raw["dataset_parameters"].(string)), + } +} + func flattenDataFactoryDataFlowSource(input *[]datafactory.DataFlowSource) []interface{} { if input == nil { return []interface{}{} @@ -269,6 +344,7 @@ func flattenDataFactoryDataFlowSource(input *[]datafactory.DataFlowSource) []int "dataset": flattenDataFactoryDatasetReference(v.Dataset), "linked_service": flattenDataFactoryLinkedServiceReference(v.LinkedService), "schema_linked_service": flattenDataFactoryLinkedServiceReference(v.SchemaLinkedService), + "flowlet": flattenDataFactoryDataFlowReference(v.Flowlet), }) } return result @@ -295,6 +371,7 @@ func flattenDataFactoryDataFlowSink(input *[]datafactory.DataFlowSink) []interfa "dataset": flattenDataFactoryDatasetReference(v.Dataset), "linked_service": flattenDataFactoryLinkedServiceReference(v.LinkedService), "schema_linked_service": flattenDataFactoryLinkedServiceReference(v.SchemaLinkedService), + "flowlet": flattenDataFactoryDataFlowReference(v.Flowlet), }) } return result @@ -320,6 +397,7 @@ func flattenDataFactoryDataFlowTransformation(input *[]datafactory.Transformatio "description": description, "dataset": flattenDataFactoryDatasetReference(v.Dataset), "linked_service": flattenDataFactoryLinkedServiceReference(v.LinkedService), + "flowlet": flattenDataFactoryDataFlowReference(v.Flowlet), }) } return result @@ -360,3 +438,22 @@ func flattenDataFactoryLinkedServiceReference(input *datafactory.LinkedServiceRe }, } } + +func flattenDataFactoryDataFlowReference(input *datafactory.DataFlowReference) []interface{} { + if input == nil { + return []interface{}{} + } + + name := "" + if input.ReferenceName != nil { + name = *input.ReferenceName + } + + return []interface{}{ + map[string]interface{}{ + "name": name, + "parameters": input.Parameters, + "dataset_parameters": input.DatasetParameters, + }, + } +} diff --git a/internal/services/datafactory/data_factory_data_flow_resource_test.go b/internal/services/datafactory/data_factory_data_flow_resource_test.go index 90134778f698..3795cd384961 100644 --- a/internal/services/datafactory/data_factory_data_flow_resource_test.go +++ b/internal/services/datafactory/data_factory_data_flow_resource_test.go @@ -174,7 +174,7 @@ func (r DataFlowResource) complete(data acceptance.TestData) string { %s resource "azurerm_data_factory_data_flow" "test" { - name = "acctestdf%d" + name = "acctestdf3%[2]d" data_factory_id = azurerm_data_factory.test.id description = "description for data flow" annotations = ["anno1", "anno2"] @@ -184,6 +184,13 @@ resource "azurerm_data_factory_data_flow" "test" { name = "source1" description = "description for source1" + flowlet { + name = azurerm_data_factory_flowlet_data_flow.test1.name + parameters = { + "Key1" = "value1" + } + } + linked_service { name = azurerm_data_factory_linked_custom_service.test.name parameters = { @@ -203,6 +210,13 @@ resource "azurerm_data_factory_data_flow" "test" { name = "sink1" description = "description for sink1" + flowlet { + name = azurerm_data_factory_flowlet_data_flow.test2.name + parameters = { + "Key1" = "value1" + } + } + linked_service { name = azurerm_data_factory_linked_custom_service.test.name parameters = { @@ -303,6 +317,60 @@ Filter1 sink(allowSchemaDrift: true, partitionBy('roundRobin', 3)) ~> sink1 EOT } + +resource "azurerm_data_factory_flowlet_data_flow" "test1" { + name = "acctest1fdf%[2]d" + data_factory_id = azurerm_data_factory.test.id + + source { + name = "source1" + } + + sink { + name = "sink1" + } + + script = < source1 +source1 sink( + allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true) ~> sink1 +EOT +} + +resource "azurerm_data_factory_flowlet_data_flow" "test2" { + name = "acctest2fdf%[2]d" + data_factory_id = azurerm_data_factory.test.id + + source { + name = "source1" + } + + sink { + name = "sink1" + } + + script = < source1 +source1 sink( + allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true) ~> sink1 +EOT +} `, r.template(data), data.RandomInteger) } @@ -313,12 +381,12 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-df-%d" - location = "%s" + name = "acctestRG-df-%[1]d" + location = "%[2]s" } resource "azurerm_storage_account" "test" { - name = "acctestsa%s" + name = "acctestsa%[3]s" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name account_tier = "Standard" @@ -326,13 +394,13 @@ resource "azurerm_storage_account" "test" { } resource "azurerm_data_factory" "test" { - name = "acctestdf%d" + name = "acctestdf%[1]d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name } resource "azurerm_data_factory_linked_custom_service" "test" { - name = "acctestls%d" + name = "acctestls%[1]d" data_factory_id = azurerm_data_factory.test.id type = "AzureBlobStorage" type_properties_json = < source1 +source1 sink( + allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true) ~> sink1 +EOT +} +`, r.template(data), data.RandomInteger) +} + +func (r FlowletDataFlowResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_factory_flowlet_data_flow" "import" { + name = azurerm_data_factory_flowlet_data_flow.test.name + data_factory_id = azurerm_data_factory_flowlet_data_flow.test.data_factory_id + script = azurerm_data_factory_flowlet_data_flow.test.script + source { + name = azurerm_data_factory_flowlet_data_flow.test.source.0.name + linked_service { + name = azurerm_data_factory_flowlet_data_flow.test.source.0.linked_service.0.name + } + } + + sink { + name = azurerm_data_factory_flowlet_data_flow.test.sink.0.name + linked_service { + name = azurerm_data_factory_flowlet_data_flow.test.sink.0.linked_service.0.name + } + } +} +`, r.basic(data)) +} + +func (r FlowletDataFlowResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_data_factory_flowlet_data_flow" "test" { + name = "acctestfdf%[2]d" + data_factory_id = azurerm_data_factory.test.id + description = "description for data flow" + annotations = ["anno1", "anno2"] + folder = "folder1" + + source { + name = "source1" + description = "description for source1" + + flowlet { + name = azurerm_data_factory_flowlet_data_flow.test1.name + parameters = { + "Key1" = "value1" + } + } + + linked_service { + name = azurerm_data_factory_linked_custom_service.test.name + parameters = { + "Key1" = "value1" + } + } + + schema_linked_service { + name = azurerm_data_factory_linked_custom_service.test.name + parameters = { + "Key1" = "value1" + } + } + } + + sink { + name = "sink1" + description = "description for sink1" + + flowlet { + name = azurerm_data_factory_flowlet_data_flow.test2.name + parameters = { + "Key1" = "value1" + } + } + + linked_service { + name = azurerm_data_factory_linked_custom_service.test.name + parameters = { + "Key1" = "value1" + } + } + + schema_linked_service { + name = azurerm_data_factory_linked_custom_service.test.name + parameters = { + "Key1" = "value1" + } + } + } + + transformation { + name = "filter1" + description = "description for filter1" + + dataset { + name = azurerm_data_factory_dataset_json.test1.name + parameters = { + "Key1" = "value1" + } + } + + linked_service { + name = azurerm_data_factory_linked_custom_service.test.name + parameters = { + "Key1" = "value1" + } + } + } + + script_lines = [< source1 +source1 filter(toInteger(year) >= 1910 && toInteger(year) <= 2000) ~> Filter1 +Filter1 sink(allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true, + saveOrder: 0, + partitionBy('roundRobin', 3)) ~> sink1 +EOT, +< source1 +source1 filter(toInteger(year) >= 1910 && toInteger(year) <= 2000) ~> Filter1 +Filter1 sink(allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true, + saveOrder: 0, + partitionBy('roundRobin', 3)) ~> sink1 +EOT + ] + + script = < source1 +source1 filter(toInteger(year) >= 1910 && toInteger(year) <= 2000) ~> Filter1 +Filter1 sink(allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true, + saveOrder: 0, + partitionBy('roundRobin', 3)) ~> sink1 +EOT +} + +resource "azurerm_data_factory_flowlet_data_flow" "test1" { + name = "acctest1fdf%[2]d" + data_factory_id = azurerm_data_factory.test.id + + source { + name = "source1" + } + + sink { + name = "sink1" + } + + script = < source1 +source1 sink( + allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true) ~> sink1 +EOT +} + +resource "azurerm_data_factory_flowlet_data_flow" "test2" { + name = "acctest2fdf%[2]d" + data_factory_id = azurerm_data_factory.test.id + + source { + name = "source1" + } + + sink { + name = "sink1" + } + + script = < source1 +source1 sink( + allowSchemaDrift: true, + validateSchema: false, + skipDuplicateMapInputs: true, + skipDuplicateMapOutputs: true) ~> sink1 +EOT +} +`, r.template(data), data.RandomInteger) +} + +func (FlowletDataFlowResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-df-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[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_data_factory" "test" { + name = "acctestdf%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_data_factory_linked_custom_service" "test" { + name = "acctestls%[1]d" + data_factory_id = azurerm_data_factory.test.id + type = "AzureBlobStorage" + type_properties_json = < 0 { - if parameter, ok := (*props.PolicyInfo.PolicyParameters.DataStoreParametersList)[0].AsAzureOperationalStoreParameters(); ok { - if parameter.ResourceGroupID != nil { - resourceGroupId, err := resourceParse.ResourceGroupID(*parameter.ResourceGroupID) - if err != nil { - return err - } - d.Set("snapshot_resource_group_name", resourceGroupId.ResourceGroup) + parameter := (*props.PolicyInfo.PolicyParameters.DataStoreParametersList)[0].(backupinstances.AzureOperationalStoreParameters) + + if parameter.ResourceGroupId != nil { + resourceGroupId, err := resourceParse.ResourceGroupID(*parameter.ResourceGroupId) + if err != nil { + return err } + d.Set("snapshot_resource_group_name", resourceGroupId.ResourceGroup) } } } @@ -209,18 +204,15 @@ func resourceDataProtectionBackupInstanceDiskDelete(d *schema.ResourceData, meta ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupInstanceID(d.Id()) + id, err := backupinstances.ParseBackupInstanceID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + err = client.DeleteThenPoll(ctx, *id) if err != nil { return fmt.Errorf("deleting DataProtection BackupInstance (%q): %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of the DataProtection BackupInstance (%q): %+v", id.Name, err) - } return nil } diff --git a/internal/services/dataprotection/data_protection_backup_instance_disk_resource_test.go b/internal/services/dataprotection/data_protection_backup_instance_disk_resource_test.go index 57baadd82fc6..9038dfbf6a65 100644 --- a/internal/services/dataprotection/data_protection_backup_instance_disk_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_instance_disk_resource_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupinstances" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -87,13 +88,13 @@ func TestAccDataProtectionBackupInstanceDisk_update(t *testing.T) { } func (r DataProtectionBackupInstanceDiskResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { - id, err := parse.BackupInstanceID(state.ID) + id, err := backupinstances.ParseBackupInstanceID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupInstanceClient.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.DataProtection.BackupInstanceClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupInstance (%q): %+v", id, err) diff --git a/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource.go b/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource.go index 4a514e2f396f..f61bc4f73b00 100644 --- a/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource.go +++ b/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource.go @@ -6,17 +6,17 @@ import ( "log" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupinstances" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/databases" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/validate" keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" - postgresParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" - postgresValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" azSchema "github.com/hashicorp/terraform-provider-azurerm/internal/tf/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -38,7 +38,7 @@ func resourceDataProtectionBackupInstancePostgreSQL() *pluginsdk.Resource { }, Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { - _, err := parse.BackupInstanceID(id) + _, err := backupinstances.ParseBackupInstanceID(id) return err }), @@ -55,20 +55,20 @@ func resourceDataProtectionBackupInstancePostgreSQL() *pluginsdk.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.BackupVaultID, + ValidateFunc: backupinstances.ValidateBackupVaultID, }, "database_id": { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: postgresValidate.DatabaseID, + ValidateFunc: databases.ValidateDatabaseID, }, "backup_policy_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validate.BackupPolicyID, + ValidateFunc: backuppolicies.ValidateBackupPoliciesID, }, "database_credential_key_vault_secret_id": { @@ -87,71 +87,66 @@ func resourceDataProtectionBackupInstancePostgreSQLCreateUpdate(d *schema.Resour defer cancel() name := d.Get("name").(string) - vaultId, _ := parse.BackupVaultID(d.Get("vault_id").(string)) + vaultId, _ := backupinstances.ParseBackupVaultID(d.Get("vault_id").(string)) - id := parse.NewBackupInstanceID(subscriptionId, vaultId.ResourceGroup, vaultId.Name, name) + id := backupinstances.NewBackupInstanceID(subscriptionId, vaultId.ResourceGroupName, vaultId.VaultName, name) if d.IsNewResource() { - existing, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing DataProtection BackupInstance (%q): %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_data_protection_backup_instance_postgresql", id.ID()) } } - databaseId, _ := postgresParse.DatabaseID(d.Get("database_id").(string)) + databaseId, _ := databases.ParseDatabaseID(d.Get("database_id").(string)) location := location.Normalize(d.Get("location").(string)) - serverId := postgresParse.NewServerID(databaseId.SubscriptionId, databaseId.ResourceGroup, databaseId.ServerName) - policyId, _ := parse.BackupPolicyID(d.Get("backup_policy_id").(string)) + serverId := servers.NewServerID(databaseId.SubscriptionId, databaseId.ResourceGroupName, databaseId.ServerName) + policyId, _ := backuppolicies.ParseBackupPoliciesID(d.Get("backup_policy_id").(string)) - parameters := dataprotection.BackupInstanceResource{ - Properties: &dataprotection.BackupInstance{ - DataSourceInfo: &dataprotection.Datasource{ + parameters := backupinstances.BackupInstanceResource{ + Properties: &backupinstances.BackupInstance{ + DataSourceInfo: backupinstances.Datasource{ DatasourceType: utils.String("Microsoft.DBforPostgreSQL/servers/databases"), ObjectType: utils.String("Datasource"), - ResourceID: utils.String(databaseId.ID()), + ResourceID: databaseId.ID(), ResourceLocation: utils.String(location), - ResourceName: utils.String(databaseId.Name), + ResourceName: utils.String(databaseId.DatabaseName), ResourceType: utils.String("Microsoft.DBforPostgreSQL/servers/databases"), - ResourceURI: utils.String(""), + ResourceUri: utils.String(""), }, - DataSourceSetInfo: &dataprotection.DatasourceSet{ + DataSourceSetInfo: &backupinstances.DatasourceSet{ DatasourceType: utils.String("Microsoft.DBForPostgreSQL/servers"), ObjectType: utils.String("DatasourceSet"), - ResourceID: utils.String(serverId.ID()), + ResourceID: serverId.ID(), ResourceLocation: utils.String(location), - ResourceName: utils.String(serverId.Name), + ResourceName: utils.String(serverId.ServerName), ResourceType: utils.String("Microsoft.DBForPostgreSQL/servers"), - ResourceURI: utils.String(""), + ResourceUri: utils.String(""), }, - FriendlyName: utils.String(id.Name), - PolicyInfo: &dataprotection.PolicyInfo{ - PolicyID: utils.String(policyId.ID()), + FriendlyName: utils.String(id.BackupInstanceName), + PolicyInfo: backupinstances.PolicyInfo{ + PolicyId: policyId.ID(), }, }, } if v, ok := d.GetOk("database_credential_key_vault_secret_id"); ok { - parameters.Properties.DatasourceAuthCredentials = &dataprotection.SecretStoreBasedAuthCredentials{ - SecretStoreResource: &dataprotection.SecretStoreResource{ - URI: utils.String(v.(string)), - SecretStoreType: dataprotection.SecretStoreTypeAzureKeyVault, + parameters.Properties.DatasourceAuthCredentials = backupinstances.SecretStoreBasedAuthCredentials{ + SecretStoreResource: &backupinstances.SecretStoreResource{ + Uri: utils.String(v.(string)), + SecretStoreType: backupinstances.SecretStoreTypeAzureKeyVault, }, - ObjectType: dataprotection.ObjectTypeSecretStoreBasedAuthCredentials, } } - future, err := client.CreateOrUpdate(ctx, id.BackupVaultName, id.ResourceGroup, id.Name, parameters) + err := client.CreateOrUpdateThenPoll(ctx, id, parameters) if err != nil { - return fmt.Errorf("creating/updating DataProtection BackupInstance (%q): %+v", id, err) - } - - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation/update of the DataProtection BackupInstance (%q): %+v", id, err) + return fmt.Errorf("creating/updating %s: %+v", id, err) } deadline, ok := ctx.Deadline() @@ -159,8 +154,8 @@ func resourceDataProtectionBackupInstancePostgreSQLCreateUpdate(d *schema.Resour return fmt.Errorf("context had no deadline") } stateConf := &pluginsdk.StateChangeConf{ - Pending: []string{string(dataprotection.StatusConfiguringProtection), "UpdatingProtection"}, - Target: []string{string(dataprotection.StatusProtectionConfigured)}, + Pending: []string{string(backupinstances.StatusConfiguringProtection), "UpdatingProtection"}, + Target: []string{string(backupinstances.StatusProtectionConfigured)}, Refresh: policyProtectionStateRefreshFunc(ctx, client, id), MinTimeout: 1 * time.Minute, Timeout: time.Until(deadline), @@ -179,35 +174,34 @@ func resourceDataProtectionBackupInstancePostgreSQLRead(d *schema.ResourceData, ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupInstanceID(d.Id()) + id, err := backupinstances.ParseBackupInstanceID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] dataprotection %q does not exist - removing from state", d.Id()) d.SetId("") return nil } return fmt.Errorf("retrieving DataProtection BackupInstance (%q): %+v", id, err) } - vaultId := parse.NewBackupVaultID(id.SubscriptionId, id.ResourceGroup, id.BackupVaultName) - d.Set("name", id.Name) + vaultId := backupinstances.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.VaultName) + d.Set("name", id.BackupInstanceName) d.Set("vault_id", vaultId.ID()) - if props := resp.Properties; props != nil { - if props.DataSourceInfo != nil { + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { d.Set("database_id", props.DataSourceInfo.ResourceID) d.Set("location", props.DataSourceInfo.ResourceLocation) - } - if props.PolicyInfo != nil { - d.Set("backup_policy_id", props.PolicyInfo.PolicyID) - } - if props.DatasourceAuthCredentials != nil { - if credential, ok := props.DatasourceAuthCredentials.AsSecretStoreBasedAuthCredentials(); ok { + d.Set("backup_policy_id", props.PolicyInfo.PolicyId) + + if props.DatasourceAuthCredentials != nil { + credential := props.DatasourceAuthCredentials.(backupinstances.SecretStoreBasedAuthCredentials) if credential.SecretStoreResource != nil { - d.Set("database_credential_key_vault_secret_id", credential.SecretStoreResource.URI) + d.Set("database_credential_key_vault_secret_id", credential.SecretStoreResource.Uri) } } else { log.Printf("[DEBUG] Skipping setting database_credential_key_vault_secret_id since this DatasourceAuthCredentials is not supported") @@ -222,32 +216,29 @@ func resourceDataProtectionBackupInstancePostgreSQLDelete(d *schema.ResourceData ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupInstanceID(d.Id()) + id, err := backupinstances.ParseBackupInstanceID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + err = client.DeleteThenPoll(ctx, *id) if err != nil { return fmt.Errorf("deleting DataProtection BackupInstance (%q): %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of the DataProtection BackupInstance (%q): %+v", id.Name, err) - } return nil } -func policyProtectionStateRefreshFunc(ctx context.Context, client *dataprotection.BackupInstancesClient, id parse.BackupInstanceId) pluginsdk.StateRefreshFunc { +func policyProtectionStateRefreshFunc(ctx context.Context, client *backupinstances.BackupInstancesClient, id backupinstances.BackupInstanceId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + res, err := client.Get(ctx, id) if err != nil { return nil, "", fmt.Errorf("retrieving DataProtection BackupInstance (%q): %+v", id, err) } - if res.Properties == nil || res.Properties.ProtectionStatus == nil { + if res.Model == nil || res.Model.Properties == nil || res.Model.Properties.ProtectionStatus == nil || res.Model.Properties.ProtectionStatus.Status == nil { return nil, "", fmt.Errorf("reading DataProtection BackupInstance (%q) protection status: %+v", id, err) } - return res, string(res.Properties.ProtectionStatus.Status), nil + return res, string(*res.Model.Properties.ProtectionStatus.Status), nil } } diff --git a/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource_test.go b/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource_test.go index cf3fd742d178..0f2f6f689b4d 100644 --- a/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_instance_postgresql_resource_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupinstances" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -101,16 +102,16 @@ func TestAccDataProtectionBackupInstancePostgreSQL_update(t *testing.T) { } func (r DataProtectionBackupInstancePostgreSQLResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { - id, err := parse.BackupInstanceID(state.ID) + id, err := backupinstances.ParseBackupInstanceID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupInstanceClient.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.DataProtection.BackupInstanceClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } - return nil, fmt.Errorf("retrieving DataProtection BackupInstance (%q): %+v", id, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return utils.Bool(true), nil } diff --git a/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource.go b/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource.go index 6c01d0500c36..bbfdf86d6d88 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource.go +++ b/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource.go @@ -6,13 +6,12 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" helperValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/validate" azSchema "github.com/hashicorp/terraform-provider-azurerm/internal/tf/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -33,7 +32,7 @@ func resourceDataProtectionBackupPolicyBlobStorage() *schema.Resource { }, Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { - _, err := parse.BackupPolicyID(id) + _, err := backuppolicies.ParseBackupPoliciesID(id) return err }), @@ -52,7 +51,7 @@ func resourceDataProtectionBackupPolicyBlobStorage() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.BackupVaultID, + ValidateFunc: backuppolicies.ValidateBackupVaultID, }, "retention_duration": { @@ -72,47 +71,44 @@ func resourceDataProtectionBackupPolicyBlobStorageCreate(d *schema.ResourceData, defer cancel() name := d.Get("name").(string) - vaultId, _ := parse.BackupVaultID(d.Get("vault_id").(string)) - id := parse.NewBackupPolicyID(subscriptionId, vaultId.ResourceGroup, vaultId.Name, name) + vaultId, _ := backuppolicies.ParseBackupVaultID(d.Get("vault_id").(string)) + id := backuppolicies.NewBackupPoliciesID(subscriptionId, vaultId.ResourceGroupName, vaultId.VaultName, name) - existing, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing DataProtection BackupPolicy (%q): %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_data_protection_backup_policy_blob_storage", id.ID()) } - parameters := dataprotection.BaseBackupPolicyResource{ - Properties: &dataprotection.BackupPolicy{ - PolicyRules: &[]dataprotection.BasicBasePolicyRule{ - dataprotection.AzureRetentionRule{ - Name: utils.String("Default"), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, - IsDefault: utils.Bool(true), - Lifecycles: &[]dataprotection.SourceLifeCycle{ + parameters := backuppolicies.BaseBackupPolicyResource{ + Properties: &backuppolicies.BackupPolicy{ + PolicyRules: []backuppolicies.BasePolicyRule{ + backuppolicies.AzureRetentionRule{ + Name: "Default", + IsDefault: utils.Bool(true), + Lifecycles: []backuppolicies.SourceLifeCycle{ { - DeleteAfter: dataprotection.AbsoluteDeleteOption{ - Duration: utils.String(d.Get("retention_duration").(string)), - ObjectType: dataprotection.ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: d.Get("retention_duration").(string), }, - SourceDataStore: &dataprotection.DataStoreInfoBase{ + SourceDataStore: backuppolicies.DataStoreInfoBase{ DataStoreType: "OperationalStore", - ObjectType: utils.String("DataStoreInfoBase"), + ObjectType: "DataStoreInfoBase", }, - TargetDataStoreCopySettings: &[]dataprotection.TargetCopySetting{}, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, }, }, }, }, - DatasourceTypes: &[]string{"Microsoft.Storage/storageAccounts/blobServices"}, - ObjectType: dataprotection.ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy, + DatasourceTypes: []string{"Microsoft.Storage/storageAccounts/blobServices"}, }, } - if _, err := client.CreateOrUpdate(ctx, id.BackupVaultName, id.ResourceGroup, id.Name, parameters); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating DataProtection BackupPolicy (%q): %+v", id, err) } @@ -125,27 +121,29 @@ func resourceDataProtectionBackupPolicyBlobStorageRead(d *schema.ResourceData, m ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] dataprotection %q does not exist - removing from state", d.Id()) d.SetId("") return nil } return fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) } - vaultId := parse.NewBackupVaultID(id.SubscriptionId, id.ResourceGroup, id.BackupVaultName) - d.Set("name", id.Name) + vaultId := backuppolicies.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.VaultName) + d.Set("name", id.BackupPolicyName) d.Set("vault_id", vaultId.ID()) - if resp.Properties != nil { - if props, ok := resp.Properties.AsBackupPolicy(); ok { - if err := d.Set("retention_duration", flattenBackupPolicyBlobStorageDefaultRetentionRuleDuration(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `default_retention_duration`: %+v", err) + if resp.Model != nil { + if resp.Model.Properties != nil { + if props, ok := resp.Model.Properties.(backuppolicies.BackupPolicy); ok { + if err := d.Set("retention_duration", flattenBackupPolicyBlobStorageDefaultRetentionRuleDuration(props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_duration`: %+v", err) + } } } } @@ -157,13 +155,13 @@ func resourceDataProtectionBackupPolicyBlobStorageDelete(d *schema.ResourceData, ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - if resp, err := client.Delete(ctx, id.BackupVaultName, id.ResourceGroup, id.Name); err != nil { - if utils.ResponseWasNotFound(resp) { + if resp, err := client.Delete(ctx, *id); err != nil { + if response.WasNotFound(resp.HttpResponse) { return nil } @@ -172,16 +170,16 @@ func resourceDataProtectionBackupPolicyBlobStorageDelete(d *schema.ResourceData, return nil } -func flattenBackupPolicyBlobStorageDefaultRetentionRuleDuration(input *[]dataprotection.BasicBasePolicyRule) interface{} { +func flattenBackupPolicyBlobStorageDefaultRetentionRuleDuration(input []backuppolicies.BasePolicyRule) interface{} { if input == nil { return nil } - for _, item := range *input { - if retentionRule, ok := item.AsAzureRetentionRule(); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { - if retentionRule.Lifecycles != nil && len(*retentionRule.Lifecycles) > 0 { - if deleteOption, ok := (*retentionRule.Lifecycles)[0].DeleteAfter.AsAbsoluteDeleteOption(); ok { - return *deleteOption.Duration + for _, item := range input { + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { + if retentionRule.Lifecycles != nil && len(retentionRule.Lifecycles) > 0 { + if deleteOption, ok := (retentionRule.Lifecycles)[0].DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + return deleteOption.Duration } } } diff --git a/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource_test.go index a2965b1328e8..248e8af1c3f8 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_policy_blob_storage_resource_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -45,13 +46,13 @@ func TestAccDataProtectionBackupPolicyBlobStorage_requiresImport(t *testing.T) { } func (r DataProtectionBackupPolicyBlobStorageResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { - id, err := parse.BackupPolicyID(state.ID) + id, err := backuppolicies.ParseBackupPoliciesID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) diff --git a/internal/services/dataprotection/data_protection_backup_policy_disk_resource.go b/internal/services/dataprotection/data_protection_backup_policy_disk_resource.go index 4c2a209cf35e..60dd2cf37163 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_disk_resource.go +++ b/internal/services/dataprotection/data_protection_backup_policy_disk_resource.go @@ -7,13 +7,12 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" helperValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/validate" azSchema "github.com/hashicorp/terraform-provider-azurerm/internal/tf/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -34,7 +33,7 @@ func resourceDataProtectionBackupPolicyDisk() *schema.Resource { }, Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { - _, err := parse.BackupPolicyID(id) + _, err := backuppolicies.ParseBackupPoliciesID(id) return err }), @@ -53,7 +52,7 @@ func resourceDataProtectionBackupPolicyDisk() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.BackupVaultID, + ValidateFunc: backuppolicies.ValidateBackupVaultID, }, "backup_repeating_time_intervals": { @@ -104,8 +103,8 @@ func resourceDataProtectionBackupPolicyDisk() *schema.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(dataprotection.AbsoluteMarkerFirstOfDay), - string(dataprotection.AbsoluteMarkerFirstOfWeek), + string(backuppolicies.AbsoluteMarkerFirstOfDay), + string(backuppolicies.AbsoluteMarkerFirstOfWeek), }, false), }, }, @@ -131,33 +130,32 @@ func resourceDataProtectionBackupPolicyDiskCreate(d *schema.ResourceData, meta i defer cancel() name := d.Get("name").(string) - vaultId, _ := parse.BackupVaultID(d.Get("vault_id").(string)) - id := parse.NewBackupPolicyID(subscriptionId, vaultId.ResourceGroup, vaultId.Name, name) + vaultId, _ := backuppolicies.ParseBackupVaultID(d.Get("vault_id").(string)) + id := backuppolicies.NewBackupPoliciesID(subscriptionId, vaultId.ResourceGroupName, vaultId.VaultName, name) - existing, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing DataProtection BackupPolicy (%q): %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_data_protection_backup_policy_disk", id.ID()) } taggingCriteria := expandBackupPolicyDiskTaggingCriteriaArray(d.Get("retention_rule").([]interface{})) - policyRules := make([]dataprotection.BasicBasePolicyRule, 0) + policyRules := make([]backuppolicies.BasePolicyRule, 0) policyRules = append(policyRules, expandBackupPolicyDiskAzureBackupRuleArray(d.Get("backup_repeating_time_intervals").([]interface{}), taggingCriteria)...) policyRules = append(policyRules, expandBackupPolicyDiskDefaultAzureRetentionRule(d.Get("default_retention_duration"))) policyRules = append(policyRules, expandBackupPolicyDiskAzureRetentionRuleArray(d.Get("retention_rule").([]interface{}))...) - parameters := dataprotection.BaseBackupPolicyResource{ - Properties: &dataprotection.BackupPolicy{ - PolicyRules: &policyRules, - DatasourceTypes: &[]string{"Microsoft.Compute/disks"}, - ObjectType: dataprotection.ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy, + parameters := backuppolicies.BaseBackupPolicyResource{ + Properties: &backuppolicies.BackupPolicy{ + PolicyRules: policyRules, + DatasourceTypes: []string{"Microsoft.Compute/disks"}, }, } - if _, err := client.CreateOrUpdate(ctx, id.BackupVaultName, id.ResourceGroup, id.Name, parameters); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating DataProtection BackupPolicy (%q): %+v", id, err) } @@ -170,33 +168,35 @@ func resourceDataProtectionBackupPolicyDiskRead(d *schema.ResourceData, meta int ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] dataprotection %q does not exist - removing from state", d.Id()) d.SetId("") return nil } return fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) } - vaultId := parse.NewBackupVaultID(id.SubscriptionId, id.ResourceGroup, id.BackupVaultName) - d.Set("name", id.Name) + vaultId := backuppolicies.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.VaultName) + d.Set("name", id.BackupPolicyName) d.Set("vault_id", vaultId.ID()) - if resp.Properties != nil { - if props, ok := resp.Properties.AsBackupPolicy(); ok { - if err := d.Set("backup_repeating_time_intervals", flattenBackupPolicyDiskBackupRuleArray(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `backup_repeating_time_intervals`: %+v", err) - } - if err := d.Set("default_retention_duration", flattenBackupPolicyDiskDefaultRetentionRuleDuration(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `default_retention_duration`: %+v", err) - } - if err := d.Set("retention_rule", flattenBackupPolicyDiskRetentionRuleArray(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `retention_rule`: %+v", err) + if resp.Model != nil { + if resp.Model.Properties != nil { + if props, ok := resp.Model.Properties.(backuppolicies.BackupPolicy); ok { + if err := d.Set("backup_repeating_time_intervals", flattenBackupPolicyDiskBackupRuleArray(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `backup_repeating_time_intervals`: %+v", err) + } + if err := d.Set("default_retention_duration", flattenBackupPolicyDiskDefaultRetentionRuleDuration(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_duration`: %+v", err) + } + if err := d.Set("retention_rule", flattenBackupPolicyDiskRetentionRuleArray(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `retention_rule`: %+v", err) + } } } } @@ -208,13 +208,13 @@ func resourceDataProtectionBackupPolicyDiskDelete(d *schema.ResourceData, meta i ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - if resp, err := client.Delete(ctx, id.BackupVaultName, id.ResourceGroup, id.Name); err != nil { - if utils.ResponseWasNotFound(resp) { + if resp, err := client.Delete(ctx, *id); err != nil { + if response.WasNotFound(resp.HttpResponse) { return nil } @@ -223,50 +223,45 @@ func resourceDataProtectionBackupPolicyDiskDelete(d *schema.ResourceData, meta i return nil } -func expandBackupPolicyDiskAzureBackupRuleArray(input []interface{}, taggingCriteria *[]dataprotection.TaggingCriteria) []dataprotection.BasicBasePolicyRule { - results := make([]dataprotection.BasicBasePolicyRule, 0) +func expandBackupPolicyDiskAzureBackupRuleArray(input []interface{}, taggingCriteria *[]backuppolicies.TaggingCriteria) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) - results = append(results, dataprotection.AzureBackupRule{ - Name: utils.String("BackupIntervals"), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule, - DataStore: &dataprotection.DataStoreInfoBase{ - DataStoreType: dataprotection.DataStoreTypesOperationalStore, - ObjectType: utils.String("DataStoreInfoBase"), + results = append(results, backuppolicies.AzureBackupRule{ + Name: "BackupIntervals", + DataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypesOperationalStore, + ObjectType: "DataStoreInfoBase", }, - BackupParameters: &dataprotection.AzureBackupParams{ - BackupType: utils.String("Incremental"), - ObjectType: dataprotection.ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams, + BackupParameters: &backuppolicies.AzureBackupParams{ + BackupType: "Incremental", }, - Trigger: dataprotection.ScheduleBasedTriggerContext{ - Schedule: &dataprotection.BackupSchedule{ - RepeatingTimeIntervals: utils.ExpandStringSlice(input), + Trigger: backuppolicies.ScheduleBasedTriggerContext{ + Schedule: backuppolicies.BackupSchedule{ + RepeatingTimeIntervals: *utils.ExpandStringSlice(input), }, - TaggingCriteria: taggingCriteria, - ObjectType: dataprotection.ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext, + TaggingCriteria: *taggingCriteria, }, }) return results } -func expandBackupPolicyDiskAzureRetentionRuleArray(input []interface{}) []dataprotection.BasicBasePolicyRule { - results := make([]dataprotection.BasicBasePolicyRule, 0) +func expandBackupPolicyDiskAzureRetentionRuleArray(input []interface{}) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) for _, item := range input { v := item.(map[string]interface{}) - results = append(results, dataprotection.AzureRetentionRule{ - Name: utils.String(v["name"].(string)), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, - IsDefault: utils.Bool(false), - Lifecycles: &[]dataprotection.SourceLifeCycle{ + results = append(results, backuppolicies.AzureRetentionRule{ + Name: v["name"].(string), + IsDefault: utils.Bool(false), + Lifecycles: []backuppolicies.SourceLifeCycle{ { - DeleteAfter: dataprotection.AbsoluteDeleteOption{ - Duration: utils.String(v["duration"].(string)), - ObjectType: dataprotection.ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: v["duration"].(string), }, - SourceDataStore: &dataprotection.DataStoreInfoBase{ + SourceDataStore: backuppolicies.DataStoreInfoBase{ DataStoreType: "OperationalStore", - ObjectType: utils.String("DataStoreInfoBase"), + ObjectType: "DataStoreInfoBase", }, - TargetDataStoreCopySettings: &[]dataprotection.TargetCopySetting{}, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, }, }, }) @@ -274,81 +269,76 @@ func expandBackupPolicyDiskAzureRetentionRuleArray(input []interface{}) []datapr return results } -func expandBackupPolicyDiskDefaultAzureRetentionRule(input interface{}) dataprotection.BasicBasePolicyRule { - return dataprotection.AzureRetentionRule{ - Name: utils.String("Default"), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, - IsDefault: utils.Bool(true), - Lifecycles: &[]dataprotection.SourceLifeCycle{ +func expandBackupPolicyDiskDefaultAzureRetentionRule(input interface{}) backuppolicies.BasePolicyRule { + return backuppolicies.AzureRetentionRule{ + Name: "Default", + IsDefault: utils.Bool(true), + Lifecycles: []backuppolicies.SourceLifeCycle{ { - DeleteAfter: dataprotection.AbsoluteDeleteOption{ - Duration: utils.String(input.(string)), - ObjectType: dataprotection.ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: input.(string), }, - SourceDataStore: &dataprotection.DataStoreInfoBase{ + SourceDataStore: backuppolicies.DataStoreInfoBase{ DataStoreType: "OperationalStore", - ObjectType: utils.String("DataStoreInfoBase"), + ObjectType: "DataStoreInfoBase", }, - TargetDataStoreCopySettings: &[]dataprotection.TargetCopySetting{}, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, }, }, } } -func expandBackupPolicyDiskTaggingCriteriaArray(input []interface{}) *[]dataprotection.TaggingCriteria { - results := []dataprotection.TaggingCriteria{ +func expandBackupPolicyDiskTaggingCriteriaArray(input []interface{}) *[]backuppolicies.TaggingCriteria { + results := []backuppolicies.TaggingCriteria{ { Criteria: nil, - IsDefault: utils.Bool(true), - TaggingPriority: utils.Int64(99), - TagInfo: &dataprotection.RetentionTag{ - ID: utils.String("Default_"), - TagName: utils.String("Default"), + IsDefault: true, + TaggingPriority: 99, + TagInfo: backuppolicies.RetentionTag{ + Id: utils.String("Default_"), + TagName: "Default", }, }, } for _, item := range input { v := item.(map[string]interface{}) - results = append(results, dataprotection.TaggingCriteria{ + results = append(results, backuppolicies.TaggingCriteria{ Criteria: expandBackupPolicyDiskCriteriaArray(v["criteria"].([]interface{})), - IsDefault: utils.Bool(false), - TaggingPriority: utils.Int64(int64(v["priority"].(int))), - TagInfo: &dataprotection.RetentionTag{ - ID: utils.String(v["name"].(string) + "_"), - TagName: utils.String(v["name"].(string)), + IsDefault: false, + TaggingPriority: int64(v["priority"].(int)), + TagInfo: backuppolicies.RetentionTag{ + Id: utils.String(v["name"].(string) + "_"), + TagName: v["name"].(string), }, }) } return &results } -func expandBackupPolicyDiskCriteriaArray(input []interface{}) *[]dataprotection.BasicBackupCriteria { - results := make([]dataprotection.BasicBackupCriteria, 0) +func expandBackupPolicyDiskCriteriaArray(input []interface{}) *[]backuppolicies.BackupCriteria { + results := make([]backuppolicies.BackupCriteria, 0) for _, item := range input { v := item.(map[string]interface{}) - var absoluteCriteria []dataprotection.AbsoluteMarker + var absoluteCriteria []backuppolicies.AbsoluteMarker if absoluteCriteriaRaw := v["absolute_criteria"].(string); len(absoluteCriteriaRaw) > 0 { - absoluteCriteria = []dataprotection.AbsoluteMarker{dataprotection.AbsoluteMarker(absoluteCriteriaRaw)} + absoluteCriteria = []backuppolicies.AbsoluteMarker{backuppolicies.AbsoluteMarker(absoluteCriteriaRaw)} } - results = append(results, dataprotection.ScheduleBasedBackupCriteria{ + results = append(results, backuppolicies.ScheduleBasedBackupCriteria{ AbsoluteCriteria: &absoluteCriteria, - ObjectType: dataprotection.ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria, }) } return &results } -func flattenBackupPolicyDiskBackupRuleArray(input *[]dataprotection.BasicBasePolicyRule) []interface{} { +func flattenBackupPolicyDiskBackupRuleArray(input *[]backuppolicies.BasePolicyRule) []interface{} { if input == nil { return make([]interface{}, 0) } for _, item := range *input { - if backupRule, ok := item.AsAzureBackupRule(); ok { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { if backupRule.Trigger != nil { - if scheduleBasedTrigger, ok := backupRule.Trigger.AsScheduleBasedTriggerContext(); ok { - if scheduleBasedTrigger.Schedule != nil { - return utils.FlattenStringSlice(scheduleBasedTrigger.Schedule.RepeatingTimeIntervals) - } + if scheduleBasedTrigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { + return utils.FlattenStringSlice(&scheduleBasedTrigger.Schedule.RepeatingTimeIntervals) } } } @@ -356,16 +346,16 @@ func flattenBackupPolicyDiskBackupRuleArray(input *[]dataprotection.BasicBasePol return make([]interface{}, 0) } -func flattenBackupPolicyDiskDefaultRetentionRuleDuration(input *[]dataprotection.BasicBasePolicyRule) interface{} { +func flattenBackupPolicyDiskDefaultRetentionRuleDuration(input *[]backuppolicies.BasePolicyRule) interface{} { if input == nil { return nil } for _, item := range *input { - if retentionRule, ok := item.AsAzureRetentionRule(); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { - if retentionRule.Lifecycles != nil && len(*retentionRule.Lifecycles) > 0 { - if deleteOption, ok := (*retentionRule.Lifecycles)[0].DeleteAfter.AsAbsoluteDeleteOption(); ok { - return *deleteOption.Duration + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { + if retentionRule.Lifecycles != nil && len(retentionRule.Lifecycles) > 0 { + if deleteOption, ok := (retentionRule.Lifecycles)[0].DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + return deleteOption.Duration } } } @@ -373,41 +363,38 @@ func flattenBackupPolicyDiskDefaultRetentionRuleDuration(input *[]dataprotection return nil } -func flattenBackupPolicyDiskRetentionRuleArray(input *[]dataprotection.BasicBasePolicyRule) []interface{} { +func flattenBackupPolicyDiskRetentionRuleArray(input *[]backuppolicies.BasePolicyRule) []interface{} { results := make([]interface{}, 0) if input == nil { return results } - var taggingCriterias []dataprotection.TaggingCriteria + var taggingCriterias []backuppolicies.TaggingCriteria for _, item := range *input { - if backupRule, ok := item.AsAzureBackupRule(); ok { - if trigger, ok := backupRule.Trigger.AsScheduleBasedTriggerContext(); ok { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { + if trigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { if trigger.TaggingCriteria != nil { - taggingCriterias = *trigger.TaggingCriteria + taggingCriterias = trigger.TaggingCriteria } } } } for _, item := range *input { - if retentionRule, ok := item.AsAzureRetentionRule(); ok && (retentionRule.IsDefault == nil || !*retentionRule.IsDefault) { - var name string - if retentionRule.Name != nil { - name = *retentionRule.Name - } + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok && (retentionRule.IsDefault == nil || !*retentionRule.IsDefault) { + name := retentionRule.Name var taggingPriority int64 var taggingCriteria []interface{} for _, criteria := range taggingCriterias { - if criteria.TagInfo != nil && criteria.TagInfo.TagName != nil && strings.EqualFold(*criteria.TagInfo.TagName, name) { - taggingPriority = *criteria.TaggingPriority + if strings.EqualFold(criteria.TagInfo.TagName, name) { + taggingPriority = criteria.TaggingPriority taggingCriteria = flattenBackupPolicyDiskBackupCriteriaArray(criteria.Criteria) } } var duration string - if retentionRule.Lifecycles != nil && len(*retentionRule.Lifecycles) > 0 { - if deleteOption, ok := (*retentionRule.Lifecycles)[0].DeleteAfter.AsAbsoluteDeleteOption(); ok { - duration = *deleteOption.Duration + if retentionRule.Lifecycles != nil && len(retentionRule.Lifecycles) > 0 { + if deleteOption, ok := (retentionRule.Lifecycles)[0].DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + duration = deleteOption.Duration } } results = append(results, map[string]interface{}{ @@ -421,14 +408,14 @@ func flattenBackupPolicyDiskRetentionRuleArray(input *[]dataprotection.BasicBase return results } -func flattenBackupPolicyDiskBackupCriteriaArray(input *[]dataprotection.BasicBackupCriteria) []interface{} { +func flattenBackupPolicyDiskBackupCriteriaArray(input *[]backuppolicies.BackupCriteria) []interface{} { results := make([]interface{}, 0) if input == nil { return results } for _, item := range *input { - if criteria, ok := item.AsScheduleBasedBackupCriteria(); ok { + if criteria, ok := item.(backuppolicies.ScheduleBasedBackupCriteria); ok { var absoluteCriteria string if criteria.AbsoluteCriteria != nil && len(*criteria.AbsoluteCriteria) > 0 { absoluteCriteria = string((*criteria.AbsoluteCriteria)[0]) diff --git a/internal/services/dataprotection/data_protection_backup_policy_disk_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_disk_resource_test.go index 19291fa48421..b4ea6899ca91 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_disk_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_policy_disk_resource_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -87,13 +88,13 @@ func TestAccDataProtectionBackupPolicyDisk_update(t *testing.T) { } func (r DataProtectionBackupPolicyDiskResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { - id, err := parse.BackupPolicyID(state.ID) + id, err := backuppolicies.ParseBackupPoliciesID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) diff --git a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go index 7d4a3ecf50f5..45ff75ddcd54 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go +++ b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource.go @@ -7,13 +7,12 @@ import ( "strings" "time" - "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -34,7 +33,7 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.BackupPolicyID(id) + _, err := backuppolicies.ParseBackupPoliciesID(id) return err }), @@ -105,11 +104,11 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(dataprotection.AbsoluteMarkerAllBackup), - string(dataprotection.AbsoluteMarkerFirstOfDay), - string(dataprotection.AbsoluteMarkerFirstOfMonth), - string(dataprotection.AbsoluteMarkerFirstOfWeek), - string(dataprotection.AbsoluteMarkerFirstOfYear), + string(backuppolicies.AbsoluteMarkerAllBackup), + string(backuppolicies.AbsoluteMarkerFirstOfDay), + string(backuppolicies.AbsoluteMarkerFirstOfMonth), + string(backuppolicies.AbsoluteMarkerFirstOfWeek), + string(backuppolicies.AbsoluteMarkerFirstOfYear), }, false), }, @@ -154,11 +153,11 @@ func resourceDataProtectionBackupPolicyPostgreSQL() *pluginsdk.Resource { Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, ValidateFunc: validation.StringInSlice([]string{ - string(dataprotection.WeekNumberFirst), - string(dataprotection.WeekNumberSecond), - string(dataprotection.WeekNumberThird), - string(dataprotection.WeekNumberFourth), - string(dataprotection.WeekNumberLast), + string(backuppolicies.WeekNumberFirst), + string(backuppolicies.WeekNumberSecond), + string(backuppolicies.WeekNumberThird), + string(backuppolicies.WeekNumberFourth), + string(backuppolicies.WeekNumberLast), }, false), }, }, @@ -188,32 +187,35 @@ func resourceDataProtectionBackupPolicyPostgreSQLCreate(d *pluginsdk.ResourceDat resourceGroup := d.Get("resource_group_name").(string) vaultName := d.Get("vault_name").(string) - id := parse.NewBackupPolicyID(subscriptionId, resourceGroup, vaultName, name) + id := backuppolicies.NewBackupPoliciesID(subscriptionId, resourceGroup, vaultName, name) - existing, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing DataProtection BackupPolicy (%q): %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_data_protection_backup_policy_postgresql", id.ID()) } - taggingCriteria := expandBackupPolicyPostgreSQLTaggingCriteriaArray(d.Get("retention_rule").([]interface{})) - policyRules := make([]dataprotection.BasicBasePolicyRule, 0) + taggingCriteria, err := expandBackupPolicyPostgreSQLTaggingCriteriaArray(d.Get("retention_rule").([]interface{})) + if err != nil { + return err + } + + policyRules := make([]backuppolicies.BasePolicyRule, 0) policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureBackupRuleArray(d.Get("backup_repeating_time_intervals").([]interface{}), taggingCriteria)...) policyRules = append(policyRules, expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(d.Get("default_retention_duration"))) policyRules = append(policyRules, expandBackupPolicyPostgreSQLAzureRetentionRuleArray(d.Get("retention_rule").([]interface{}))...) - parameters := dataprotection.BaseBackupPolicyResource{ - Properties: &dataprotection.BackupPolicy{ - PolicyRules: &policyRules, - DatasourceTypes: &[]string{"Microsoft.DBforPostgreSQL/servers/databases"}, - ObjectType: dataprotection.ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy, + parameters := backuppolicies.BaseBackupPolicyResource{ + Properties: &backuppolicies.BackupPolicy{ + PolicyRules: policyRules, + DatasourceTypes: []string{"Microsoft.DBforPostgreSQL/servers/databases"}, }, } - if _, err := client.CreateOrUpdate(ctx, id.BackupVaultName, id.ResourceGroup, id.Name, parameters); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating DataProtection BackupPolicy (%q): %+v", id, err) } @@ -226,33 +228,36 @@ func resourceDataProtectionBackupPolicyPostgreSQLRead(d *pluginsdk.ResourceData, ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] dataprotection %q does not exist - removing from state", d.Id()) d.SetId("") return nil } return fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("vault_name", id.BackupVaultName) - if resp.Properties != nil { - if props, ok := resp.Properties.AsBackupPolicy(); ok { - if err := d.Set("backup_repeating_time_intervals", flattenBackupPolicyPostgreSQLBackupRuleArray(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `backup_rule`: %+v", err) - } - if err := d.Set("default_retention_duration", flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `default_retention_duration`: %+v", err) - } - if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRuleArray(props.PolicyRules)); err != nil { - return fmt.Errorf("setting `retention_rule`: %+v", err) + d.Set("name", id.BackupPolicyName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("vault_name", id.VaultName) + + if resp.Model != nil { + if resp.Model.Properties != nil { + if props, ok := resp.Model.Properties.(backuppolicies.BackupPolicy); ok { + if err := d.Set("backup_repeating_time_intervals", flattenBackupPolicyPostgreSQLBackupRuleArray(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `backup_rule`: %+v", err) + } + if err := d.Set("default_retention_duration", flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `default_retention_duration`: %+v", err) + } + if err := d.Set("retention_rule", flattenBackupPolicyPostgreSQLRetentionRuleArray(&props.PolicyRules)); err != nil { + return fmt.Errorf("setting `retention_rule`: %+v", err) + } } } } @@ -264,13 +269,13 @@ func resourceDataProtectionBackupPolicyPostgreSQLDelete(d *pluginsdk.ResourceDat ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupPolicyID(d.Id()) + id, err := backuppolicies.ParseBackupPoliciesID(d.Id()) if err != nil { return err } - if resp, err := client.Delete(ctx, id.BackupVaultName, id.ResourceGroup, id.Name); err != nil { - if utils.ResponseWasNotFound(resp) { + if resp, err := client.Delete(ctx, *id); err != nil { + if response.WasNotFound(resp.HttpResponse) { return nil } @@ -279,50 +284,45 @@ func resourceDataProtectionBackupPolicyPostgreSQLDelete(d *pluginsdk.ResourceDat return nil } -func expandBackupPolicyPostgreSQLAzureBackupRuleArray(input []interface{}, taggingCriteria *[]dataprotection.TaggingCriteria) []dataprotection.BasicBasePolicyRule { - results := make([]dataprotection.BasicBasePolicyRule, 0) - results = append(results, dataprotection.AzureBackupRule{ - Name: utils.String("BackupIntervals"), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule, - DataStore: &dataprotection.DataStoreInfoBase{ - DataStoreType: dataprotection.DataStoreTypesVaultStore, - ObjectType: utils.String("DataStoreInfoBase"), +func expandBackupPolicyPostgreSQLAzureBackupRuleArray(input []interface{}, taggingCriteria *[]backuppolicies.TaggingCriteria) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) + results = append(results, backuppolicies.AzureBackupRule{ + Name: "BackupIntervals", + DataStore: backuppolicies.DataStoreInfoBase{ + DataStoreType: backuppolicies.DataStoreTypesVaultStore, + ObjectType: "DataStoreInfoBase", }, - BackupParameters: &dataprotection.AzureBackupParams{ - BackupType: utils.String("Full"), - ObjectType: dataprotection.ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams, + BackupParameters: &backuppolicies.AzureBackupParams{ + BackupType: "Full", }, - Trigger: dataprotection.ScheduleBasedTriggerContext{ - Schedule: &dataprotection.BackupSchedule{ - RepeatingTimeIntervals: utils.ExpandStringSlice(input), + Trigger: backuppolicies.ScheduleBasedTriggerContext{ + Schedule: backuppolicies.BackupSchedule{ + RepeatingTimeIntervals: *utils.ExpandStringSlice(input), }, - TaggingCriteria: taggingCriteria, - ObjectType: dataprotection.ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext, + TaggingCriteria: *taggingCriteria, }, }) return results } -func expandBackupPolicyPostgreSQLAzureRetentionRuleArray(input []interface{}) []dataprotection.BasicBasePolicyRule { - results := make([]dataprotection.BasicBasePolicyRule, 0) +func expandBackupPolicyPostgreSQLAzureRetentionRuleArray(input []interface{}) []backuppolicies.BasePolicyRule { + results := make([]backuppolicies.BasePolicyRule, 0) for _, item := range input { v := item.(map[string]interface{}) - results = append(results, dataprotection.AzureRetentionRule{ - Name: utils.String(v["name"].(string)), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, - IsDefault: utils.Bool(false), - Lifecycles: &[]dataprotection.SourceLifeCycle{ + results = append(results, backuppolicies.AzureRetentionRule{ + Name: v["name"].(string), + IsDefault: utils.Bool(false), + Lifecycles: []backuppolicies.SourceLifeCycle{ { - DeleteAfter: dataprotection.AbsoluteDeleteOption{ - Duration: utils.String(v["duration"].(string)), - ObjectType: dataprotection.ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: v["duration"].(string), }, - SourceDataStore: &dataprotection.DataStoreInfoBase{ + SourceDataStore: backuppolicies.DataStoreInfoBase{ DataStoreType: "VaultStore", - ObjectType: utils.String("DataStoreInfoBase"), + ObjectType: "DataStoreInfoBase", }, - TargetDataStoreCopySettings: &[]dataprotection.TargetCopySetting{}, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, }, }, }) @@ -330,119 +330,122 @@ func expandBackupPolicyPostgreSQLAzureRetentionRuleArray(input []interface{}) [] return results } -func expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(input interface{}) dataprotection.BasicBasePolicyRule { - return dataprotection.AzureRetentionRule{ - Name: utils.String("Default"), - ObjectType: dataprotection.ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, - IsDefault: utils.Bool(true), - Lifecycles: &[]dataprotection.SourceLifeCycle{ +func expandBackupPolicyPostgreSQLDefaultAzureRetentionRule(input interface{}) backuppolicies.BasePolicyRule { + return backuppolicies.AzureRetentionRule{ + Name: "Default", + IsDefault: utils.Bool(true), + Lifecycles: []backuppolicies.SourceLifeCycle{ { - DeleteAfter: dataprotection.AbsoluteDeleteOption{ - Duration: utils.String(input.(string)), - ObjectType: dataprotection.ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, + DeleteAfter: backuppolicies.AbsoluteDeleteOption{ + Duration: input.(string), }, - SourceDataStore: &dataprotection.DataStoreInfoBase{ + SourceDataStore: backuppolicies.DataStoreInfoBase{ DataStoreType: "VaultStore", - ObjectType: utils.String("DataStoreInfoBase"), + ObjectType: "DataStoreInfoBase", }, - TargetDataStoreCopySettings: &[]dataprotection.TargetCopySetting{}, + TargetDataStoreCopySettings: &[]backuppolicies.TargetCopySetting{}, }, }, } } -func expandBackupPolicyPostgreSQLTaggingCriteriaArray(input []interface{}) *[]dataprotection.TaggingCriteria { - results := []dataprotection.TaggingCriteria{ +func expandBackupPolicyPostgreSQLTaggingCriteriaArray(input []interface{}) (*[]backuppolicies.TaggingCriteria, error) { + results := []backuppolicies.TaggingCriteria{ + { Criteria: nil, - IsDefault: utils.Bool(true), - TaggingPriority: utils.Int64(99), - TagInfo: &dataprotection.RetentionTag{ - ID: utils.String("Default_"), - TagName: utils.String("Default"), + IsDefault: true, + TaggingPriority: 99, + TagInfo: backuppolicies.RetentionTag{ + Id: utils.String("Default_"), + TagName: "Default", }, }, } for _, item := range input { v := item.(map[string]interface{}) - results = append(results, dataprotection.TaggingCriteria{ - Criteria: expandBackupPolicyPostgreSQLCriteriaArray(v["criteria"].([]interface{})), - IsDefault: utils.Bool(false), - TaggingPriority: utils.Int64(int64(v["priority"].(int))), - TagInfo: &dataprotection.RetentionTag{ - ID: utils.String(v["name"].(string) + "_"), - TagName: utils.String(v["name"].(string)), + result := backuppolicies.TaggingCriteria{ + IsDefault: false, + TaggingPriority: int64(v["priority"].(int)), + TagInfo: backuppolicies.RetentionTag{ + Id: utils.String(v["name"].(string) + "_"), + TagName: v["name"].(string), }, - }) + } + + criteria, err := expandBackupPolicyPostgreSQLCriteriaArray(v["criteria"].([]interface{})) + if err != nil { + return nil, err + } + result.Criteria = criteria + + results = append(results, result) } - return &results + return &results, nil } -func expandBackupPolicyPostgreSQLCriteriaArray(input []interface{}) *[]dataprotection.BasicBackupCriteria { - results := make([]dataprotection.BasicBackupCriteria, 0) +func expandBackupPolicyPostgreSQLCriteriaArray(input []interface{}) (*[]backuppolicies.BackupCriteria, error) { + if len(input) == 0 || input[0] == nil { + return nil, fmt.Errorf("criteria is a required field, cannot leave blank") + } + results := make([]backuppolicies.BackupCriteria, 0) + for _, item := range input { v := item.(map[string]interface{}) - var absoluteCriteria []dataprotection.AbsoluteMarker + var absoluteCriteria []backuppolicies.AbsoluteMarker if absoluteCriteriaRaw := v["absolute_criteria"].(string); len(absoluteCriteriaRaw) > 0 { - absoluteCriteria = []dataprotection.AbsoluteMarker{dataprotection.AbsoluteMarker(absoluteCriteriaRaw)} + absoluteCriteria = []backuppolicies.AbsoluteMarker{backuppolicies.AbsoluteMarker(absoluteCriteriaRaw)} } - var daysOfWeek []dataprotection.DayOfWeek + var daysOfWeek []backuppolicies.DayOfWeek if v["days_of_week"].(*pluginsdk.Set).Len() > 0 { - daysOfWeek = make([]dataprotection.DayOfWeek, 0) + daysOfWeek = make([]backuppolicies.DayOfWeek, 0) for _, value := range v["days_of_week"].(*pluginsdk.Set).List() { - daysOfWeek = append(daysOfWeek, dataprotection.DayOfWeek(value.(string))) + daysOfWeek = append(daysOfWeek, backuppolicies.DayOfWeek(value.(string))) } } - var monthsOfYear []dataprotection.Month + var monthsOfYear []backuppolicies.Month if v["months_of_year"].(*pluginsdk.Set).Len() > 0 { - monthsOfYear = make([]dataprotection.Month, 0) + monthsOfYear = make([]backuppolicies.Month, 0) for _, value := range v["months_of_year"].(*pluginsdk.Set).List() { - monthsOfYear = append(monthsOfYear, dataprotection.Month(value.(string))) + monthsOfYear = append(monthsOfYear, backuppolicies.Month(value.(string))) } } - var weeksOfMonth []dataprotection.WeekNumber + var weeksOfMonth []backuppolicies.WeekNumber if v["weeks_of_month"].(*pluginsdk.Set).Len() > 0 { - weeksOfMonth = make([]dataprotection.WeekNumber, 0) + weeksOfMonth = make([]backuppolicies.WeekNumber, 0) for _, value := range v["weeks_of_month"].(*pluginsdk.Set).List() { - weeksOfMonth = append(weeksOfMonth, dataprotection.WeekNumber(value.(string))) + weeksOfMonth = append(weeksOfMonth, backuppolicies.WeekNumber(value.(string))) } } - var scheduleTimes []date.Time + var scheduleTimes *[]string if v["scheduled_backup_times"].(*pluginsdk.Set).Len() > 0 { - scheduleTimes = make([]date.Time, 0) - for _, value := range v["scheduled_backup_times"].(*pluginsdk.Set).List() { - t, _ := time.Parse(time.RFC3339, value.(string)) - scheduleTimes = append(scheduleTimes, date.Time{Time: t}) - } + scheduleTimes = utils.ExpandStringSlice(v["scheduled_backup_times"].(*pluginsdk.Set).List()) } - results = append(results, dataprotection.ScheduleBasedBackupCriteria{ + results = append(results, backuppolicies.ScheduleBasedBackupCriteria{ AbsoluteCriteria: &absoluteCriteria, DaysOfMonth: nil, DaysOfTheWeek: &daysOfWeek, MonthsOfYear: &monthsOfYear, - ScheduleTimes: &scheduleTimes, + ScheduleTimes: scheduleTimes, WeeksOfTheMonth: &weeksOfMonth, - ObjectType: dataprotection.ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria, }) } - return &results + return &results, nil } -func flattenBackupPolicyPostgreSQLBackupRuleArray(input *[]dataprotection.BasicBasePolicyRule) []interface{} { +func flattenBackupPolicyPostgreSQLBackupRuleArray(input *[]backuppolicies.BasePolicyRule) []interface{} { if input == nil { return make([]interface{}, 0) } for _, item := range *input { - if backupRule, ok := item.AsAzureBackupRule(); ok { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { if backupRule.Trigger != nil { - if scheduleBasedTrigger, ok := backupRule.Trigger.AsScheduleBasedTriggerContext(); ok { - if scheduleBasedTrigger.Schedule != nil { - return utils.FlattenStringSlice(scheduleBasedTrigger.Schedule.RepeatingTimeIntervals) - } + if scheduleBasedTrigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { + return utils.FlattenStringSlice(&scheduleBasedTrigger.Schedule.RepeatingTimeIntervals) } } } @@ -450,16 +453,16 @@ func flattenBackupPolicyPostgreSQLBackupRuleArray(input *[]dataprotection.BasicB return make([]interface{}, 0) } -func flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(input *[]dataprotection.BasicBasePolicyRule) interface{} { +func flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(input *[]backuppolicies.BasePolicyRule) interface{} { if input == nil { return nil } for _, item := range *input { - if retentionRule, ok := item.AsAzureRetentionRule(); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { - if retentionRule.Lifecycles != nil && len(*retentionRule.Lifecycles) > 0 { - if deleteOption, ok := (*retentionRule.Lifecycles)[0].DeleteAfter.AsAbsoluteDeleteOption(); ok { - return *deleteOption.Duration + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok && retentionRule.IsDefault != nil && *retentionRule.IsDefault { + if retentionRule.Lifecycles != nil && len(retentionRule.Lifecycles) > 0 { + if deleteOption, ok := (retentionRule.Lifecycles)[0].DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + return deleteOption.Duration } } } @@ -467,41 +470,38 @@ func flattenBackupPolicyPostgreSQLDefaultRetentionRuleDuration(input *[]dataprot return nil } -func flattenBackupPolicyPostgreSQLRetentionRuleArray(input *[]dataprotection.BasicBasePolicyRule) []interface{} { +func flattenBackupPolicyPostgreSQLRetentionRuleArray(input *[]backuppolicies.BasePolicyRule) []interface{} { results := make([]interface{}, 0) if input == nil { return results } - var taggingCriterias []dataprotection.TaggingCriteria + var taggingCriterias []backuppolicies.TaggingCriteria for _, item := range *input { - if backupRule, ok := item.AsAzureBackupRule(); ok { - if trigger, ok := backupRule.Trigger.AsScheduleBasedTriggerContext(); ok { + if backupRule, ok := item.(backuppolicies.AzureBackupRule); ok { + if trigger, ok := backupRule.Trigger.(backuppolicies.ScheduleBasedTriggerContext); ok { if trigger.TaggingCriteria != nil { - taggingCriterias = *trigger.TaggingCriteria + taggingCriterias = trigger.TaggingCriteria } } } } for _, item := range *input { - if retentionRule, ok := item.AsAzureRetentionRule(); ok && (retentionRule.IsDefault == nil || !*retentionRule.IsDefault) { - var name string - if retentionRule.Name != nil { - name = *retentionRule.Name - } + if retentionRule, ok := item.(backuppolicies.AzureRetentionRule); ok && (retentionRule.IsDefault == nil || !*retentionRule.IsDefault) { + name := retentionRule.Name var taggingPriority int64 var taggingCriteria []interface{} for _, criteria := range taggingCriterias { - if criteria.TagInfo != nil && criteria.TagInfo.TagName != nil && strings.EqualFold(*criteria.TagInfo.TagName, name) { - taggingPriority = *criteria.TaggingPriority + if strings.EqualFold(criteria.TagInfo.TagName, name) { + taggingPriority = criteria.TaggingPriority taggingCriteria = flattenBackupPolicyPostgreSQLBackupCriteriaArray(criteria.Criteria) } } var duration string - if retentionRule.Lifecycles != nil && len(*retentionRule.Lifecycles) > 0 { - if deleteOption, ok := (*retentionRule.Lifecycles)[0].DeleteAfter.AsAbsoluteDeleteOption(); ok { - duration = *deleteOption.Duration + if retentionRule.Lifecycles != nil && len(retentionRule.Lifecycles) > 0 { + if deleteOption, ok := (retentionRule.Lifecycles)[0].DeleteAfter.(backuppolicies.AbsoluteDeleteOption); ok { + duration = deleteOption.Duration } } results = append(results, map[string]interface{}{ @@ -515,14 +515,14 @@ func flattenBackupPolicyPostgreSQLRetentionRuleArray(input *[]dataprotection.Bas return results } -func flattenBackupPolicyPostgreSQLBackupCriteriaArray(input *[]dataprotection.BasicBackupCriteria) []interface{} { +func flattenBackupPolicyPostgreSQLBackupCriteriaArray(input *[]backuppolicies.BackupCriteria) []interface{} { results := make([]interface{}, 0) if input == nil { return results } for _, item := range *input { - if criteria, ok := item.AsScheduleBasedBackupCriteria(); ok { + if criteria, ok := item.(backuppolicies.ScheduleBasedBackupCriteria); ok { var absoluteCriteria string if criteria.AbsoluteCriteria != nil && len(*criteria.AbsoluteCriteria) > 0 { absoluteCriteria = string((*criteria.AbsoluteCriteria)[0]) @@ -552,7 +552,7 @@ func flattenBackupPolicyPostgreSQLBackupCriteriaArray(input *[]dataprotection.Ba if criteria.ScheduleTimes != nil { scheduleTimes = make([]string, 0) for _, item := range *criteria.ScheduleTimes { - scheduleTimes = append(scheduleTimes, item.String()) + scheduleTimes = append(scheduleTimes, item) } } diff --git a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go index be3f79289d2d..8a4423757c44 100644 --- a/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_policy_postgresql_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backuppolicies" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -86,13 +87,13 @@ func TestAccDataProtectionBackupPolicyPostgreSQL_update(t *testing.T) { } func (r DataProtectionBackupPolicyPostgreSQLResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.BackupPolicyID(state.ID) + id, err := backuppolicies.ParseBackupPoliciesID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, id.BackupVaultName, id.ResourceGroup, id.Name) + resp, err := client.DataProtection.BackupPolicyClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupPolicy (%q): %+v", id, err) diff --git a/internal/services/dataprotection/data_protection_backup_vault_data_source.go b/internal/services/dataprotection/data_protection_backup_vault_data_source.go index 9efe7a7a14b2..e64b9b9d848f 100644 --- a/internal/services/dataprotection/data_protection_backup_vault_data_source.go +++ b/internal/services/dataprotection/data_protection_backup_vault_data_source.go @@ -6,17 +6,16 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupvaults" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceDataProtectionBackupVault() *pluginsdk.Resource { @@ -28,7 +27,7 @@ func dataSourceDataProtectionBackupVault() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.BackupVaultID(id) + _, err := backupvaults.ParseBackupVaultID(id) return err }), @@ -72,11 +71,11 @@ func dataSourceDataProtectionBackupVaultRead(d *pluginsdk.ResourceData, meta int name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - id := parse.NewBackupVaultID(subscriptionId, resourceGroup, name) + id := backupvaults.NewBackupVaultID(subscriptionId, resourceGroup, name) - resp, err := client.Get(ctx, id.Name, id.ResourceGroup) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] DataProtection BackupVault %q does not exist - removing from state", d.Id()) d.SetId("") return nil @@ -85,32 +84,40 @@ func dataSourceDataProtectionBackupVaultRead(d *pluginsdk.ResourceData, meta int } d.SetId(id.ID()) - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) - if props := resp.Properties; props != nil { - if props.StorageSettings != nil && len(*props.StorageSettings) > 0 { - d.Set("datastore_type", (*props.StorageSettings)[0].DatastoreType) - d.Set("redundancy", (*props.StorageSettings)[0].Type) + d.Set("name", id.VaultName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(&model.Location)) + + props := model.Properties + if props.StorageSettings != nil && len(props.StorageSettings) > 0 { + d.Set("datastore_type", (props.StorageSettings)[0].DatastoreType) + d.Set("redundancy", (props.StorageSettings)[0].Type) + } + + if err = d.Set("identity", dataSourceFlattenBackupVaultDppIdentityDetails(model.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if err = tags.FlattenAndSet(d, flattenTags(model.Tags)); err != nil { + return err } } - if err := d.Set("identity", dataSourceFlattenBackupVaultDppIdentityDetails(resp.Identity)); err != nil { - return fmt.Errorf("setting `identity`: %+v", err) - } - return tags.FlattenAndSet(d, resp.Tags) + return nil } -func dataSourceFlattenBackupVaultDppIdentityDetails(input *dataprotection.DppIdentityDetails) []interface{} { +func dataSourceFlattenBackupVaultDppIdentityDetails(input *backupvaults.DppIdentityDetails) []interface{} { var config *identity.SystemAssigned if input != nil { principalId := "" - if input.PrincipalID != nil { - principalId = *input.PrincipalID + if input.PrincipalId != nil { + principalId = *input.PrincipalId } tenantId := "" - if input.TenantID != nil { - tenantId = *input.TenantID + if input.TenantId != nil { + tenantId = *input.TenantId } config = &identity.SystemAssigned{ Type: identity.Type(*input.Type), diff --git a/internal/services/dataprotection/data_protection_backup_vault_data_source_test.go b/internal/services/dataprotection/data_protection_backup_vault_data_source_test.go index de7e0d635861..9fd27450053d 100644 --- a/internal/services/dataprotection/data_protection_backup_vault_data_source_test.go +++ b/internal/services/dataprotection/data_protection_backup_vault_data_source_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupvaults" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -36,13 +37,13 @@ func TestAccDataProtectionBackupVaultDataSource_complete(t *testing.T) { } func (r DataProtectionBackupVaultDataSource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.BackupVaultID(state.ID) + id, err := backupvaults.ParseBackupVaultID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupVaultClient.Get(ctx, id.Name, id.ResourceGroup) + resp, err := client.DataProtection.BackupVaultClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupVault (%q): %+v", id, err) diff --git a/internal/services/dataprotection/data_protection_backup_vault_resource.go b/internal/services/dataprotection/data_protection_backup_vault_resource.go index 67e5be458aac..96dfc7f637a1 100644 --- a/internal/services/dataprotection/data_protection_backup_vault_resource.go +++ b/internal/services/dataprotection/data_protection_backup_vault_resource.go @@ -6,14 +6,14 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupvaults" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/legacysdk/dataprotection" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -36,7 +36,7 @@ func resourceDataProtectionBackupVault() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.BackupVaultID(id) + _, err := backupvaults.ParseBackupVaultIDInsensitively(id) return err }), @@ -60,9 +60,9 @@ func resourceDataProtectionBackupVault() *pluginsdk.Resource { Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(dataprotection.StorageSettingStoreTypesArchiveStore), - string(dataprotection.StorageSettingStoreTypesSnapshotStore), - string(dataprotection.StorageSettingStoreTypesVaultStore), + string(backupvaults.StorageSettingStoreTypesArchiveStore), + string(backupvaults.StorageSettingStoreTypesSnapshotStore), + string(backupvaults.StorageSettingStoreTypesVaultStore), }, false), }, @@ -71,8 +71,8 @@ func resourceDataProtectionBackupVault() *pluginsdk.Resource { Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(dataprotection.StorageSettingTypesGeoRedundant), - string(dataprotection.StorageSettingTypesLocallyRedundant), + string(backupvaults.StorageSettingTypesGeoRedundant), + string(backupvaults.StorageSettingTypesLocallyRedundant), }, false), }, @@ -92,16 +92,16 @@ func resourceDataProtectionBackupVaultCreateUpdate(d *pluginsdk.ResourceData, me name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - id := parse.NewBackupVaultID(subscriptionId, resourceGroup, name) + id := backupvaults.NewBackupVaultID(subscriptionId, resourceGroup, name) if d.IsNewResource() { - existing, err := client.Get(ctx, id.Name, id.ResourceGroup) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for existing DataProtection BackupVault (%q): %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_data_protection_backup_vault", id.ID()) } } @@ -111,28 +111,27 @@ func resourceDataProtectionBackupVaultCreateUpdate(d *pluginsdk.ResourceData, me return fmt.Errorf("expanding `identity`: %+v", err) } - parameters := dataprotection.BackupVaultResource{ - Location: utils.String(location.Normalize(d.Get("location").(string))), - Properties: &dataprotection.BackupVault{ - StorageSettings: &[]dataprotection.StorageSetting{ + datastoreType := backupvaults.StorageSettingStoreTypes(d.Get("datastore_type").(string)) + storageSettingType := backupvaults.StorageSettingTypes(d.Get("redundancy").(string)) + + parameters := backupvaults.BackupVaultResource{ + Location: location.Normalize(d.Get("location").(string)), + Properties: backupvaults.BackupVault{ + StorageSettings: []backupvaults.StorageSetting{ { - DatastoreType: dataprotection.StorageSettingStoreTypes(d.Get("datastore_type").(string)), - Type: dataprotection.StorageSettingTypes(d.Get("redundancy").(string)), + DatastoreType: &datastoreType, + Type: &storageSettingType, }, }, }, Identity: expandedIdentity, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + Tags: expandTags(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, id.Name, id.ResourceGroup, parameters) + err = client.CreateOrUpdateThenPoll(ctx, id, parameters) if err != nil { return fmt.Errorf("creating DataProtection BackupVault (%q): %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of the DataProtection BackupVault (%q): %+v", id, err) - } - d.SetId(id.ID()) return resourceDataProtectionBackupVaultRead(d, meta) } @@ -142,33 +141,40 @@ func resourceDataProtectionBackupVaultRead(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupVaultID(d.Id()) + id, err := backupvaults.ParseBackupVaultID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.Name, id.ResourceGroup) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] DataProtection BackupVault %q does not exist - removing from state", d.Id()) d.SetId("") return nil } return fmt.Errorf("retrieving DataProtection BackupVault (%q): %+v", id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) - if props := resp.Properties; props != nil { - if props.StorageSettings != nil && len(*props.StorageSettings) > 0 { - d.Set("datastore_type", (*props.StorageSettings)[0].DatastoreType) - d.Set("redundancy", (*props.StorageSettings)[0].Type) + d.Set("name", id.VaultName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(&model.Location)) + props := model.Properties + if props.StorageSettings != nil && len(props.StorageSettings) > 0 { + d.Set("datastore_type", (props.StorageSettings)[0].DatastoreType) + d.Set("redundancy", (props.StorageSettings)[0].Type) + } + + if err = d.Set("identity", flattenBackupVaultDppIdentityDetails(model.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + if err = tags.FlattenAndSet(d, flattenTags(model.Tags)); err != nil { + return err } } - if err := d.Set("identity", flattenBackupVaultDppIdentityDetails(resp.Identity)); err != nil { - return fmt.Errorf("setting `identity`: %+v", err) - } - return tags.FlattenAndSet(d, resp.Tags) + + return nil } func resourceDataProtectionBackupVaultDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -176,13 +182,13 @@ func resourceDataProtectionBackupVaultDelete(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.BackupVaultID(d.Id()) + id, err := backupvaults.ParseBackupVaultID(d.Id()) if err != nil { return err } - if resp, err := client.Delete(ctx, id.Name, id.ResourceGroup); err != nil { - if utils.ResponseWasNotFound(resp) { + if resp, err := client.Delete(ctx, *id); err != nil { + if response.WasNotFound(resp.HttpResponse) { return nil } return fmt.Errorf("deleting DataProtection BackupVault (%q): %+v", id, err) @@ -190,28 +196,28 @@ func resourceDataProtectionBackupVaultDelete(d *pluginsdk.ResourceData, meta int return nil } -func expandBackupVaultDppIdentityDetails(input []interface{}) (*dataprotection.DppIdentityDetails, error) { +func expandBackupVaultDppIdentityDetails(input []interface{}) (*backupvaults.DppIdentityDetails, error) { config, err := identity.ExpandSystemAssigned(input) if err != nil { return nil, err } - return &dataprotection.DppIdentityDetails{ + return &backupvaults.DppIdentityDetails{ Type: utils.String(string(config.Type)), }, nil } -func flattenBackupVaultDppIdentityDetails(input *dataprotection.DppIdentityDetails) []interface{} { +func flattenBackupVaultDppIdentityDetails(input *backupvaults.DppIdentityDetails) []interface{} { var config *identity.SystemAssigned if input != nil { principalId := "" - if input.PrincipalID != nil { - principalId = *input.PrincipalID + if input.PrincipalId != nil { + principalId = *input.PrincipalId } tenantId := "" - if input.TenantID != nil { - tenantId = *input.TenantID + if input.TenantId != nil { + tenantId = *input.TenantId } config = &identity.SystemAssigned{ Type: identity.Type(*input.Type), diff --git a/internal/services/dataprotection/data_protection_backup_vault_resource_test.go b/internal/services/dataprotection/data_protection_backup_vault_resource_test.go index 19688e1e7706..dcc57f542838 100644 --- a/internal/services/dataprotection/data_protection_backup_vault_resource_test.go +++ b/internal/services/dataprotection/data_protection_backup_vault_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2022-04-01/backupvaults" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -111,13 +112,13 @@ func TestAccDataProtectionBackupVault_updateIdentity(t *testing.T) { } func (r DataProtectionBackupVaultResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.BackupVaultID(state.ID) + id, err := backupvaults.ParseBackupVaultID(state.ID) if err != nil { return nil, err } - resp, err := client.DataProtection.BackupVaultClient.Get(ctx, id.Name, id.ResourceGroup) + resp, err := client.DataProtection.BackupVaultClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, fmt.Errorf("retrieving DataProtection BackupVault (%q): %+v", id, err) diff --git a/internal/services/dataprotection/legacysdk/dataprotection/backupinstances.go b/internal/services/dataprotection/legacysdk/dataprotection/backupinstances.go deleted file mode 100644 index 20d27ac0997d..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/backupinstances.go +++ /dev/null @@ -1,927 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/validation" - "github.com/Azure/go-autorest/tracing" -) - -// BackupInstancesClient is the open API 2.0 Specs for Azure Data Protection service -type BackupInstancesClient struct { - BaseClient -} - -// NewBackupInstancesClient creates an instance of the BackupInstancesClient client. -func NewBackupInstancesClient(subscriptionID string) BackupInstancesClient { - return NewBackupInstancesClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewBackupInstancesClientWithBaseURI creates an instance of the BackupInstancesClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewBackupInstancesClientWithBaseURI(baseURI string, subscriptionID string) BackupInstancesClient { - return BackupInstancesClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// AdhocBackup trigger adhoc backup -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// parameters - request body for operation -func (client BackupInstancesClient) AdhocBackup(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters TriggerBackupRequest) (result BackupInstancesAdhocBackupFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.AdhocBackup") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - if err := validation.Validate([]validation.Validation{ - { - TargetValue: parameters, - Constraints: []validation.Constraint{{ - Target: "parameters.BackupRuleOptions", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{ - {Target: "parameters.BackupRuleOptions.RuleName", Name: validation.Null, Rule: true, Chain: nil}, - {Target: "parameters.BackupRuleOptions.TriggerOption", Name: validation.Null, Rule: true, Chain: nil}, - }, - }}, - }, - }); err != nil { - return result, validation.NewError("dataprotection.BackupInstancesClient", "AdhocBackup", err.Error()) - } - - req, err := client.AdhocBackupPreparer(ctx, vaultName, resourceGroupName, backupInstanceName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "AdhocBackup", nil, "Failure preparing request") - return - } - - result, err = client.AdhocBackupSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "AdhocBackup", nil, "Failure sending request") - return - } - - return -} - -// AdhocBackupPreparer prepares the AdhocBackup request. -func (client BackupInstancesClient) AdhocBackupPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters TriggerBackupRequest) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/backup", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// AdhocBackupSender sends the AdhocBackup request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) AdhocBackupSender(req *http.Request) (future BackupInstancesAdhocBackupFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// AdhocBackupResponder handles the response to the AdhocBackup request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) AdhocBackupResponder(resp *http.Response) (result OperationJobExtendedInfo, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// CreateOrUpdate create or update a backup instance in a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// parameters - request body for operation -func (client BackupInstancesClient) CreateOrUpdate(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters BackupInstanceResource) (result BackupInstancesCreateOrUpdateFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.CreateOrUpdate") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - if err := validation.Validate([]validation.Validation{ - { - TargetValue: parameters, - Constraints: []validation.Constraint{{ - Target: "parameters.Properties", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.Properties.DataSourceInfo", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{{Target: "parameters.Properties.DataSourceInfo.ResourceID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.Properties.DataSourceSetInfo", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.Properties.DataSourceSetInfo.ResourceID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.Properties.PolicyInfo", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{{Target: "parameters.Properties.PolicyInfo.PolicyID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.Properties.ProtectionStatus", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.Properties.ProtectionStatus.ErrorDetails", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.Properties.ProtectionStatus.ErrorDetails.InnerError", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.Properties.ProtectionStatus.ErrorDetails.InnerError.EmbeddedInnerError", Name: validation.Null, Rule: false, Chain: nil}}, - }, - }, - }, - }, - }, - { - Target: "parameters.Properties.ProtectionErrorDetails", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.Properties.ProtectionErrorDetails.InnerError", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.Properties.ProtectionErrorDetails.InnerError.EmbeddedInnerError", Name: validation.Null, Rule: false, Chain: nil}}, - }, - }, - }, - {Target: "parameters.Properties.ObjectType", Name: validation.Null, Rule: true, Chain: nil}, - }, - }}, - }, - }); err != nil { - return result, validation.NewError("dataprotection.BackupInstancesClient", "CreateOrUpdate", err.Error()) - } - - req, err := client.CreateOrUpdatePreparer(ctx, vaultName, resourceGroupName, backupInstanceName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "CreateOrUpdate", nil, "Failure preparing request") - return - } - - result, err = client.CreateOrUpdateSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "CreateOrUpdate", nil, "Failure sending request") - return - } - - return -} - -// CreateOrUpdatePreparer prepares the CreateOrUpdate request. -func (client BackupInstancesClient) CreateOrUpdatePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters BackupInstanceResource) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPut(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) CreateOrUpdateSender(req *http.Request) (future BackupInstancesCreateOrUpdateFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) CreateOrUpdateResponder(resp *http.Response) (result BackupInstanceResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// Delete delete a backup instance in a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -func (client BackupInstancesClient) Delete(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string) (result BackupInstancesDeleteFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.Delete") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.DeletePreparer(ctx, vaultName, resourceGroupName, backupInstanceName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "Delete", nil, "Failure preparing request") - return - } - - result, err = client.DeleteSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "Delete", nil, "Failure sending request") - return - } - - return -} - -// DeletePreparer prepares the Delete request. -func (client BackupInstancesClient) DeletePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsDelete(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// DeleteSender sends the Delete request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) DeleteSender(req *http.Request) (future BackupInstancesDeleteFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// DeleteResponder handles the response to the Delete request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} - -// Get gets a backup instance with name in a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -func (client BackupInstancesClient) Get(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string) (result BackupInstanceResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, vaultName, resourceGroupName, backupInstanceName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client BackupInstancesClient) GetPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) GetResponder(resp *http.Response) (result BackupInstanceResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// List gets a backup instances belonging to a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupInstancesClient) List(ctx context.Context, vaultName string, resourceGroupName string) (result BackupInstanceResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.List") - defer func() { - sc := -1 - if result.birl.Response.Response != nil { - sc = result.birl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.listNextResults - req, err := client.ListPreparer(ctx, vaultName, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "List", nil, "Failure preparing request") - return - } - - resp, err := client.ListSender(req) - if err != nil { - result.birl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "List", resp, "Failure sending request") - return - } - - result.birl, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "List", resp, "Failure responding to request") - return - } - if result.birl.hasNextLink() && result.birl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// ListPreparer prepares the List request. -func (client BackupInstancesClient) ListPreparer(ctx context.Context, vaultName string, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ListSender sends the List request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) ListSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// ListResponder handles the response to the List request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) ListResponder(resp *http.Response) (result BackupInstanceResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// listNextResults retrieves the next set of results, if any. -func (client BackupInstancesClient) listNextResults(ctx context.Context, lastResults BackupInstanceResourceList) (result BackupInstanceResourceList, err error) { - req, err := lastResults.backupInstanceResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "listNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.ListSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "listNextResults", resp, "Failure sending next results request") - } - result, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "listNextResults", resp, "Failure responding to next results request") - } - return -} - -// ListComplete enumerates all values, automatically crossing page boundaries as required. -func (client BackupInstancesClient) ListComplete(ctx context.Context, vaultName string, resourceGroupName string) (result BackupInstanceResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.List") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.List(ctx, vaultName, resourceGroupName) - return -} - -// TriggerRehydrate rehydrate recovery point for restore for a BackupInstance -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// vaultName - the name of the backup vault. -// parameters - request body for operation -func (client BackupInstancesClient) TriggerRehydrate(ctx context.Context, resourceGroupName string, vaultName string, parameters AzureBackupRehydrationRequest, backupInstanceName string) (result BackupInstancesTriggerRehydrateFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.TriggerRehydrate") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - if err := validation.Validate([]validation.Validation{ - { - TargetValue: parameters, - Constraints: []validation.Constraint{ - {Target: "parameters.RecoveryPointID", Name: validation.Null, Rule: true, Chain: nil}, - {Target: "parameters.RehydrationRetentionDuration", Name: validation.Null, Rule: true, Chain: nil}, - }, - }, - }); err != nil { - return result, validation.NewError("dataprotection.BackupInstancesClient", "TriggerRehydrate", err.Error()) - } - - req, err := client.TriggerRehydratePreparer(ctx, resourceGroupName, vaultName, parameters, backupInstanceName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "TriggerRehydrate", nil, "Failure preparing request") - return - } - - result, err = client.TriggerRehydrateSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "TriggerRehydrate", nil, "Failure sending request") - return - } - - return -} - -// TriggerRehydratePreparer prepares the TriggerRehydrate request. -func (client BackupInstancesClient) TriggerRehydratePreparer(ctx context.Context, resourceGroupName string, vaultName string, parameters AzureBackupRehydrationRequest, backupInstanceName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/rehydrate", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// TriggerRehydrateSender sends the TriggerRehydrate request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) TriggerRehydrateSender(req *http.Request) (future BackupInstancesTriggerRehydrateFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// TriggerRehydrateResponder handles the response to the TriggerRehydrate request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) TriggerRehydrateResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} - -// TriggerRestore triggers restore for a BackupInstance -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// parameters - request body for operation -func (client BackupInstancesClient) TriggerRestore(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters BasicAzureBackupRestoreRequest) (result BackupInstancesTriggerRestoreFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.TriggerRestore") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.TriggerRestorePreparer(ctx, vaultName, resourceGroupName, backupInstanceName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "TriggerRestore", nil, "Failure preparing request") - return - } - - result, err = client.TriggerRestoreSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "TriggerRestore", nil, "Failure sending request") - return - } - - return -} - -// TriggerRestorePreparer prepares the TriggerRestore request. -func (client BackupInstancesClient) TriggerRestorePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters BasicAzureBackupRestoreRequest) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/restore", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// TriggerRestoreSender sends the TriggerRestore request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) TriggerRestoreSender(req *http.Request) (future BackupInstancesTriggerRestoreFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// TriggerRestoreResponder handles the response to the TriggerRestore request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) TriggerRestoreResponder(resp *http.Response) (result OperationJobExtendedInfo, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// ValidateForBackup validate whether adhoc backup will be successful or not -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// parameters - request body for operation -func (client BackupInstancesClient) ValidateForBackup(ctx context.Context, vaultName string, resourceGroupName string, parameters ValidateForBackupRequest) (result BackupInstancesValidateForBackupFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.ValidateForBackup") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - if err := validation.Validate([]validation.Validation{ - { - TargetValue: parameters, - Constraints: []validation.Constraint{{ - Target: "parameters.BackupInstance", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{ - { - Target: "parameters.BackupInstance.DataSourceInfo", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{{Target: "parameters.BackupInstance.DataSourceInfo.ResourceID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.BackupInstance.DataSourceSetInfo", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.BackupInstance.DataSourceSetInfo.ResourceID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.BackupInstance.PolicyInfo", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{{Target: "parameters.BackupInstance.PolicyInfo.PolicyID", Name: validation.Null, Rule: true, Chain: nil}}, - }, - { - Target: "parameters.BackupInstance.ProtectionStatus", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.BackupInstance.ProtectionStatus.ErrorDetails", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.BackupInstance.ProtectionStatus.ErrorDetails.InnerError", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.BackupInstance.ProtectionStatus.ErrorDetails.InnerError.EmbeddedInnerError", Name: validation.Null, Rule: false, Chain: nil}}, - }, - }, - }, - }, - }, - { - Target: "parameters.BackupInstance.ProtectionErrorDetails", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{ - { - Target: "parameters.BackupInstance.ProtectionErrorDetails.InnerError", Name: validation.Null, Rule: false, - Chain: []validation.Constraint{{Target: "parameters.BackupInstance.ProtectionErrorDetails.InnerError.EmbeddedInnerError", Name: validation.Null, Rule: false, Chain: nil}}, - }, - }, - }, - {Target: "parameters.BackupInstance.ObjectType", Name: validation.Null, Rule: true, Chain: nil}, - }, - }}, - }, - }); err != nil { - return result, validation.NewError("dataprotection.BackupInstancesClient", "ValidateForBackup", err.Error()) - } - - req, err := client.ValidateForBackupPreparer(ctx, vaultName, resourceGroupName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "ValidateForBackup", nil, "Failure preparing request") - return - } - - result, err = client.ValidateForBackupSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "ValidateForBackup", nil, "Failure sending request") - return - } - - return -} - -// ValidateForBackupPreparer prepares the ValidateForBackup request. -func (client BackupInstancesClient) ValidateForBackupPreparer(ctx context.Context, vaultName string, resourceGroupName string, parameters ValidateForBackupRequest) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/validateForBackup", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ValidateForBackupSender sends the ValidateForBackup request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) ValidateForBackupSender(req *http.Request) (future BackupInstancesValidateForBackupFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// ValidateForBackupResponder handles the response to the ValidateForBackup request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) ValidateForBackupResponder(resp *http.Response) (result OperationJobExtendedInfo, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// ValidateForRestore validates if Restore can be triggered for a DataSource -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// parameters - request body for operation -func (client BackupInstancesClient) ValidateForRestore(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters ValidateRestoreRequestObject) (result BackupInstancesValidateForRestoreFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstancesClient.ValidateForRestore") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.ValidateForRestorePreparer(ctx, vaultName, resourceGroupName, backupInstanceName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "ValidateForRestore", nil, "Failure preparing request") - return - } - - result, err = client.ValidateForRestoreSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesClient", "ValidateForRestore", nil, "Failure sending request") - return - } - - return -} - -// ValidateForRestorePreparer prepares the ValidateForRestore request. -func (client BackupInstancesClient) ValidateForRestorePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters ValidateRestoreRequestObject) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/validateRestore", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ValidateForRestoreSender sends the ValidateForRestore request. The method will close the -// http.Response Body if it receives an error. -func (client BackupInstancesClient) ValidateForRestoreSender(req *http.Request) (future BackupInstancesValidateForRestoreFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// ValidateForRestoreResponder handles the response to the ValidateForRestore request. The method always -// closes the http.Response Body. -func (client BackupInstancesClient) ValidateForRestoreResponder(resp *http.Response) (result OperationJobExtendedInfo, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/backuppolicies.go b/internal/services/dataprotection/legacysdk/dataprotection/backuppolicies.go deleted file mode 100644 index 4f730fb6f7ea..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/backuppolicies.go +++ /dev/null @@ -1,384 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// BackupPoliciesClient is the open API 2.0 Specs for Azure Data Protection service -type BackupPoliciesClient struct { - BaseClient -} - -// NewBackupPoliciesClient creates an instance of the BackupPoliciesClient client. -func NewBackupPoliciesClient(subscriptionID string) BackupPoliciesClient { - return NewBackupPoliciesClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewBackupPoliciesClientWithBaseURI creates an instance of the BackupPoliciesClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewBackupPoliciesClientWithBaseURI(baseURI string, subscriptionID string) BackupPoliciesClient { - return BackupPoliciesClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// CreateOrUpdate sends the create or update request. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupPolicyName - name of the policy -// parameters - request body for operation -func (client BackupPoliciesClient) CreateOrUpdate(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string, parameters BaseBackupPolicyResource) (result BaseBackupPolicyResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupPoliciesClient.CreateOrUpdate") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.CreateOrUpdatePreparer(ctx, vaultName, resourceGroupName, backupPolicyName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "CreateOrUpdate", nil, "Failure preparing request") - return - } - - resp, err := client.CreateOrUpdateSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "CreateOrUpdate", resp, "Failure sending request") - return - } - - result, err = client.CreateOrUpdateResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "CreateOrUpdate", resp, "Failure responding to request") - return - } - - return -} - -// CreateOrUpdatePreparer prepares the CreateOrUpdate request. -func (client BackupPoliciesClient) CreateOrUpdatePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string, parameters BaseBackupPolicyResource) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupPolicyName": autorest.Encode("path", backupPolicyName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPut(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupPolicies/{backupPolicyName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the -// http.Response Body if it receives an error. -func (client BackupPoliciesClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always -// closes the http.Response Body. -func (client BackupPoliciesClient) CreateOrUpdateResponder(resp *http.Response) (result BaseBackupPolicyResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// Delete sends the delete request. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupPoliciesClient) Delete(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string) (result autorest.Response, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupPoliciesClient.Delete") - defer func() { - sc := -1 - if result.Response != nil { - sc = result.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.DeletePreparer(ctx, vaultName, resourceGroupName, backupPolicyName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Delete", nil, "Failure preparing request") - return - } - - resp, err := client.DeleteSender(req) - if err != nil { - result.Response = resp - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Delete", resp, "Failure sending request") - return - } - - result, err = client.DeleteResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Delete", resp, "Failure responding to request") - return - } - - return -} - -// DeletePreparer prepares the Delete request. -func (client BackupPoliciesClient) DeletePreparer(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupPolicyName": autorest.Encode("path", backupPolicyName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsDelete(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupPolicies/{backupPolicyName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// DeleteSender sends the Delete request. The method will close the -// http.Response Body if it receives an error. -func (client BackupPoliciesClient) DeleteSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// DeleteResponder handles the response to the Delete request. The method always -// closes the http.Response Body. -func (client BackupPoliciesClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} - -// Get gets a backup policy belonging to a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupPoliciesClient) Get(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string) (result BaseBackupPolicyResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupPoliciesClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, vaultName, resourceGroupName, backupPolicyName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client BackupPoliciesClient) GetPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupPolicyName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupPolicyName": autorest.Encode("path", backupPolicyName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupPolicies/{backupPolicyName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client BackupPoliciesClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client BackupPoliciesClient) GetResponder(resp *http.Response) (result BaseBackupPolicyResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// List returns list of backup policies belonging to a backup vault -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupPoliciesClient) List(ctx context.Context, vaultName string, resourceGroupName string) (result BaseBackupPolicyResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupPoliciesClient.List") - defer func() { - sc := -1 - if result.bbprl.Response.Response != nil { - sc = result.bbprl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.listNextResults - req, err := client.ListPreparer(ctx, vaultName, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "List", nil, "Failure preparing request") - return - } - - resp, err := client.ListSender(req) - if err != nil { - result.bbprl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "List", resp, "Failure sending request") - return - } - - result.bbprl, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "List", resp, "Failure responding to request") - return - } - if result.bbprl.hasNextLink() && result.bbprl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// ListPreparer prepares the List request. -func (client BackupPoliciesClient) ListPreparer(ctx context.Context, vaultName string, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupPolicies", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ListSender sends the List request. The method will close the -// http.Response Body if it receives an error. -func (client BackupPoliciesClient) ListSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// ListResponder handles the response to the List request. The method always -// closes the http.Response Body. -func (client BackupPoliciesClient) ListResponder(resp *http.Response) (result BaseBackupPolicyResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// listNextResults retrieves the next set of results, if any. -func (client BackupPoliciesClient) listNextResults(ctx context.Context, lastResults BaseBackupPolicyResourceList) (result BaseBackupPolicyResourceList, err error) { - req, err := lastResults.baseBackupPolicyResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "listNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.ListSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "listNextResults", resp, "Failure sending next results request") - } - result, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupPoliciesClient", "listNextResults", resp, "Failure responding to next results request") - } - return -} - -// ListComplete enumerates all values, automatically crossing page boundaries as required. -func (client BackupPoliciesClient) ListComplete(ctx context.Context, vaultName string, resourceGroupName string) (result BaseBackupPolicyResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupPoliciesClient.List") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.List(ctx, vaultName, resourceGroupName) - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/backupvaultoperationresults.go b/internal/services/dataprotection/legacysdk/dataprotection/backupvaultoperationresults.go deleted file mode 100644 index 7433a7399926..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/backupvaultoperationresults.go +++ /dev/null @@ -1,110 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// BackupVaultOperationResultsClient is the open API 2.0 Specs for Azure Data Protection service -type BackupVaultOperationResultsClient struct { - BaseClient -} - -// NewBackupVaultOperationResultsClient creates an instance of the BackupVaultOperationResultsClient client. -func NewBackupVaultOperationResultsClient(subscriptionID string) BackupVaultOperationResultsClient { - return NewBackupVaultOperationResultsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewBackupVaultOperationResultsClientWithBaseURI creates an instance of the BackupVaultOperationResultsClient client -// using a custom endpoint. Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign -// clouds, Azure stack). -func NewBackupVaultOperationResultsClientWithBaseURI(baseURI string, subscriptionID string) BackupVaultOperationResultsClient { - return BackupVaultOperationResultsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get sends the get request. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupVaultOperationResultsClient) Get(ctx context.Context, vaultName string, resourceGroupName string, operationID string) (result BackupVaultResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultOperationResultsClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, vaultName, resourceGroupName, operationID) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultOperationResultsClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultOperationResultsClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultOperationResultsClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client BackupVaultOperationResultsClient) GetPreparer(ctx context.Context, vaultName string, resourceGroupName string, operationID string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "operationId": autorest.Encode("path", operationID), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/operationResults/{operationId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultOperationResultsClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client BackupVaultOperationResultsClient) GetResponder(resp *http.Response) (result BackupVaultResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/backupvaults.go b/internal/services/dataprotection/legacysdk/dataprotection/backupvaults.go deleted file mode 100644 index 38a2ad1943e2..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/backupvaults.go +++ /dev/null @@ -1,666 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/validation" - "github.com/Azure/go-autorest/tracing" -) - -// BackupVaultsClient is the open API 2.0 Specs for Azure Data Protection service -type BackupVaultsClient struct { - BaseClient -} - -// NewBackupVaultsClient creates an instance of the BackupVaultsClient client. -func NewBackupVaultsClient(subscriptionID string) BackupVaultsClient { - return NewBackupVaultsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewBackupVaultsClientWithBaseURI creates an instance of the BackupVaultsClient client using a custom endpoint. Use -// this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewBackupVaultsClientWithBaseURI(baseURI string, subscriptionID string) BackupVaultsClient { - return BackupVaultsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// CheckNameAvailability sends the check name availability request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// location - the location in which uniqueness will be verified. -// parameters - check name availability request -func (client BackupVaultsClient) CheckNameAvailability(ctx context.Context, resourceGroupName string, location string, parameters CheckNameAvailabilityRequest) (result CheckNameAvailabilityResult, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.CheckNameAvailability") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.CheckNameAvailabilityPreparer(ctx, resourceGroupName, location, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "CheckNameAvailability", nil, "Failure preparing request") - return - } - - resp, err := client.CheckNameAvailabilitySender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "CheckNameAvailability", resp, "Failure sending request") - return - } - - result, err = client.CheckNameAvailabilityResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "CheckNameAvailability", resp, "Failure responding to request") - return - } - - return -} - -// CheckNameAvailabilityPreparer prepares the CheckNameAvailability request. -func (client BackupVaultsClient) CheckNameAvailabilityPreparer(ctx context.Context, resourceGroupName string, location string, parameters CheckNameAvailabilityRequest) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "location": autorest.Encode("path", location), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/locations/{location}/checkNameAvailability", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// CheckNameAvailabilitySender sends the CheckNameAvailability request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) CheckNameAvailabilitySender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// CheckNameAvailabilityResponder handles the response to the CheckNameAvailability request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) CheckNameAvailabilityResponder(resp *http.Response) (result CheckNameAvailabilityResult, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// CreateOrUpdate creates or updates a BackupVault resource belonging to a resource group. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// parameters - request body for operation -func (client BackupVaultsClient) CreateOrUpdate(ctx context.Context, vaultName string, resourceGroupName string, parameters BackupVaultResource) (result BackupVaultsCreateOrUpdateFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.CreateOrUpdate") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - if err := validation.Validate([]validation.Validation{ - { - TargetValue: parameters, - Constraints: []validation.Constraint{{ - Target: "parameters.Properties", Name: validation.Null, Rule: true, - Chain: []validation.Constraint{{Target: "parameters.Properties.StorageSettings", Name: validation.Null, Rule: true, Chain: nil}}, - }}, - }, - }); err != nil { - return result, validation.NewError("dataprotection.BackupVaultsClient", "CreateOrUpdate", err.Error()) - } - - req, err := client.CreateOrUpdatePreparer(ctx, vaultName, resourceGroupName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "CreateOrUpdate", nil, "Failure preparing request") - return - } - - result, err = client.CreateOrUpdateSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "CreateOrUpdate", nil, "Failure sending request") - return - } - - return -} - -// CreateOrUpdatePreparer prepares the CreateOrUpdate request. -func (client BackupVaultsClient) CreateOrUpdatePreparer(ctx context.Context, vaultName string, resourceGroupName string, parameters BackupVaultResource) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPut(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) CreateOrUpdateSender(req *http.Request) (future BackupVaultsCreateOrUpdateFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) CreateOrUpdateResponder(resp *http.Response) (result BackupVaultResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// Delete deletes a BackupVault resource from the resource group. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupVaultsClient) Delete(ctx context.Context, vaultName string, resourceGroupName string) (result autorest.Response, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.Delete") - defer func() { - sc := -1 - if result.Response != nil { - sc = result.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.DeletePreparer(ctx, vaultName, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Delete", nil, "Failure preparing request") - return - } - - resp, err := client.DeleteSender(req) - if err != nil { - result.Response = resp - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Delete", resp, "Failure sending request") - return - } - - result, err = client.DeleteResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Delete", resp, "Failure responding to request") - return - } - - return -} - -// DeletePreparer prepares the Delete request. -func (client BackupVaultsClient) DeletePreparer(ctx context.Context, vaultName string, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsDelete(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// DeleteSender sends the Delete request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) DeleteSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// DeleteResponder handles the response to the Delete request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} - -// Get returns a resource belonging to a resource group. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupVaultsClient) Get(ctx context.Context, vaultName string, resourceGroupName string) (result BackupVaultResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, vaultName, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client BackupVaultsClient) GetPreparer(ctx context.Context, vaultName string, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) GetResponder(resp *http.Response) (result BackupVaultResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetInResourceGroup returns resource collection belonging to a resource group. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client BackupVaultsClient) GetInResourceGroup(ctx context.Context, resourceGroupName string) (result BackupVaultResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.GetInResourceGroup") - defer func() { - sc := -1 - if result.bvrl.Response.Response != nil { - sc = result.bvrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getInResourceGroupNextResults - req, err := client.GetInResourceGroupPreparer(ctx, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInResourceGroup", nil, "Failure preparing request") - return - } - - resp, err := client.GetInResourceGroupSender(req) - if err != nil { - result.bvrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInResourceGroup", resp, "Failure sending request") - return - } - - result.bvrl, err = client.GetInResourceGroupResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInResourceGroup", resp, "Failure responding to request") - return - } - if result.bvrl.hasNextLink() && result.bvrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetInResourceGroupPreparer prepares the GetInResourceGroup request. -func (client BackupVaultsClient) GetInResourceGroupPreparer(ctx context.Context, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetInResourceGroupSender sends the GetInResourceGroup request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) GetInResourceGroupSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetInResourceGroupResponder handles the response to the GetInResourceGroup request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) GetInResourceGroupResponder(resp *http.Response) (result BackupVaultResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getInResourceGroupNextResults retrieves the next set of results, if any. -func (client BackupVaultsClient) getInResourceGroupNextResults(ctx context.Context, lastResults BackupVaultResourceList) (result BackupVaultResourceList, err error) { - req, err := lastResults.backupVaultResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInResourceGroupNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetInResourceGroupSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInResourceGroupNextResults", resp, "Failure sending next results request") - } - result, err = client.GetInResourceGroupResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInResourceGroupNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetInResourceGroupComplete enumerates all values, automatically crossing page boundaries as required. -func (client BackupVaultsClient) GetInResourceGroupComplete(ctx context.Context, resourceGroupName string) (result BackupVaultResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.GetInResourceGroup") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetInResourceGroup(ctx, resourceGroupName) - return -} - -// GetInSubscription returns resource collection belonging to a subscription. -func (client BackupVaultsClient) GetInSubscription(ctx context.Context) (result BackupVaultResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.GetInSubscription") - defer func() { - sc := -1 - if result.bvrl.Response.Response != nil { - sc = result.bvrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getInSubscriptionNextResults - req, err := client.GetInSubscriptionPreparer(ctx) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInSubscription", nil, "Failure preparing request") - return - } - - resp, err := client.GetInSubscriptionSender(req) - if err != nil { - result.bvrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInSubscription", resp, "Failure sending request") - return - } - - result.bvrl, err = client.GetInSubscriptionResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "GetInSubscription", resp, "Failure responding to request") - return - } - if result.bvrl.hasNextLink() && result.bvrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetInSubscriptionPreparer prepares the GetInSubscription request. -func (client BackupVaultsClient) GetInSubscriptionPreparer(ctx context.Context) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.DataProtection/backupVaults", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetInSubscriptionSender sends the GetInSubscription request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) GetInSubscriptionSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetInSubscriptionResponder handles the response to the GetInSubscription request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) GetInSubscriptionResponder(resp *http.Response) (result BackupVaultResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getInSubscriptionNextResults retrieves the next set of results, if any. -func (client BackupVaultsClient) getInSubscriptionNextResults(ctx context.Context, lastResults BackupVaultResourceList) (result BackupVaultResourceList, err error) { - req, err := lastResults.backupVaultResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInSubscriptionNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetInSubscriptionSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInSubscriptionNextResults", resp, "Failure sending next results request") - } - result, err = client.GetInSubscriptionResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "getInSubscriptionNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetInSubscriptionComplete enumerates all values, automatically crossing page boundaries as required. -func (client BackupVaultsClient) GetInSubscriptionComplete(ctx context.Context) (result BackupVaultResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.GetInSubscription") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetInSubscription(ctx) - return -} - -// Update updates a BackupVault resource belonging to a resource group. For example, updating tags for a resource. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// parameters - request body for operation -func (client BackupVaultsClient) Update(ctx context.Context, vaultName string, resourceGroupName string, parameters PatchResourceRequestInput) (result BackupVaultsUpdateFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultsClient.Update") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.UpdatePreparer(ctx, vaultName, resourceGroupName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Update", nil, "Failure preparing request") - return - } - - result, err = client.UpdateSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsClient", "Update", nil, "Failure sending request") - return - } - - return -} - -// UpdatePreparer prepares the Update request. -func (client BackupVaultsClient) UpdatePreparer(ctx context.Context, vaultName string, resourceGroupName string, parameters PatchResourceRequestInput) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPatch(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// UpdateSender sends the Update request. The method will close the -// http.Response Body if it receives an error. -func (client BackupVaultsClient) UpdateSender(req *http.Request) (future BackupVaultsUpdateFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// UpdateResponder handles the response to the Update request. The method always -// closes the http.Response Body. -func (client BackupVaultsClient) UpdateResponder(resp *http.Response) (result BackupVaultResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/client.go b/internal/services/dataprotection/legacysdk/dataprotection/client.go deleted file mode 100644 index 64d725f2bae8..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/client.go +++ /dev/null @@ -1,41 +0,0 @@ -// Package dataprotection implements the Azure ARM Dataprotection service API version 2021-07-01. -// -// Open API 2.0 Specs for Azure Data Protection service -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "github.com/Azure/go-autorest/autorest" -) - -const ( - // DefaultBaseURI is the default URI used for the service Dataprotection - DefaultBaseURI = "https://management.azure.com" -) - -// BaseClient is the base client for Dataprotection. -type BaseClient struct { - autorest.Client - BaseURI string - SubscriptionID string -} - -// New creates an instance of the BaseClient client. -func New(subscriptionID string) BaseClient { - return NewWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewWithBaseURI creates an instance of the BaseClient client using a custom endpoint. Use this when interacting with -// an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { - return BaseClient{ - Client: autorest.NewClientWithUserAgent(UserAgent()), - BaseURI: baseURI, - SubscriptionID: subscriptionID, - } -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/dataprotection.go b/internal/services/dataprotection/legacysdk/dataprotection/dataprotection.go deleted file mode 100644 index 57e523220de8..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/dataprotection.go +++ /dev/null @@ -1,108 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// Client is the open API 2.0 Specs for Azure Data Protection service -type Client struct { - BaseClient -} - -// NewClient creates an instance of the Client client. -func NewClient(subscriptionID string) Client { - return NewClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewClientWithBaseURI creates an instance of the Client client using a custom endpoint. Use this when interacting -// with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewClientWithBaseURI(baseURI string, subscriptionID string) Client { - return Client{NewWithBaseURI(baseURI, subscriptionID)} -} - -// CheckFeatureSupport sends the check feature support request. -// Parameters: -// parameters - feature support request object -func (client Client) CheckFeatureSupport(ctx context.Context, location string, parameters BasicFeatureValidationRequestBase) (result FeatureValidationResponseBaseModel, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/Client.CheckFeatureSupport") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.CheckFeatureSupportPreparer(ctx, location, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.Client", "CheckFeatureSupport", nil, "Failure preparing request") - return - } - - resp, err := client.CheckFeatureSupportSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.Client", "CheckFeatureSupport", resp, "Failure sending request") - return - } - - result, err = client.CheckFeatureSupportResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.Client", "CheckFeatureSupport", resp, "Failure responding to request") - return - } - - return -} - -// CheckFeatureSupportPreparer prepares the CheckFeatureSupport request. -func (client Client) CheckFeatureSupportPreparer(ctx context.Context, location string, parameters BasicFeatureValidationRequestBase) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "location": autorest.Encode("path", location), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.DataProtection/locations/{location}/checkFeatureSupport", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// CheckFeatureSupportSender sends the CheckFeatureSupport request. The method will close the -// http.Response Body if it receives an error. -func (client Client) CheckFeatureSupportSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// CheckFeatureSupportResponder handles the response to the CheckFeatureSupport request. The method always -// closes the http.Response Body. -func (client Client) CheckFeatureSupportResponder(resp *http.Response) (result FeatureValidationResponseBaseModel, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/enums.go b/internal/services/dataprotection/legacysdk/dataprotection/enums.go deleted file mode 100644 index 9da554a4ddd0..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/enums.go +++ /dev/null @@ -1,690 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -// AbsoluteMarker enumerates the values for absolute marker. -type AbsoluteMarker string - -const ( - // AbsoluteMarkerAllBackup ... - AbsoluteMarkerAllBackup AbsoluteMarker = "AllBackup" - // AbsoluteMarkerFirstOfDay ... - AbsoluteMarkerFirstOfDay AbsoluteMarker = "FirstOfDay" - // AbsoluteMarkerFirstOfMonth ... - AbsoluteMarkerFirstOfMonth AbsoluteMarker = "FirstOfMonth" - // AbsoluteMarkerFirstOfWeek ... - AbsoluteMarkerFirstOfWeek AbsoluteMarker = "FirstOfWeek" - // AbsoluteMarkerFirstOfYear ... - AbsoluteMarkerFirstOfYear AbsoluteMarker = "FirstOfYear" -) - -// PossibleAbsoluteMarkerValues returns an array of possible values for the AbsoluteMarker const type. -func PossibleAbsoluteMarkerValues() []AbsoluteMarker { - return []AbsoluteMarker{AbsoluteMarkerAllBackup, AbsoluteMarkerFirstOfDay, AbsoluteMarkerFirstOfMonth, AbsoluteMarkerFirstOfWeek, AbsoluteMarkerFirstOfYear} -} - -// CreatedByType enumerates the values for created by type. -type CreatedByType string - -const ( - // CreatedByTypeApplication ... - CreatedByTypeApplication CreatedByType = "Application" - // CreatedByTypeKey ... - CreatedByTypeKey CreatedByType = "Key" - // CreatedByTypeManagedIdentity ... - CreatedByTypeManagedIdentity CreatedByType = "ManagedIdentity" - // CreatedByTypeUser ... - CreatedByTypeUser CreatedByType = "User" -) - -// PossibleCreatedByTypeValues returns an array of possible values for the CreatedByType const type. -func PossibleCreatedByTypeValues() []CreatedByType { - return []CreatedByType{CreatedByTypeApplication, CreatedByTypeKey, CreatedByTypeManagedIdentity, CreatedByTypeUser} -} - -// CurrentProtectionState enumerates the values for current protection state. -type CurrentProtectionState string - -const ( - // CurrentProtectionStateBackupSchedulesSuspended ... - CurrentProtectionStateBackupSchedulesSuspended CurrentProtectionState = "BackupSchedulesSuspended" - // CurrentProtectionStateConfiguringProtection ... - CurrentProtectionStateConfiguringProtection CurrentProtectionState = "ConfiguringProtection" - // CurrentProtectionStateConfiguringProtectionFailed ... - CurrentProtectionStateConfiguringProtectionFailed CurrentProtectionState = "ConfiguringProtectionFailed" - // CurrentProtectionStateInvalid ... - CurrentProtectionStateInvalid CurrentProtectionState = "Invalid" - // CurrentProtectionStateNotProtected ... - CurrentProtectionStateNotProtected CurrentProtectionState = "NotProtected" - // CurrentProtectionStateProtectionConfigured ... - CurrentProtectionStateProtectionConfigured CurrentProtectionState = "ProtectionConfigured" - // CurrentProtectionStateProtectionError ... - CurrentProtectionStateProtectionError CurrentProtectionState = "ProtectionError" - // CurrentProtectionStateProtectionStopped ... - CurrentProtectionStateProtectionStopped CurrentProtectionState = "ProtectionStopped" - // CurrentProtectionStateRetentionSchedulesSuspended ... - CurrentProtectionStateRetentionSchedulesSuspended CurrentProtectionState = "RetentionSchedulesSuspended" - // CurrentProtectionStateSoftDeleted ... - CurrentProtectionStateSoftDeleted CurrentProtectionState = "SoftDeleted" - // CurrentProtectionStateSoftDeleting ... - CurrentProtectionStateSoftDeleting CurrentProtectionState = "SoftDeleting" - // CurrentProtectionStateUpdatingProtection ... - CurrentProtectionStateUpdatingProtection CurrentProtectionState = "UpdatingProtection" -) - -// PossibleCurrentProtectionStateValues returns an array of possible values for the CurrentProtectionState const type. -func PossibleCurrentProtectionStateValues() []CurrentProtectionState { - return []CurrentProtectionState{CurrentProtectionStateBackupSchedulesSuspended, CurrentProtectionStateConfiguringProtection, CurrentProtectionStateConfiguringProtectionFailed, CurrentProtectionStateInvalid, CurrentProtectionStateNotProtected, CurrentProtectionStateProtectionConfigured, CurrentProtectionStateProtectionError, CurrentProtectionStateProtectionStopped, CurrentProtectionStateRetentionSchedulesSuspended, CurrentProtectionStateSoftDeleted, CurrentProtectionStateSoftDeleting, CurrentProtectionStateUpdatingProtection} -} - -// DataStoreTypes enumerates the values for data store types. -type DataStoreTypes string - -const ( - // DataStoreTypesArchiveStore ... - DataStoreTypesArchiveStore DataStoreTypes = "ArchiveStore" - // DataStoreTypesOperationalStore ... - DataStoreTypesOperationalStore DataStoreTypes = "OperationalStore" - // DataStoreTypesVaultStore ... - DataStoreTypesVaultStore DataStoreTypes = "VaultStore" -) - -// PossibleDataStoreTypesValues returns an array of possible values for the DataStoreTypes const type. -func PossibleDataStoreTypesValues() []DataStoreTypes { - return []DataStoreTypes{DataStoreTypesArchiveStore, DataStoreTypesOperationalStore, DataStoreTypesVaultStore} -} - -// DayOfWeek enumerates the values for day of week. -type DayOfWeek string - -const ( - // DayOfWeekFriday ... - DayOfWeekFriday DayOfWeek = "Friday" - // DayOfWeekMonday ... - DayOfWeekMonday DayOfWeek = "Monday" - // DayOfWeekSaturday ... - DayOfWeekSaturday DayOfWeek = "Saturday" - // DayOfWeekSunday ... - DayOfWeekSunday DayOfWeek = "Sunday" - // DayOfWeekThursday ... - DayOfWeekThursday DayOfWeek = "Thursday" - // DayOfWeekTuesday ... - DayOfWeekTuesday DayOfWeek = "Tuesday" - // DayOfWeekWednesday ... - DayOfWeekWednesday DayOfWeek = "Wednesday" -) - -// PossibleDayOfWeekValues returns an array of possible values for the DayOfWeek const type. -func PossibleDayOfWeekValues() []DayOfWeek { - return []DayOfWeek{DayOfWeekFriday, DayOfWeekMonday, DayOfWeekSaturday, DayOfWeekSunday, DayOfWeekThursday, DayOfWeekTuesday, DayOfWeekWednesday} -} - -// FeatureSupportStatus enumerates the values for feature support status. -type FeatureSupportStatus string - -const ( - // FeatureSupportStatusAlphaPreview ... - FeatureSupportStatusAlphaPreview FeatureSupportStatus = "AlphaPreview" - // FeatureSupportStatusGenerallyAvailable ... - FeatureSupportStatusGenerallyAvailable FeatureSupportStatus = "GenerallyAvailable" - // FeatureSupportStatusInvalid ... - FeatureSupportStatusInvalid FeatureSupportStatus = "Invalid" - // FeatureSupportStatusNotSupported ... - FeatureSupportStatusNotSupported FeatureSupportStatus = "NotSupported" - // FeatureSupportStatusPrivatePreview ... - FeatureSupportStatusPrivatePreview FeatureSupportStatus = "PrivatePreview" - // FeatureSupportStatusPublicPreview ... - FeatureSupportStatusPublicPreview FeatureSupportStatus = "PublicPreview" -) - -// PossibleFeatureSupportStatusValues returns an array of possible values for the FeatureSupportStatus const type. -func PossibleFeatureSupportStatusValues() []FeatureSupportStatus { - return []FeatureSupportStatus{FeatureSupportStatusAlphaPreview, FeatureSupportStatusGenerallyAvailable, FeatureSupportStatusInvalid, FeatureSupportStatusNotSupported, FeatureSupportStatusPrivatePreview, FeatureSupportStatusPublicPreview} -} - -// FeatureType enumerates the values for feature type. -type FeatureType string - -const ( - // FeatureTypeDataSourceType ... - FeatureTypeDataSourceType FeatureType = "DataSourceType" - // FeatureTypeInvalid ... - FeatureTypeInvalid FeatureType = "Invalid" -) - -// PossibleFeatureTypeValues returns an array of possible values for the FeatureType const type. -func PossibleFeatureTypeValues() []FeatureType { - return []FeatureType{FeatureTypeDataSourceType, FeatureTypeInvalid} -} - -// Month enumerates the values for month. -type Month string - -const ( - // MonthApril ... - MonthApril Month = "April" - // MonthAugust ... - MonthAugust Month = "August" - // MonthDecember ... - MonthDecember Month = "December" - // MonthFebruary ... - MonthFebruary Month = "February" - // MonthJanuary ... - MonthJanuary Month = "January" - // MonthJuly ... - MonthJuly Month = "July" - // MonthJune ... - MonthJune Month = "June" - // MonthMarch ... - MonthMarch Month = "March" - // MonthMay ... - MonthMay Month = "May" - // MonthNovember ... - MonthNovember Month = "November" - // MonthOctober ... - MonthOctober Month = "October" - // MonthSeptember ... - MonthSeptember Month = "September" -) - -// PossibleMonthValues returns an array of possible values for the Month const type. -func PossibleMonthValues() []Month { - return []Month{MonthApril, MonthAugust, MonthDecember, MonthFebruary, MonthJanuary, MonthJuly, MonthJune, MonthMarch, MonthMay, MonthNovember, MonthOctober, MonthSeptember} -} - -// ObjectType enumerates the values for object type. -type ObjectType string - -const ( - // ObjectTypeAuthCredentials ... - ObjectTypeAuthCredentials ObjectType = "AuthCredentials" - // ObjectTypeSecretStoreBasedAuthCredentials ... - ObjectTypeSecretStoreBasedAuthCredentials ObjectType = "SecretStoreBasedAuthCredentials" -) - -// PossibleObjectTypeValues returns an array of possible values for the ObjectType const type. -func PossibleObjectTypeValues() []ObjectType { - return []ObjectType{ObjectTypeAuthCredentials, ObjectTypeSecretStoreBasedAuthCredentials} -} - -// ObjectTypeBasicAzureBackupRecoveryPoint enumerates the values for object type basic azure backup recovery -// point. -type ObjectTypeBasicAzureBackupRecoveryPoint string - -const ( - // ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint ... - ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint ObjectTypeBasicAzureBackupRecoveryPoint = "AzureBackupDiscreteRecoveryPoint" - // ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint ... - ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint ObjectTypeBasicAzureBackupRecoveryPoint = "AzureBackupRecoveryPoint" -) - -// PossibleObjectTypeBasicAzureBackupRecoveryPointValues returns an array of possible values for the ObjectTypeBasicAzureBackupRecoveryPoint const type. -func PossibleObjectTypeBasicAzureBackupRecoveryPointValues() []ObjectTypeBasicAzureBackupRecoveryPoint { - return []ObjectTypeBasicAzureBackupRecoveryPoint{ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint, ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint} -} - -// ObjectTypeBasicAzureBackupRestoreRequest enumerates the values for object type basic azure backup restore -// request. -type ObjectTypeBasicAzureBackupRestoreRequest string - -const ( - // ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest ... - ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest ObjectTypeBasicAzureBackupRestoreRequest = "AzureBackupRecoveryPointBasedRestoreRequest" - // ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest ... - ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest ObjectTypeBasicAzureBackupRestoreRequest = "AzureBackupRecoveryTimeBasedRestoreRequest" - // ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest ... - ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest ObjectTypeBasicAzureBackupRestoreRequest = "AzureBackupRestoreRequest" - // ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest ... - ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest ObjectTypeBasicAzureBackupRestoreRequest = "AzureBackupRestoreWithRehydrationRequest" -) - -// PossibleObjectTypeBasicAzureBackupRestoreRequestValues returns an array of possible values for the ObjectTypeBasicAzureBackupRestoreRequest const type. -func PossibleObjectTypeBasicAzureBackupRestoreRequestValues() []ObjectTypeBasicAzureBackupRestoreRequest { - return []ObjectTypeBasicAzureBackupRestoreRequest{ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest, ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest, ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest, ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest} -} - -// ObjectTypeBasicBackupCriteria enumerates the values for object type basic backup criteria. -type ObjectTypeBasicBackupCriteria string - -const ( - // ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria ... - ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria ObjectTypeBasicBackupCriteria = "BackupCriteria" - // ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria ... - ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria ObjectTypeBasicBackupCriteria = "ScheduleBasedBackupCriteria" -) - -// PossibleObjectTypeBasicBackupCriteriaValues returns an array of possible values for the ObjectTypeBasicBackupCriteria const type. -func PossibleObjectTypeBasicBackupCriteriaValues() []ObjectTypeBasicBackupCriteria { - return []ObjectTypeBasicBackupCriteria{ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria, ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria} -} - -// ObjectTypeBasicBackupParameters enumerates the values for object type basic backup parameters. -type ObjectTypeBasicBackupParameters string - -const ( - // ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams ... - ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams ObjectTypeBasicBackupParameters = "AzureBackupParams" - // ObjectTypeBasicBackupParametersObjectTypeBackupParameters ... - ObjectTypeBasicBackupParametersObjectTypeBackupParameters ObjectTypeBasicBackupParameters = "BackupParameters" -) - -// PossibleObjectTypeBasicBackupParametersValues returns an array of possible values for the ObjectTypeBasicBackupParameters const type. -func PossibleObjectTypeBasicBackupParametersValues() []ObjectTypeBasicBackupParameters { - return []ObjectTypeBasicBackupParameters{ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams, ObjectTypeBasicBackupParametersObjectTypeBackupParameters} -} - -// ObjectTypeBasicBaseBackupPolicy enumerates the values for object type basic base backup policy. -type ObjectTypeBasicBaseBackupPolicy string - -const ( - // ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy ... - ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy ObjectTypeBasicBaseBackupPolicy = "BackupPolicy" - // ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy ... - ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy ObjectTypeBasicBaseBackupPolicy = "BaseBackupPolicy" -) - -// PossibleObjectTypeBasicBaseBackupPolicyValues returns an array of possible values for the ObjectTypeBasicBaseBackupPolicy const type. -func PossibleObjectTypeBasicBaseBackupPolicyValues() []ObjectTypeBasicBaseBackupPolicy { - return []ObjectTypeBasicBaseBackupPolicy{ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy, ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy} -} - -// ObjectTypeBasicBasePolicyRule enumerates the values for object type basic base policy rule. -type ObjectTypeBasicBasePolicyRule string - -const ( - // ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule ... - ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule ObjectTypeBasicBasePolicyRule = "AzureBackupRule" - // ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule ... - ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule ObjectTypeBasicBasePolicyRule = "AzureRetentionRule" - // ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule ... - ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule ObjectTypeBasicBasePolicyRule = "BasePolicyRule" -) - -// PossibleObjectTypeBasicBasePolicyRuleValues returns an array of possible values for the ObjectTypeBasicBasePolicyRule const type. -func PossibleObjectTypeBasicBasePolicyRuleValues() []ObjectTypeBasicBasePolicyRule { - return []ObjectTypeBasicBasePolicyRule{ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule, ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule, ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule} -} - -// ObjectTypeBasicCopyOption enumerates the values for object type basic copy option. -type ObjectTypeBasicCopyOption string - -const ( - // ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption ... - ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption ObjectTypeBasicCopyOption = "CopyOnExpiryOption" - // ObjectTypeBasicCopyOptionObjectTypeCopyOption ... - ObjectTypeBasicCopyOptionObjectTypeCopyOption ObjectTypeBasicCopyOption = "CopyOption" - // ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption ... - ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption ObjectTypeBasicCopyOption = "CustomCopyOption" - // ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption ... - ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption ObjectTypeBasicCopyOption = "ImmediateCopyOption" -) - -// PossibleObjectTypeBasicCopyOptionValues returns an array of possible values for the ObjectTypeBasicCopyOption const type. -func PossibleObjectTypeBasicCopyOptionValues() []ObjectTypeBasicCopyOption { - return []ObjectTypeBasicCopyOption{ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption, ObjectTypeBasicCopyOptionObjectTypeCopyOption, ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption, ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption} -} - -// ObjectTypeBasicDataStoreParameters enumerates the values for object type basic data store parameters. -type ObjectTypeBasicDataStoreParameters string - -const ( - // ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters ... - ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters ObjectTypeBasicDataStoreParameters = "AzureOperationalStoreParameters" - // ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters ... - ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters ObjectTypeBasicDataStoreParameters = "DataStoreParameters" -) - -// PossibleObjectTypeBasicDataStoreParametersValues returns an array of possible values for the ObjectTypeBasicDataStoreParameters const type. -func PossibleObjectTypeBasicDataStoreParametersValues() []ObjectTypeBasicDataStoreParameters { - return []ObjectTypeBasicDataStoreParameters{ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters, ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters} -} - -// ObjectTypeBasicDeleteOption enumerates the values for object type basic delete option. -type ObjectTypeBasicDeleteOption string - -const ( - // ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption ... - ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption ObjectTypeBasicDeleteOption = "AbsoluteDeleteOption" - // ObjectTypeBasicDeleteOptionObjectTypeDeleteOption ... - ObjectTypeBasicDeleteOptionObjectTypeDeleteOption ObjectTypeBasicDeleteOption = "DeleteOption" -) - -// PossibleObjectTypeBasicDeleteOptionValues returns an array of possible values for the ObjectTypeBasicDeleteOption const type. -func PossibleObjectTypeBasicDeleteOptionValues() []ObjectTypeBasicDeleteOption { - return []ObjectTypeBasicDeleteOption{ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption, ObjectTypeBasicDeleteOptionObjectTypeDeleteOption} -} - -// ObjectTypeBasicFeatureValidationRequestBase enumerates the values for object type basic feature validation -// request base. -type ObjectTypeBasicFeatureValidationRequestBase string - -const ( - // ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest ... - ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest ObjectTypeBasicFeatureValidationRequestBase = "FeatureValidationRequest" - // ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase ... - ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase ObjectTypeBasicFeatureValidationRequestBase = "FeatureValidationRequestBase" -) - -// PossibleObjectTypeBasicFeatureValidationRequestBaseValues returns an array of possible values for the ObjectTypeBasicFeatureValidationRequestBase const type. -func PossibleObjectTypeBasicFeatureValidationRequestBaseValues() []ObjectTypeBasicFeatureValidationRequestBase { - return []ObjectTypeBasicFeatureValidationRequestBase{ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest, ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase} -} - -// ObjectTypeBasicFeatureValidationResponseBase enumerates the values for object type basic feature validation -// response base. -type ObjectTypeBasicFeatureValidationResponseBase string - -const ( - // ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse ... - ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse ObjectTypeBasicFeatureValidationResponseBase = "FeatureValidationResponse" - // ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase ... - ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase ObjectTypeBasicFeatureValidationResponseBase = "FeatureValidationResponseBase" -) - -// PossibleObjectTypeBasicFeatureValidationResponseBaseValues returns an array of possible values for the ObjectTypeBasicFeatureValidationResponseBase const type. -func PossibleObjectTypeBasicFeatureValidationResponseBaseValues() []ObjectTypeBasicFeatureValidationResponseBase { - return []ObjectTypeBasicFeatureValidationResponseBase{ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse, ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase} -} - -// ObjectTypeBasicItemLevelRestoreCriteria enumerates the values for object type basic item level restore -// criteria. -type ObjectTypeBasicItemLevelRestoreCriteria string - -const ( - // ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria ... - ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria ObjectTypeBasicItemLevelRestoreCriteria = "ItemLevelRestoreCriteria" - // ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria ... - ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria ObjectTypeBasicItemLevelRestoreCriteria = "RangeBasedItemLevelRestoreCriteria" -) - -// PossibleObjectTypeBasicItemLevelRestoreCriteriaValues returns an array of possible values for the ObjectTypeBasicItemLevelRestoreCriteria const type. -func PossibleObjectTypeBasicItemLevelRestoreCriteriaValues() []ObjectTypeBasicItemLevelRestoreCriteria { - return []ObjectTypeBasicItemLevelRestoreCriteria{ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria, ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria} -} - -// ObjectTypeBasicOperationExtendedInfo enumerates the values for object type basic operation extended info. -type ObjectTypeBasicOperationExtendedInfo string - -const ( - // ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo ... - ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo ObjectTypeBasicOperationExtendedInfo = "OperationExtendedInfo" - // ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo ... - ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo ObjectTypeBasicOperationExtendedInfo = "OperationJobExtendedInfo" -) - -// PossibleObjectTypeBasicOperationExtendedInfoValues returns an array of possible values for the ObjectTypeBasicOperationExtendedInfo const type. -func PossibleObjectTypeBasicOperationExtendedInfoValues() []ObjectTypeBasicOperationExtendedInfo { - return []ObjectTypeBasicOperationExtendedInfo{ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo, ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo} -} - -// ObjectTypeBasicRestoreTargetInfoBase enumerates the values for object type basic restore target info base. -type ObjectTypeBasicRestoreTargetInfoBase string - -const ( - // ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo ... - ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo ObjectTypeBasicRestoreTargetInfoBase = "ItemLevelRestoreTargetInfo" - // ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo ... - ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo ObjectTypeBasicRestoreTargetInfoBase = "RestoreFilesTargetInfo" - // ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo ... - ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo ObjectTypeBasicRestoreTargetInfoBase = "RestoreTargetInfo" - // ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase ... - ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase ObjectTypeBasicRestoreTargetInfoBase = "RestoreTargetInfoBase" -) - -// PossibleObjectTypeBasicRestoreTargetInfoBaseValues returns an array of possible values for the ObjectTypeBasicRestoreTargetInfoBase const type. -func PossibleObjectTypeBasicRestoreTargetInfoBaseValues() []ObjectTypeBasicRestoreTargetInfoBase { - return []ObjectTypeBasicRestoreTargetInfoBase{ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo, ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo, ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo, ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase} -} - -// ObjectTypeBasicTriggerContext enumerates the values for object type basic trigger context. -type ObjectTypeBasicTriggerContext string - -const ( - // ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext ... - ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext ObjectTypeBasicTriggerContext = "AdhocBasedTriggerContext" - // ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext ... - ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext ObjectTypeBasicTriggerContext = "ScheduleBasedTriggerContext" - // ObjectTypeBasicTriggerContextObjectTypeTriggerContext ... - ObjectTypeBasicTriggerContextObjectTypeTriggerContext ObjectTypeBasicTriggerContext = "TriggerContext" -) - -// PossibleObjectTypeBasicTriggerContextValues returns an array of possible values for the ObjectTypeBasicTriggerContext const type. -func PossibleObjectTypeBasicTriggerContextValues() []ObjectTypeBasicTriggerContext { - return []ObjectTypeBasicTriggerContext{ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext, ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext, ObjectTypeBasicTriggerContextObjectTypeTriggerContext} -} - -// ProvisioningState enumerates the values for provisioning state. -type ProvisioningState string - -const ( - // ProvisioningStateFailed ... - ProvisioningStateFailed ProvisioningState = "Failed" - // ProvisioningStateProvisioning ... - ProvisioningStateProvisioning ProvisioningState = "Provisioning" - // ProvisioningStateSucceeded ... - ProvisioningStateSucceeded ProvisioningState = "Succeeded" - // ProvisioningStateUnknown ... - ProvisioningStateUnknown ProvisioningState = "Unknown" - // ProvisioningStateUpdating ... - ProvisioningStateUpdating ProvisioningState = "Updating" -) - -// PossibleProvisioningStateValues returns an array of possible values for the ProvisioningState const type. -func PossibleProvisioningStateValues() []ProvisioningState { - return []ProvisioningState{ProvisioningStateFailed, ProvisioningStateProvisioning, ProvisioningStateSucceeded, ProvisioningStateUnknown, ProvisioningStateUpdating} -} - -// RehydrationPriority enumerates the values for rehydration priority. -type RehydrationPriority string - -const ( - // RehydrationPriorityHigh ... - RehydrationPriorityHigh RehydrationPriority = "High" - // RehydrationPriorityInvalid ... - RehydrationPriorityInvalid RehydrationPriority = "Invalid" - // RehydrationPriorityStandard ... - RehydrationPriorityStandard RehydrationPriority = "Standard" -) - -// PossibleRehydrationPriorityValues returns an array of possible values for the RehydrationPriority const type. -func PossibleRehydrationPriorityValues() []RehydrationPriority { - return []RehydrationPriority{RehydrationPriorityHigh, RehydrationPriorityInvalid, RehydrationPriorityStandard} -} - -// RehydrationStatus enumerates the values for rehydration status. -type RehydrationStatus string - -const ( - // RehydrationStatusCOMPLETED ... - RehydrationStatusCOMPLETED RehydrationStatus = "COMPLETED" - // RehydrationStatusCREATEINPROGRESS ... - RehydrationStatusCREATEINPROGRESS RehydrationStatus = "CREATE_IN_PROGRESS" - // RehydrationStatusDELETED ... - RehydrationStatusDELETED RehydrationStatus = "DELETED" - // RehydrationStatusDELETEINPROGRESS ... - RehydrationStatusDELETEINPROGRESS RehydrationStatus = "DELETE_IN_PROGRESS" - // RehydrationStatusFAILED ... - RehydrationStatusFAILED RehydrationStatus = "FAILED" -) - -// PossibleRehydrationStatusValues returns an array of possible values for the RehydrationStatus const type. -func PossibleRehydrationStatusValues() []RehydrationStatus { - return []RehydrationStatus{RehydrationStatusCOMPLETED, RehydrationStatusCREATEINPROGRESS, RehydrationStatusDELETED, RehydrationStatusDELETEINPROGRESS, RehydrationStatusFAILED} -} - -// ResourceMoveState enumerates the values for resource move state. -type ResourceMoveState string - -const ( - // ResourceMoveStateCommitFailed ... - ResourceMoveStateCommitFailed ResourceMoveState = "CommitFailed" - // ResourceMoveStateCommitTimedout ... - ResourceMoveStateCommitTimedout ResourceMoveState = "CommitTimedout" - // ResourceMoveStateCriticalFailure ... - ResourceMoveStateCriticalFailure ResourceMoveState = "CriticalFailure" - // ResourceMoveStateFailed ... - ResourceMoveStateFailed ResourceMoveState = "Failed" - // ResourceMoveStateInProgress ... - ResourceMoveStateInProgress ResourceMoveState = "InProgress" - // ResourceMoveStateMoveSucceeded ... - ResourceMoveStateMoveSucceeded ResourceMoveState = "MoveSucceeded" - // ResourceMoveStatePartialSuccess ... - ResourceMoveStatePartialSuccess ResourceMoveState = "PartialSuccess" - // ResourceMoveStatePrepareFailed ... - ResourceMoveStatePrepareFailed ResourceMoveState = "PrepareFailed" - // ResourceMoveStatePrepareTimedout ... - ResourceMoveStatePrepareTimedout ResourceMoveState = "PrepareTimedout" - // ResourceMoveStateUnknown ... - ResourceMoveStateUnknown ResourceMoveState = "Unknown" -) - -// PossibleResourceMoveStateValues returns an array of possible values for the ResourceMoveState const type. -func PossibleResourceMoveStateValues() []ResourceMoveState { - return []ResourceMoveState{ResourceMoveStateCommitFailed, ResourceMoveStateCommitTimedout, ResourceMoveStateCriticalFailure, ResourceMoveStateFailed, ResourceMoveStateInProgress, ResourceMoveStateMoveSucceeded, ResourceMoveStatePartialSuccess, ResourceMoveStatePrepareFailed, ResourceMoveStatePrepareTimedout, ResourceMoveStateUnknown} -} - -// RestoreSourceDataStoreType enumerates the values for restore source data store type. -type RestoreSourceDataStoreType string - -const ( - // RestoreSourceDataStoreTypeArchiveStore ... - RestoreSourceDataStoreTypeArchiveStore RestoreSourceDataStoreType = "ArchiveStore" - // RestoreSourceDataStoreTypeOperationalStore ... - RestoreSourceDataStoreTypeOperationalStore RestoreSourceDataStoreType = "OperationalStore" - // RestoreSourceDataStoreTypeVaultStore ... - RestoreSourceDataStoreTypeVaultStore RestoreSourceDataStoreType = "VaultStore" -) - -// PossibleRestoreSourceDataStoreTypeValues returns an array of possible values for the RestoreSourceDataStoreType const type. -func PossibleRestoreSourceDataStoreTypeValues() []RestoreSourceDataStoreType { - return []RestoreSourceDataStoreType{RestoreSourceDataStoreTypeArchiveStore, RestoreSourceDataStoreTypeOperationalStore, RestoreSourceDataStoreTypeVaultStore} -} - -// RestoreTargetLocationType enumerates the values for restore target location type. -type RestoreTargetLocationType string - -const ( - // RestoreTargetLocationTypeAzureBlobs ... - RestoreTargetLocationTypeAzureBlobs RestoreTargetLocationType = "AzureBlobs" - // RestoreTargetLocationTypeAzureFiles ... - RestoreTargetLocationTypeAzureFiles RestoreTargetLocationType = "AzureFiles" - // RestoreTargetLocationTypeInvalid ... - RestoreTargetLocationTypeInvalid RestoreTargetLocationType = "Invalid" -) - -// PossibleRestoreTargetLocationTypeValues returns an array of possible values for the RestoreTargetLocationType const type. -func PossibleRestoreTargetLocationTypeValues() []RestoreTargetLocationType { - return []RestoreTargetLocationType{RestoreTargetLocationTypeAzureBlobs, RestoreTargetLocationTypeAzureFiles, RestoreTargetLocationTypeInvalid} -} - -// SecretStoreType enumerates the values for secret store type. -type SecretStoreType string - -const ( - // SecretStoreTypeAzureKeyVault ... - SecretStoreTypeAzureKeyVault SecretStoreType = "AzureKeyVault" - // SecretStoreTypeInvalid ... - SecretStoreTypeInvalid SecretStoreType = "Invalid" -) - -// PossibleSecretStoreTypeValues returns an array of possible values for the SecretStoreType const type. -func PossibleSecretStoreTypeValues() []SecretStoreType { - return []SecretStoreType{SecretStoreTypeAzureKeyVault, SecretStoreTypeInvalid} -} - -// SourceDataStoreType enumerates the values for source data store type. -type SourceDataStoreType string - -const ( - // SourceDataStoreTypeArchiveStore ... - SourceDataStoreTypeArchiveStore SourceDataStoreType = "ArchiveStore" - // SourceDataStoreTypeSnapshotStore ... - SourceDataStoreTypeSnapshotStore SourceDataStoreType = "SnapshotStore" - // SourceDataStoreTypeVaultStore ... - SourceDataStoreTypeVaultStore SourceDataStoreType = "VaultStore" -) - -// PossibleSourceDataStoreTypeValues returns an array of possible values for the SourceDataStoreType const type. -func PossibleSourceDataStoreTypeValues() []SourceDataStoreType { - return []SourceDataStoreType{SourceDataStoreTypeArchiveStore, SourceDataStoreTypeSnapshotStore, SourceDataStoreTypeVaultStore} -} - -// Status enumerates the values for status. -type Status string - -const ( - // StatusConfiguringProtection ... - StatusConfiguringProtection Status = "ConfiguringProtection" - // StatusConfiguringProtectionFailed ... - StatusConfiguringProtectionFailed Status = "ConfiguringProtectionFailed" - // StatusProtectionConfigured ... - StatusProtectionConfigured Status = "ProtectionConfigured" - // StatusProtectionStopped ... - StatusProtectionStopped Status = "ProtectionStopped" - // StatusSoftDeleted ... - StatusSoftDeleted Status = "SoftDeleted" - // StatusSoftDeleting ... - StatusSoftDeleting Status = "SoftDeleting" -) - -// PossibleStatusValues returns an array of possible values for the Status const type. -func PossibleStatusValues() []Status { - return []Status{StatusConfiguringProtection, StatusConfiguringProtectionFailed, StatusProtectionConfigured, StatusProtectionStopped, StatusSoftDeleted, StatusSoftDeleting} -} - -// StorageSettingStoreTypes enumerates the values for storage setting store types. -type StorageSettingStoreTypes string - -const ( - // StorageSettingStoreTypesArchiveStore ... - StorageSettingStoreTypesArchiveStore StorageSettingStoreTypes = "ArchiveStore" - // StorageSettingStoreTypesSnapshotStore ... - StorageSettingStoreTypesSnapshotStore StorageSettingStoreTypes = "SnapshotStore" - // StorageSettingStoreTypesVaultStore ... - StorageSettingStoreTypesVaultStore StorageSettingStoreTypes = "VaultStore" -) - -// PossibleStorageSettingStoreTypesValues returns an array of possible values for the StorageSettingStoreTypes const type. -func PossibleStorageSettingStoreTypesValues() []StorageSettingStoreTypes { - return []StorageSettingStoreTypes{StorageSettingStoreTypesArchiveStore, StorageSettingStoreTypesSnapshotStore, StorageSettingStoreTypesVaultStore} -} - -// StorageSettingTypes enumerates the values for storage setting types. -type StorageSettingTypes string - -const ( - // StorageSettingTypesGeoRedundant ... - StorageSettingTypesGeoRedundant StorageSettingTypes = "GeoRedundant" - // StorageSettingTypesLocallyRedundant ... - StorageSettingTypesLocallyRedundant StorageSettingTypes = "LocallyRedundant" -) - -// PossibleStorageSettingTypesValues returns an array of possible values for the StorageSettingTypes const type. -func PossibleStorageSettingTypesValues() []StorageSettingTypes { - return []StorageSettingTypes{StorageSettingTypesGeoRedundant, StorageSettingTypesLocallyRedundant} -} - -// WeekNumber enumerates the values for week number. -type WeekNumber string - -const ( - // WeekNumberFirst ... - WeekNumberFirst WeekNumber = "First" - // WeekNumberFourth ... - WeekNumberFourth WeekNumber = "Fourth" - // WeekNumberLast ... - WeekNumberLast WeekNumber = "Last" - // WeekNumberSecond ... - WeekNumberSecond WeekNumber = "Second" - // WeekNumberThird ... - WeekNumberThird WeekNumber = "Third" -) - -// PossibleWeekNumberValues returns an array of possible values for the WeekNumber const type. -func PossibleWeekNumberValues() []WeekNumber { - return []WeekNumber{WeekNumberFirst, WeekNumberFourth, WeekNumberLast, WeekNumberSecond, WeekNumberThird} -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/exportjobs.go b/internal/services/dataprotection/legacysdk/dataprotection/exportjobs.go deleted file mode 100644 index c99af74ac9d3..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/exportjobs.go +++ /dev/null @@ -1,109 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// ExportJobsClient is the open API 2.0 Specs for Azure Data Protection service -type ExportJobsClient struct { - BaseClient -} - -// NewExportJobsClient creates an instance of the ExportJobsClient client. -func NewExportJobsClient(subscriptionID string) ExportJobsClient { - return NewExportJobsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewExportJobsClientWithBaseURI creates an instance of the ExportJobsClient client using a custom endpoint. Use this -// when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewExportJobsClientWithBaseURI(baseURI string, subscriptionID string) ExportJobsClient { - return ExportJobsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Trigger triggers export of jobs and returns an OperationID to track. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// vaultName - the name of the backup vault. -func (client ExportJobsClient) Trigger(ctx context.Context, resourceGroupName string, vaultName string) (result ExportJobsTriggerFuture, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ExportJobsClient.Trigger") - defer func() { - sc := -1 - if result.FutureAPI != nil && result.FutureAPI.Response() != nil { - sc = result.FutureAPI.Response().StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.TriggerPreparer(ctx, resourceGroupName, vaultName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsClient", "Trigger", nil, "Failure preparing request") - return - } - - result, err = client.TriggerSender(req) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsClient", "Trigger", nil, "Failure sending request") - return - } - - return -} - -// TriggerPreparer prepares the Trigger request. -func (client ExportJobsClient) TriggerPreparer(ctx context.Context, resourceGroupName string, vaultName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/exportBackupJobs", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// TriggerSender sends the Trigger request. The method will close the -// http.Response Body if it receives an error. -func (client ExportJobsClient) TriggerSender(req *http.Request) (future ExportJobsTriggerFuture, err error) { - var resp *http.Response - resp, err = client.Send(req, azure.DoRetryWithRegistration(client.Client)) - if err != nil { - return - } - var azf azure.Future - azf, err = azure.NewFutureFromResponse(resp) - future.FutureAPI = &azf - future.Result = future.result - return -} - -// TriggerResponder handles the response to the Trigger request. The method always -// closes the http.Response Body. -func (client ExportJobsClient) TriggerResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/exportjobsoperationresult.go b/internal/services/dataprotection/legacysdk/dataprotection/exportjobsoperationresult.go deleted file mode 100644 index 03e70773d694..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/exportjobsoperationresult.go +++ /dev/null @@ -1,113 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// ExportJobsOperationResultClient is the open API 2.0 Specs for Azure Data Protection service -type ExportJobsOperationResultClient struct { - BaseClient -} - -// NewExportJobsOperationResultClient creates an instance of the ExportJobsOperationResultClient client. -func NewExportJobsOperationResultClient(subscriptionID string) ExportJobsOperationResultClient { - return NewExportJobsOperationResultClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewExportJobsOperationResultClientWithBaseURI creates an instance of the ExportJobsOperationResultClient client -// using a custom endpoint. Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign -// clouds, Azure stack). -func NewExportJobsOperationResultClientWithBaseURI(baseURI string, subscriptionID string) ExportJobsOperationResultClient { - return ExportJobsOperationResultClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get gets the operation result of operation triggered by Export Jobs API. If the operation is successful, then it -// also contains URL of a Blob and a SAS key to access the same. The blob contains exported jobs in JSON serialized -// format. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// vaultName - the name of the backup vault. -// operationID - operationID which represents the export job. -func (client ExportJobsOperationResultClient) Get(ctx context.Context, resourceGroupName string, vaultName string, operationID string) (result ExportJobsResult, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ExportJobsOperationResultClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, resourceGroupName, vaultName, operationID) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsOperationResultClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsOperationResultClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsOperationResultClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client ExportJobsOperationResultClient) GetPreparer(ctx context.Context, resourceGroupName string, vaultName string, operationID string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "operationId": autorest.Encode("path", operationID), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupJobs/operations/{operationId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client ExportJobsOperationResultClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client ExportJobsOperationResultClient) GetResponder(resp *http.Response) (result ExportJobsResult, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/jobs.go b/internal/services/dataprotection/legacysdk/dataprotection/jobs.go deleted file mode 100644 index e35e5a3f8966..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/jobs.go +++ /dev/null @@ -1,228 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// JobsClient is the open API 2.0 Specs for Azure Data Protection service -type JobsClient struct { - BaseClient -} - -// NewJobsClient creates an instance of the JobsClient client. -func NewJobsClient(subscriptionID string) JobsClient { - return NewJobsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewJobsClientWithBaseURI creates an instance of the JobsClient client using a custom endpoint. Use this when -// interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewJobsClientWithBaseURI(baseURI string, subscriptionID string) JobsClient { - return JobsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get gets a job with id in a backup vault -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// vaultName - the name of the backup vault. -// jobID - the Job ID. This is a GUID-formatted string (e.g. 00000000-0000-0000-0000-000000000000). -func (client JobsClient) Get(ctx context.Context, resourceGroupName string, vaultName string, jobID string) (result AzureBackupJobResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/JobsClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, resourceGroupName, vaultName, jobID) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client JobsClient) GetPreparer(ctx context.Context, resourceGroupName string, vaultName string, jobID string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "jobId": autorest.Encode("path", jobID), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupJobs/{jobId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client JobsClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client JobsClient) GetResponder(resp *http.Response) (result AzureBackupJobResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// List returns list of jobs belonging to a backup vault -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// vaultName - the name of the backup vault. -func (client JobsClient) List(ctx context.Context, resourceGroupName string, vaultName string) (result AzureBackupJobResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/JobsClient.List") - defer func() { - sc := -1 - if result.abjrl.Response.Response != nil { - sc = result.abjrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.listNextResults - req, err := client.ListPreparer(ctx, resourceGroupName, vaultName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "List", nil, "Failure preparing request") - return - } - - resp, err := client.ListSender(req) - if err != nil { - result.abjrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "List", resp, "Failure sending request") - return - } - - result.abjrl, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "List", resp, "Failure responding to request") - return - } - if result.abjrl.hasNextLink() && result.abjrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// ListPreparer prepares the List request. -func (client JobsClient) ListPreparer(ctx context.Context, resourceGroupName string, vaultName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupJobs", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ListSender sends the List request. The method will close the -// http.Response Body if it receives an error. -func (client JobsClient) ListSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// ListResponder handles the response to the List request. The method always -// closes the http.Response Body. -func (client JobsClient) ListResponder(resp *http.Response) (result AzureBackupJobResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// listNextResults retrieves the next set of results, if any. -func (client JobsClient) listNextResults(ctx context.Context, lastResults AzureBackupJobResourceList) (result AzureBackupJobResourceList, err error) { - req, err := lastResults.azureBackupJobResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.JobsClient", "listNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.ListSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.JobsClient", "listNextResults", resp, "Failure sending next results request") - } - result, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.JobsClient", "listNextResults", resp, "Failure responding to next results request") - } - return -} - -// ListComplete enumerates all values, automatically crossing page boundaries as required. -func (client JobsClient) ListComplete(ctx context.Context, resourceGroupName string, vaultName string) (result AzureBackupJobResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/JobsClient.List") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.List(ctx, resourceGroupName, vaultName) - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/models.go b/internal/services/dataprotection/legacysdk/dataprotection/models.go deleted file mode 100644 index b42f46007610..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/models.go +++ /dev/null @@ -1,6462 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/date" - "github.com/Azure/go-autorest/autorest/to" - "github.com/Azure/go-autorest/tracing" -) - -// The package's fully qualified name. -const fqdn = "github.com/Azure/azure-sdk-for-go/services/dataprotection/mgmt/2021-07-01/dataprotection" - -// AbsoluteDeleteOption delete option with duration -type AbsoluteDeleteOption struct { - // Duration - Duration of deletion after given timespan - Duration *string `json:"duration,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicDeleteOptionObjectTypeDeleteOption', 'ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption' - ObjectType ObjectTypeBasicDeleteOption `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AbsoluteDeleteOption. -func (ado AbsoluteDeleteOption) MarshalJSON() ([]byte, error) { - ado.ObjectType = ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption - objectMap := make(map[string]interface{}) - if ado.Duration != nil { - objectMap["duration"] = ado.Duration - } - if ado.ObjectType != "" { - objectMap["objectType"] = ado.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAbsoluteDeleteOption is the BasicDeleteOption implementation for AbsoluteDeleteOption. -func (ado AbsoluteDeleteOption) AsAbsoluteDeleteOption() (*AbsoluteDeleteOption, bool) { - return &ado, true -} - -// AsDeleteOption is the BasicDeleteOption implementation for AbsoluteDeleteOption. -func (ado AbsoluteDeleteOption) AsDeleteOption() (*DeleteOption, bool) { - return nil, false -} - -// AsBasicDeleteOption is the BasicDeleteOption implementation for AbsoluteDeleteOption. -func (ado AbsoluteDeleteOption) AsBasicDeleteOption() (BasicDeleteOption, bool) { - return &ado, true -} - -// AdHocBackupRuleOptions adhoc backup rules -type AdHocBackupRuleOptions struct { - RuleName *string `json:"ruleName,omitempty"` - TriggerOption *AdhocBackupTriggerOption `json:"triggerOption,omitempty"` -} - -// AdhocBackupTriggerOption adhoc backup trigger option -type AdhocBackupTriggerOption struct { - RetentionTagOverride *string `json:"retentionTagOverride,omitempty"` -} - -// AdhocBasedTaggingCriteria adhoc backup tagging criteria -type AdhocBasedTaggingCriteria struct { - // TagInfo - Retention tag information - TagInfo *RetentionTag `json:"tagInfo,omitempty"` -} - -// AdhocBasedTriggerContext adhoc trigger context -type AdhocBasedTriggerContext struct { - // TaggingCriteria - Tagging Criteria containing retention tag for adhoc backup. - TaggingCriteria *AdhocBasedTaggingCriteria `json:"taggingCriteria,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicTriggerContextObjectTypeTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext' - ObjectType ObjectTypeBasicTriggerContext `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AdhocBasedTriggerContext. -func (abtc AdhocBasedTriggerContext) MarshalJSON() ([]byte, error) { - abtc.ObjectType = ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext - objectMap := make(map[string]interface{}) - if abtc.TaggingCriteria != nil { - objectMap["taggingCriteria"] = abtc.TaggingCriteria - } - if abtc.ObjectType != "" { - objectMap["objectType"] = abtc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAdhocBasedTriggerContext is the BasicTriggerContext implementation for AdhocBasedTriggerContext. -func (abtc AdhocBasedTriggerContext) AsAdhocBasedTriggerContext() (*AdhocBasedTriggerContext, bool) { - return &abtc, true -} - -// AsScheduleBasedTriggerContext is the BasicTriggerContext implementation for AdhocBasedTriggerContext. -func (abtc AdhocBasedTriggerContext) AsScheduleBasedTriggerContext() (*ScheduleBasedTriggerContext, bool) { - return nil, false -} - -// AsTriggerContext is the BasicTriggerContext implementation for AdhocBasedTriggerContext. -func (abtc AdhocBasedTriggerContext) AsTriggerContext() (*TriggerContext, bool) { - return nil, false -} - -// AsBasicTriggerContext is the BasicTriggerContext implementation for AdhocBasedTriggerContext. -func (abtc AdhocBasedTriggerContext) AsBasicTriggerContext() (BasicTriggerContext, bool) { - return &abtc, true -} - -// BasicAuthCredentials base class for different types of authentication credentials. -type BasicAuthCredentials interface { - AsSecretStoreBasedAuthCredentials() (*SecretStoreBasedAuthCredentials, bool) - AsAuthCredentials() (*AuthCredentials, bool) -} - -// AuthCredentials base class for different types of authentication credentials. -type AuthCredentials struct { - // ObjectType - Possible values include: 'ObjectTypeAuthCredentials', 'ObjectTypeSecretStoreBasedAuthCredentials' - ObjectType ObjectType `json:"objectType,omitempty"` -} - -func unmarshalBasicAuthCredentials(body []byte) (BasicAuthCredentials, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeSecretStoreBasedAuthCredentials): - var ssbac SecretStoreBasedAuthCredentials - err := json.Unmarshal(body, &ssbac) - return ssbac, err - default: - var ac AuthCredentials - err := json.Unmarshal(body, &ac) - return ac, err - } -} - -func unmarshalBasicAuthCredentialsArray(body []byte) ([]BasicAuthCredentials, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - acArray := make([]BasicAuthCredentials, len(rawMessages)) - - for index, rawMessage := range rawMessages { - ac, err := unmarshalBasicAuthCredentials(*rawMessage) - if err != nil { - return nil, err - } - acArray[index] = ac - } - return acArray, nil -} - -// MarshalJSON is the custom marshaler for AuthCredentials. -func (ac AuthCredentials) MarshalJSON() ([]byte, error) { - ac.ObjectType = ObjectTypeAuthCredentials - objectMap := make(map[string]interface{}) - if ac.ObjectType != "" { - objectMap["objectType"] = ac.ObjectType - } - return json.Marshal(objectMap) -} - -// AsSecretStoreBasedAuthCredentials is the BasicAuthCredentials implementation for AuthCredentials. -func (ac AuthCredentials) AsSecretStoreBasedAuthCredentials() (*SecretStoreBasedAuthCredentials, bool) { - return nil, false -} - -// AsAuthCredentials is the BasicAuthCredentials implementation for AuthCredentials. -func (ac AuthCredentials) AsAuthCredentials() (*AuthCredentials, bool) { - return &ac, true -} - -// AsBasicAuthCredentials is the BasicAuthCredentials implementation for AuthCredentials. -func (ac AuthCredentials) AsBasicAuthCredentials() (BasicAuthCredentials, bool) { - return &ac, true -} - -// AzureBackupDiscreteRecoveryPoint azure backup discrete RecoveryPoint -type AzureBackupDiscreteRecoveryPoint struct { - FriendlyName *string `json:"friendlyName,omitempty"` - RecoveryPointDataStoresDetails *[]RecoveryPointDataStoreDetails `json:"recoveryPointDataStoresDetails,omitempty"` - RecoveryPointTime *date.Time `json:"recoveryPointTime,omitempty"` - PolicyName *string `json:"policyName,omitempty"` - PolicyVersion *string `json:"policyVersion,omitempty"` - RecoveryPointID *string `json:"recoveryPointId,omitempty"` - RecoveryPointType *string `json:"recoveryPointType,omitempty"` - RetentionTagName *string `json:"retentionTagName,omitempty"` - RetentionTagVersion *string `json:"retentionTagVersion,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint', 'ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint' - ObjectType ObjectTypeBasicAzureBackupRecoveryPoint `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupDiscreteRecoveryPoint. -func (abdrp AzureBackupDiscreteRecoveryPoint) MarshalJSON() ([]byte, error) { - abdrp.ObjectType = ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint - objectMap := make(map[string]interface{}) - if abdrp.FriendlyName != nil { - objectMap["friendlyName"] = abdrp.FriendlyName - } - if abdrp.RecoveryPointDataStoresDetails != nil { - objectMap["recoveryPointDataStoresDetails"] = abdrp.RecoveryPointDataStoresDetails - } - if abdrp.RecoveryPointTime != nil { - objectMap["recoveryPointTime"] = abdrp.RecoveryPointTime - } - if abdrp.PolicyName != nil { - objectMap["policyName"] = abdrp.PolicyName - } - if abdrp.PolicyVersion != nil { - objectMap["policyVersion"] = abdrp.PolicyVersion - } - if abdrp.RecoveryPointID != nil { - objectMap["recoveryPointId"] = abdrp.RecoveryPointID - } - if abdrp.RecoveryPointType != nil { - objectMap["recoveryPointType"] = abdrp.RecoveryPointType - } - if abdrp.RetentionTagName != nil { - objectMap["retentionTagName"] = abdrp.RetentionTagName - } - if abdrp.RetentionTagVersion != nil { - objectMap["retentionTagVersion"] = abdrp.RetentionTagVersion - } - if abdrp.ObjectType != "" { - objectMap["objectType"] = abdrp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupDiscreteRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupDiscreteRecoveryPoint. -func (abdrp AzureBackupDiscreteRecoveryPoint) AsAzureBackupDiscreteRecoveryPoint() (*AzureBackupDiscreteRecoveryPoint, bool) { - return &abdrp, true -} - -// AsAzureBackupRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupDiscreteRecoveryPoint. -func (abdrp AzureBackupDiscreteRecoveryPoint) AsAzureBackupRecoveryPoint() (*AzureBackupRecoveryPoint, bool) { - return nil, false -} - -// AsBasicAzureBackupRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupDiscreteRecoveryPoint. -func (abdrp AzureBackupDiscreteRecoveryPoint) AsBasicAzureBackupRecoveryPoint() (BasicAzureBackupRecoveryPoint, bool) { - return &abdrp, true -} - -// AzureBackupFindRestorableTimeRangesRequest list Restore Ranges Request -type AzureBackupFindRestorableTimeRangesRequest struct { - // SourceDataStoreType - Gets or sets the type of the source data store. Possible values include: 'RestoreSourceDataStoreTypeOperationalStore', 'RestoreSourceDataStoreTypeVaultStore', 'RestoreSourceDataStoreTypeArchiveStore' - SourceDataStoreType RestoreSourceDataStoreType `json:"sourceDataStoreType,omitempty"` - // StartTime - Start time for the List Restore Ranges request. ISO 8601 format. - StartTime *string `json:"startTime,omitempty"` - // EndTime - End time for the List Restore Ranges request. ISO 8601 format. - EndTime *string `json:"endTime,omitempty"` -} - -// AzureBackupFindRestorableTimeRangesRequestResource list Restore Ranges Request -type AzureBackupFindRestorableTimeRangesRequestResource struct { - // Content - AzureBackupFindRestorableTimeRangesRequestResource content - Content *AzureBackupFindRestorableTimeRangesRequest `json:"content,omitempty"` - SubscriptionID *string `json:"subscriptionId,omitempty"` - URI *string `json:"uri,omitempty"` - Headers map[string][]string `json:"headers"` - SupportedGroupVersions *[]string `json:"supportedGroupVersions,omitempty"` - CultureInfo *string `json:"cultureInfo,omitempty"` - Parameters map[string]*string `json:"parameters"` - HTTPMethod *string `json:"httpMethod,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupFindRestorableTimeRangesRequestResource. -func (abfrtrrr AzureBackupFindRestorableTimeRangesRequestResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if abfrtrrr.Content != nil { - objectMap["content"] = abfrtrrr.Content - } - if abfrtrrr.SubscriptionID != nil { - objectMap["subscriptionId"] = abfrtrrr.SubscriptionID - } - if abfrtrrr.URI != nil { - objectMap["uri"] = abfrtrrr.URI - } - if abfrtrrr.Headers != nil { - objectMap["headers"] = abfrtrrr.Headers - } - if abfrtrrr.SupportedGroupVersions != nil { - objectMap["supportedGroupVersions"] = abfrtrrr.SupportedGroupVersions - } - if abfrtrrr.CultureInfo != nil { - objectMap["cultureInfo"] = abfrtrrr.CultureInfo - } - if abfrtrrr.Parameters != nil { - objectMap["parameters"] = abfrtrrr.Parameters - } - if abfrtrrr.HTTPMethod != nil { - objectMap["httpMethod"] = abfrtrrr.HTTPMethod - } - return json.Marshal(objectMap) -} - -// AzureBackupFindRestorableTimeRangesResponse list Restore Ranges Response -type AzureBackupFindRestorableTimeRangesResponse struct { - // RestorableTimeRanges - Returns the Restore Ranges available on the Backup Instance. - RestorableTimeRanges *[]RestorableTimeRange `json:"restorableTimeRanges,omitempty"` - ObjectType *string `json:"objectType,omitempty"` -} - -// AzureBackupFindRestorableTimeRangesResponseResource list Restore Ranges Response -type AzureBackupFindRestorableTimeRangesResponseResource struct { - autorest.Response `json:"-"` - // Properties - AzureBackupFindRestorableTimeRangesResponseResource properties - Properties *AzureBackupFindRestorableTimeRangesResponse `json:"properties,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupFindRestorableTimeRangesResponseResource. -func (abfrtrrr AzureBackupFindRestorableTimeRangesResponseResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if abfrtrrr.Properties != nil { - objectMap["properties"] = abfrtrrr.Properties - } - if abfrtrrr.SystemData != nil { - objectMap["systemData"] = abfrtrrr.SystemData - } - return json.Marshal(objectMap) -} - -// AzureBackupJob azureBackup Job Class -type AzureBackupJob struct { - // ActivityID - Job Activity Id - ActivityID *string `json:"activityID,omitempty"` - // BackupInstanceFriendlyName - Name of the Backup Instance - BackupInstanceFriendlyName *string `json:"backupInstanceFriendlyName,omitempty"` - // BackupInstanceID - READ-ONLY; ARM ID of the Backup Instance - BackupInstanceID *string `json:"backupInstanceId,omitempty"` - // DataSourceID - ARM ID of the DataSource - DataSourceID *string `json:"dataSourceId,omitempty"` - // DataSourceLocation - Location of the DataSource - DataSourceLocation *string `json:"dataSourceLocation,omitempty"` - // DataSourceName - User Friendly Name of the DataSource - DataSourceName *string `json:"dataSourceName,omitempty"` - // DataSourceSetName - Data Source Set Name of the DataSource - DataSourceSetName *string `json:"dataSourceSetName,omitempty"` - // DataSourceType - Type of DataSource - DataSourceType *string `json:"dataSourceType,omitempty"` - // Duration - Total run time of the job. ISO 8601 format. - Duration *string `json:"duration,omitempty"` - // EndTime - READ-ONLY; EndTime of the job(in UTC) - EndTime *date.Time `json:"endTime,omitempty"` - // ErrorDetails - READ-ONLY; A List, detailing the errors related to the job - ErrorDetails *[]UserFacingError `json:"errorDetails,omitempty"` - // ExtendedInfo - READ-ONLY; Extended Information about the job - ExtendedInfo *JobExtendedInfo `json:"extendedInfo,omitempty"` - // IsUserTriggered - Indicated that whether the job is adhoc(true) or scheduled(false) - IsUserTriggered *bool `json:"isUserTriggered,omitempty"` - // Operation - It indicates the type of Job i.e. Backup:full/log/diff ;Restore:ALR/OLR; Tiering:Backup/Archive ; Management:ConfigureProtection/UnConfigure - Operation *string `json:"operation,omitempty"` - // OperationCategory - It indicates the type of Job i.e. Backup/Restore/Tiering/Management - OperationCategory *string `json:"operationCategory,omitempty"` - // PolicyID - READ-ONLY; ARM ID of the policy - PolicyID *string `json:"policyId,omitempty"` - // PolicyName - READ-ONLY; Name of the policy - PolicyName *string `json:"policyName,omitempty"` - // ProgressEnabled - Indicated whether progress is enabled for the job - ProgressEnabled *bool `json:"progressEnabled,omitempty"` - // ProgressURL - READ-ONLY; Url which contains job's progress - ProgressURL *string `json:"progressUrl,omitempty"` - // RestoreType - READ-ONLY; It indicates the sub type of operation i.e. in case of Restore it can be ALR/OLR - RestoreType *string `json:"restoreType,omitempty"` - // SourceResourceGroup - Resource Group Name of the Datasource - SourceResourceGroup *string `json:"sourceResourceGroup,omitempty"` - // SourceSubscriptionID - SubscriptionId corresponding to the DataSource - SourceSubscriptionID *string `json:"sourceSubscriptionID,omitempty"` - // StartTime - StartTime of the job(in UTC) - StartTime *date.Time `json:"startTime,omitempty"` - // Status - Status of the job like InProgress/Success/Failed/Cancelled/SuccessWithWarning - Status *string `json:"status,omitempty"` - // SubscriptionID - Subscription Id of the corresponding backup vault - SubscriptionID *string `json:"subscriptionId,omitempty"` - // SupportedActions - List of supported actions - SupportedActions *[]string `json:"supportedActions,omitempty"` - // VaultName - Name of the vault - VaultName *string `json:"vaultName,omitempty"` - Etag *string `json:"etag,omitempty"` - SourceDataStoreName *string `json:"sourceDataStoreName,omitempty"` - DestinationDataStoreName *string `json:"destinationDataStoreName,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupJob. -func (abj AzureBackupJob) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if abj.ActivityID != nil { - objectMap["activityID"] = abj.ActivityID - } - if abj.BackupInstanceFriendlyName != nil { - objectMap["backupInstanceFriendlyName"] = abj.BackupInstanceFriendlyName - } - if abj.DataSourceID != nil { - objectMap["dataSourceId"] = abj.DataSourceID - } - if abj.DataSourceLocation != nil { - objectMap["dataSourceLocation"] = abj.DataSourceLocation - } - if abj.DataSourceName != nil { - objectMap["dataSourceName"] = abj.DataSourceName - } - if abj.DataSourceSetName != nil { - objectMap["dataSourceSetName"] = abj.DataSourceSetName - } - if abj.DataSourceType != nil { - objectMap["dataSourceType"] = abj.DataSourceType - } - if abj.Duration != nil { - objectMap["duration"] = abj.Duration - } - if abj.IsUserTriggered != nil { - objectMap["isUserTriggered"] = abj.IsUserTriggered - } - if abj.Operation != nil { - objectMap["operation"] = abj.Operation - } - if abj.OperationCategory != nil { - objectMap["operationCategory"] = abj.OperationCategory - } - if abj.ProgressEnabled != nil { - objectMap["progressEnabled"] = abj.ProgressEnabled - } - if abj.SourceResourceGroup != nil { - objectMap["sourceResourceGroup"] = abj.SourceResourceGroup - } - if abj.SourceSubscriptionID != nil { - objectMap["sourceSubscriptionID"] = abj.SourceSubscriptionID - } - if abj.StartTime != nil { - objectMap["startTime"] = abj.StartTime - } - if abj.Status != nil { - objectMap["status"] = abj.Status - } - if abj.SubscriptionID != nil { - objectMap["subscriptionId"] = abj.SubscriptionID - } - if abj.SupportedActions != nil { - objectMap["supportedActions"] = abj.SupportedActions - } - if abj.VaultName != nil { - objectMap["vaultName"] = abj.VaultName - } - if abj.Etag != nil { - objectMap["etag"] = abj.Etag - } - if abj.SourceDataStoreName != nil { - objectMap["sourceDataStoreName"] = abj.SourceDataStoreName - } - if abj.DestinationDataStoreName != nil { - objectMap["destinationDataStoreName"] = abj.DestinationDataStoreName - } - return json.Marshal(objectMap) -} - -// AzureBackupJobResource azureBackup Job Resource Class -type AzureBackupJobResource struct { - autorest.Response `json:"-"` - // Properties - AzureBackupJobResource properties - Properties *AzureBackupJob `json:"properties,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupJobResource. -func (abjr AzureBackupJobResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if abjr.Properties != nil { - objectMap["properties"] = abjr.Properties - } - if abjr.SystemData != nil { - objectMap["systemData"] = abjr.SystemData - } - return json.Marshal(objectMap) -} - -// AzureBackupJobResourceList list of AzureBackup Job resources -type AzureBackupJobResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]AzureBackupJobResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// AzureBackupJobResourceListIterator provides access to a complete listing of AzureBackupJobResource -// values. -type AzureBackupJobResourceListIterator struct { - i int - page AzureBackupJobResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *AzureBackupJobResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/AzureBackupJobResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *AzureBackupJobResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter AzureBackupJobResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter AzureBackupJobResourceListIterator) Response() AzureBackupJobResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter AzureBackupJobResourceListIterator) Value() AzureBackupJobResource { - if !iter.page.NotDone() { - return AzureBackupJobResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the AzureBackupJobResourceListIterator type. -func NewAzureBackupJobResourceListIterator(page AzureBackupJobResourceListPage) AzureBackupJobResourceListIterator { - return AzureBackupJobResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (abjrl AzureBackupJobResourceList) IsEmpty() bool { - return abjrl.Value == nil || len(*abjrl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (abjrl AzureBackupJobResourceList) hasNextLink() bool { - return abjrl.NextLink != nil && len(*abjrl.NextLink) != 0 -} - -// azureBackupJobResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (abjrl AzureBackupJobResourceList) azureBackupJobResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !abjrl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(abjrl.NextLink))) -} - -// AzureBackupJobResourceListPage contains a page of AzureBackupJobResource values. -type AzureBackupJobResourceListPage struct { - fn func(context.Context, AzureBackupJobResourceList) (AzureBackupJobResourceList, error) - abjrl AzureBackupJobResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *AzureBackupJobResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/AzureBackupJobResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.abjrl) - if err != nil { - return err - } - page.abjrl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *AzureBackupJobResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page AzureBackupJobResourceListPage) NotDone() bool { - return !page.abjrl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page AzureBackupJobResourceListPage) Response() AzureBackupJobResourceList { - return page.abjrl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page AzureBackupJobResourceListPage) Values() []AzureBackupJobResource { - if page.abjrl.IsEmpty() { - return nil - } - return *page.abjrl.Value -} - -// Creates a new instance of the AzureBackupJobResourceListPage type. -func NewAzureBackupJobResourceListPage(cur AzureBackupJobResourceList, getNextPage func(context.Context, AzureBackupJobResourceList) (AzureBackupJobResourceList, error)) AzureBackupJobResourceListPage { - return AzureBackupJobResourceListPage{ - fn: getNextPage, - abjrl: cur, - } -} - -// AzureBackupParams azure backup parameters -type AzureBackupParams struct { - // BackupType - BackupType ; Full/Incremental etc - BackupType *string `json:"backupType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBackupParametersObjectTypeBackupParameters', 'ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams' - ObjectType ObjectTypeBasicBackupParameters `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupParams. -func (abp AzureBackupParams) MarshalJSON() ([]byte, error) { - abp.ObjectType = ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams - objectMap := make(map[string]interface{}) - if abp.BackupType != nil { - objectMap["backupType"] = abp.BackupType - } - if abp.ObjectType != "" { - objectMap["objectType"] = abp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupParams is the BasicBackupParameters implementation for AzureBackupParams. -func (abp AzureBackupParams) AsAzureBackupParams() (*AzureBackupParams, bool) { - return &abp, true -} - -// AsBackupParameters is the BasicBackupParameters implementation for AzureBackupParams. -func (abp AzureBackupParams) AsBackupParameters() (*BackupParameters, bool) { - return nil, false -} - -// AsBasicBackupParameters is the BasicBackupParameters implementation for AzureBackupParams. -func (abp AzureBackupParams) AsBasicBackupParameters() (BasicBackupParameters, bool) { - return &abp, true -} - -// BasicAzureBackupRecoveryPoint azure backup recoveryPoint -type BasicAzureBackupRecoveryPoint interface { - AsAzureBackupDiscreteRecoveryPoint() (*AzureBackupDiscreteRecoveryPoint, bool) - AsAzureBackupRecoveryPoint() (*AzureBackupRecoveryPoint, bool) -} - -// AzureBackupRecoveryPoint azure backup recoveryPoint -type AzureBackupRecoveryPoint struct { - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint', 'ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint' - ObjectType ObjectTypeBasicAzureBackupRecoveryPoint `json:"objectType,omitempty"` -} - -func unmarshalBasicAzureBackupRecoveryPoint(body []byte) (BasicAzureBackupRecoveryPoint, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupDiscreteRecoveryPoint): - var abdrp AzureBackupDiscreteRecoveryPoint - err := json.Unmarshal(body, &abdrp) - return abdrp, err - default: - var abrp AzureBackupRecoveryPoint - err := json.Unmarshal(body, &abrp) - return abrp, err - } -} - -func unmarshalBasicAzureBackupRecoveryPointArray(body []byte) ([]BasicAzureBackupRecoveryPoint, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - abrpArray := make([]BasicAzureBackupRecoveryPoint, len(rawMessages)) - - for index, rawMessage := range rawMessages { - abrp, err := unmarshalBasicAzureBackupRecoveryPoint(*rawMessage) - if err != nil { - return nil, err - } - abrpArray[index] = abrp - } - return abrpArray, nil -} - -// MarshalJSON is the custom marshaler for AzureBackupRecoveryPoint. -func (abrp AzureBackupRecoveryPoint) MarshalJSON() ([]byte, error) { - abrp.ObjectType = ObjectTypeBasicAzureBackupRecoveryPointObjectTypeAzureBackupRecoveryPoint - objectMap := make(map[string]interface{}) - if abrp.ObjectType != "" { - objectMap["objectType"] = abrp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupDiscreteRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupRecoveryPoint. -func (abrp AzureBackupRecoveryPoint) AsAzureBackupDiscreteRecoveryPoint() (*AzureBackupDiscreteRecoveryPoint, bool) { - return nil, false -} - -// AsAzureBackupRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupRecoveryPoint. -func (abrp AzureBackupRecoveryPoint) AsAzureBackupRecoveryPoint() (*AzureBackupRecoveryPoint, bool) { - return &abrp, true -} - -// AsBasicAzureBackupRecoveryPoint is the BasicAzureBackupRecoveryPoint implementation for AzureBackupRecoveryPoint. -func (abrp AzureBackupRecoveryPoint) AsBasicAzureBackupRecoveryPoint() (BasicAzureBackupRecoveryPoint, bool) { - return &abrp, true -} - -// BasicAzureBackupRecoveryPointBasedRestoreRequest azure backup recoveryPoint based restore request -type BasicAzureBackupRecoveryPointBasedRestoreRequest interface { - AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) - AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) -} - -// AzureBackupRecoveryPointBasedRestoreRequest azure backup recoveryPoint based restore request -type AzureBackupRecoveryPointBasedRestoreRequest struct { - RecoveryPointID *string `json:"recoveryPointId,omitempty"` - // RestoreTargetInfo - Gets or sets the restore target information. - RestoreTargetInfo BasicRestoreTargetInfoBase `json:"restoreTargetInfo,omitempty"` - // SourceDataStoreType - Gets or sets the type of the source data store. Possible values include: 'SourceDataStoreTypeArchiveStore', 'SourceDataStoreTypeSnapshotStore', 'SourceDataStoreTypeVaultStore' - SourceDataStoreType SourceDataStoreType `json:"sourceDataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest' - ObjectType ObjectTypeBasicAzureBackupRestoreRequest `json:"objectType,omitempty"` -} - -func unmarshalBasicAzureBackupRecoveryPointBasedRestoreRequest(body []byte) (BasicAzureBackupRecoveryPointBasedRestoreRequest, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest): - var abrwrr AzureBackupRestoreWithRehydrationRequest - err := json.Unmarshal(body, &abrwrr) - return abrwrr, err - default: - var abrpbrr AzureBackupRecoveryPointBasedRestoreRequest - err := json.Unmarshal(body, &abrpbrr) - return abrpbrr, err - } -} - -func unmarshalBasicAzureBackupRecoveryPointBasedRestoreRequestArray(body []byte) ([]BasicAzureBackupRecoveryPointBasedRestoreRequest, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - abrpbrrArray := make([]BasicAzureBackupRecoveryPointBasedRestoreRequest, len(rawMessages)) - - for index, rawMessage := range rawMessages { - abrpbrr, err := unmarshalBasicAzureBackupRecoveryPointBasedRestoreRequest(*rawMessage) - if err != nil { - return nil, err - } - abrpbrrArray[index] = abrpbrr - } - return abrpbrrArray, nil -} - -// MarshalJSON is the custom marshaler for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) MarshalJSON() ([]byte, error) { - abrpbrr.ObjectType = ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest - objectMap := make(map[string]interface{}) - if abrpbrr.RecoveryPointID != nil { - objectMap["recoveryPointId"] = abrpbrr.RecoveryPointID - } - objectMap["restoreTargetInfo"] = abrpbrr.RestoreTargetInfo - if abrpbrr.SourceDataStoreType != "" { - objectMap["sourceDataStoreType"] = abrpbrr.SourceDataStoreType - } - if abrpbrr.ObjectType != "" { - objectMap["objectType"] = abrpbrr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) { - return &abrpbrr, true -} - -// AsBasicAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsBasicAzureBackupRecoveryPointBasedRestoreRequest() (BasicAzureBackupRecoveryPointBasedRestoreRequest, bool) { - return &abrpbrr, true -} - -// AsAzureBackupRestoreWithRehydrationRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) { - return nil, false -} - -// AsAzureBackupRecoveryTimeBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsAzureBackupRecoveryTimeBasedRestoreRequest() (*AzureBackupRecoveryTimeBasedRestoreRequest, bool) { - return nil, false -} - -// AsAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsAzureBackupRestoreRequest() (*AzureBackupRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryPointBasedRestoreRequest. -func (abrpbrr AzureBackupRecoveryPointBasedRestoreRequest) AsBasicAzureBackupRestoreRequest() (BasicAzureBackupRestoreRequest, bool) { - return &abrpbrr, true -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRecoveryPointBasedRestoreRequest struct. -func (abrpbrr *AzureBackupRecoveryPointBasedRestoreRequest) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "recoveryPointId": - if v != nil { - var recoveryPointID string - err = json.Unmarshal(*v, &recoveryPointID) - if err != nil { - return err - } - abrpbrr.RecoveryPointID = &recoveryPointID - } - case "restoreTargetInfo": - if v != nil { - restoreTargetInfo, err := unmarshalBasicRestoreTargetInfoBase(*v) - if err != nil { - return err - } - abrpbrr.RestoreTargetInfo = restoreTargetInfo - } - case "sourceDataStoreType": - if v != nil { - var sourceDataStoreType SourceDataStoreType - err = json.Unmarshal(*v, &sourceDataStoreType) - if err != nil { - return err - } - abrpbrr.SourceDataStoreType = sourceDataStoreType - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicAzureBackupRestoreRequest - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - abrpbrr.ObjectType = objectType - } - } - } - - return nil -} - -// AzureBackupRecoveryPointResource azure backup recoveryPoint resource -type AzureBackupRecoveryPointResource struct { - autorest.Response `json:"-"` - // Properties - AzureBackupRecoveryPointResource properties - Properties BasicAzureBackupRecoveryPoint `json:"properties,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupRecoveryPointResource. -func (abrpr AzureBackupRecoveryPointResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - objectMap["properties"] = abrpr.Properties - if abrpr.SystemData != nil { - objectMap["systemData"] = abrpr.SystemData - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRecoveryPointResource struct. -func (abrpr *AzureBackupRecoveryPointResource) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "properties": - if v != nil { - properties, err := unmarshalBasicAzureBackupRecoveryPoint(*v) - if err != nil { - return err - } - abrpr.Properties = properties - } - case "id": - if v != nil { - var ID string - err = json.Unmarshal(*v, &ID) - if err != nil { - return err - } - abrpr.ID = &ID - } - case "name": - if v != nil { - var name string - err = json.Unmarshal(*v, &name) - if err != nil { - return err - } - abrpr.Name = &name - } - case "type": - if v != nil { - var typeVar string - err = json.Unmarshal(*v, &typeVar) - if err != nil { - return err - } - abrpr.Type = &typeVar - } - case "systemData": - if v != nil { - var systemData SystemData - err = json.Unmarshal(*v, &systemData) - if err != nil { - return err - } - abrpr.SystemData = &systemData - } - } - } - - return nil -} - -// AzureBackupRecoveryPointResourceList azure backup recoveryPoint resource list -type AzureBackupRecoveryPointResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]AzureBackupRecoveryPointResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// AzureBackupRecoveryPointResourceListIterator provides access to a complete listing of -// AzureBackupRecoveryPointResource values. -type AzureBackupRecoveryPointResourceListIterator struct { - i int - page AzureBackupRecoveryPointResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *AzureBackupRecoveryPointResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/AzureBackupRecoveryPointResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *AzureBackupRecoveryPointResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter AzureBackupRecoveryPointResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter AzureBackupRecoveryPointResourceListIterator) Response() AzureBackupRecoveryPointResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter AzureBackupRecoveryPointResourceListIterator) Value() AzureBackupRecoveryPointResource { - if !iter.page.NotDone() { - return AzureBackupRecoveryPointResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the AzureBackupRecoveryPointResourceListIterator type. -func NewAzureBackupRecoveryPointResourceListIterator(page AzureBackupRecoveryPointResourceListPage) AzureBackupRecoveryPointResourceListIterator { - return AzureBackupRecoveryPointResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (abrprl AzureBackupRecoveryPointResourceList) IsEmpty() bool { - return abrprl.Value == nil || len(*abrprl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (abrprl AzureBackupRecoveryPointResourceList) hasNextLink() bool { - return abrprl.NextLink != nil && len(*abrprl.NextLink) != 0 -} - -// azureBackupRecoveryPointResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (abrprl AzureBackupRecoveryPointResourceList) azureBackupRecoveryPointResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !abrprl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(abrprl.NextLink))) -} - -// AzureBackupRecoveryPointResourceListPage contains a page of AzureBackupRecoveryPointResource values. -type AzureBackupRecoveryPointResourceListPage struct { - fn func(context.Context, AzureBackupRecoveryPointResourceList) (AzureBackupRecoveryPointResourceList, error) - abrprl AzureBackupRecoveryPointResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *AzureBackupRecoveryPointResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/AzureBackupRecoveryPointResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.abrprl) - if err != nil { - return err - } - page.abrprl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *AzureBackupRecoveryPointResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page AzureBackupRecoveryPointResourceListPage) NotDone() bool { - return !page.abrprl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page AzureBackupRecoveryPointResourceListPage) Response() AzureBackupRecoveryPointResourceList { - return page.abrprl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page AzureBackupRecoveryPointResourceListPage) Values() []AzureBackupRecoveryPointResource { - if page.abrprl.IsEmpty() { - return nil - } - return *page.abrprl.Value -} - -// Creates a new instance of the AzureBackupRecoveryPointResourceListPage type. -func NewAzureBackupRecoveryPointResourceListPage(cur AzureBackupRecoveryPointResourceList, getNextPage func(context.Context, AzureBackupRecoveryPointResourceList) (AzureBackupRecoveryPointResourceList, error)) AzureBackupRecoveryPointResourceListPage { - return AzureBackupRecoveryPointResourceListPage{ - fn: getNextPage, - abrprl: cur, - } -} - -// AzureBackupRecoveryTimeBasedRestoreRequest azureBackup RecoveryPointTime Based Restore Request -type AzureBackupRecoveryTimeBasedRestoreRequest struct { - // RecoveryPointTime - The recovery time in ISO 8601 format example - 2020-08-14T17:30:00.0000000Z. - RecoveryPointTime *string `json:"recoveryPointTime,omitempty"` - // RestoreTargetInfo - Gets or sets the restore target information. - RestoreTargetInfo BasicRestoreTargetInfoBase `json:"restoreTargetInfo,omitempty"` - // SourceDataStoreType - Gets or sets the type of the source data store. Possible values include: 'SourceDataStoreTypeArchiveStore', 'SourceDataStoreTypeSnapshotStore', 'SourceDataStoreTypeVaultStore' - SourceDataStoreType SourceDataStoreType `json:"sourceDataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest' - ObjectType ObjectTypeBasicAzureBackupRestoreRequest `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) MarshalJSON() ([]byte, error) { - abrtbrr.ObjectType = ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest - objectMap := make(map[string]interface{}) - if abrtbrr.RecoveryPointTime != nil { - objectMap["recoveryPointTime"] = abrtbrr.RecoveryPointTime - } - objectMap["restoreTargetInfo"] = abrtbrr.RestoreTargetInfo - if abrtbrr.SourceDataStoreType != "" { - objectMap["sourceDataStoreType"] = abrtbrr.SourceDataStoreType - } - if abrtbrr.ObjectType != "" { - objectMap["objectType"] = abrtbrr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsBasicAzureBackupRecoveryPointBasedRestoreRequest() (BasicAzureBackupRecoveryPointBasedRestoreRequest, bool) { - return nil, false -} - -// AsAzureBackupRestoreWithRehydrationRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) { - return nil, false -} - -// AsAzureBackupRecoveryTimeBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsAzureBackupRecoveryTimeBasedRestoreRequest() (*AzureBackupRecoveryTimeBasedRestoreRequest, bool) { - return &abrtbrr, true -} - -// AsAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsAzureBackupRestoreRequest() (*AzureBackupRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRecoveryTimeBasedRestoreRequest. -func (abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest) AsBasicAzureBackupRestoreRequest() (BasicAzureBackupRestoreRequest, bool) { - return &abrtbrr, true -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRecoveryTimeBasedRestoreRequest struct. -func (abrtbrr *AzureBackupRecoveryTimeBasedRestoreRequest) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "recoveryPointTime": - if v != nil { - var recoveryPointTime string - err = json.Unmarshal(*v, &recoveryPointTime) - if err != nil { - return err - } - abrtbrr.RecoveryPointTime = &recoveryPointTime - } - case "restoreTargetInfo": - if v != nil { - restoreTargetInfo, err := unmarshalBasicRestoreTargetInfoBase(*v) - if err != nil { - return err - } - abrtbrr.RestoreTargetInfo = restoreTargetInfo - } - case "sourceDataStoreType": - if v != nil { - var sourceDataStoreType SourceDataStoreType - err = json.Unmarshal(*v, &sourceDataStoreType) - if err != nil { - return err - } - abrtbrr.SourceDataStoreType = sourceDataStoreType - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicAzureBackupRestoreRequest - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - abrtbrr.ObjectType = objectType - } - } - } - - return nil -} - -// AzureBackupRehydrationRequest azure Backup Rehydrate Request -type AzureBackupRehydrationRequest struct { - // RecoveryPointID - Id of the recovery point to be recovered - RecoveryPointID *string `json:"recoveryPointId,omitempty"` - // RehydrationPriority - Priority to be used for rehydration. Values High or Standard. Possible values include: 'RehydrationPriorityInvalid', 'RehydrationPriorityHigh', 'RehydrationPriorityStandard' - RehydrationPriority RehydrationPriority `json:"rehydrationPriority,omitempty"` - // RehydrationRetentionDuration - Retention duration in ISO 8601 format i.e P10D . - RehydrationRetentionDuration *string `json:"rehydrationRetentionDuration,omitempty"` -} - -// BasicAzureBackupRestoreRequest azure backup restore request -type BasicAzureBackupRestoreRequest interface { - AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) - AsBasicAzureBackupRecoveryPointBasedRestoreRequest() (BasicAzureBackupRecoveryPointBasedRestoreRequest, bool) - AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) - AsAzureBackupRecoveryTimeBasedRestoreRequest() (*AzureBackupRecoveryTimeBasedRestoreRequest, bool) - AsAzureBackupRestoreRequest() (*AzureBackupRestoreRequest, bool) -} - -// AzureBackupRestoreRequest azure backup restore request -type AzureBackupRestoreRequest struct { - // RestoreTargetInfo - Gets or sets the restore target information. - RestoreTargetInfo BasicRestoreTargetInfoBase `json:"restoreTargetInfo,omitempty"` - // SourceDataStoreType - Gets or sets the type of the source data store. Possible values include: 'SourceDataStoreTypeArchiveStore', 'SourceDataStoreTypeSnapshotStore', 'SourceDataStoreTypeVaultStore' - SourceDataStoreType SourceDataStoreType `json:"sourceDataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest' - ObjectType ObjectTypeBasicAzureBackupRestoreRequest `json:"objectType,omitempty"` -} - -func unmarshalBasicAzureBackupRestoreRequest(body []byte) (BasicAzureBackupRestoreRequest, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest): - var abrpbrr AzureBackupRecoveryPointBasedRestoreRequest - err := json.Unmarshal(body, &abrpbrr) - return abrpbrr, err - case string(ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest): - var abrwrr AzureBackupRestoreWithRehydrationRequest - err := json.Unmarshal(body, &abrwrr) - return abrwrr, err - case string(ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest): - var abrtbrr AzureBackupRecoveryTimeBasedRestoreRequest - err := json.Unmarshal(body, &abrtbrr) - return abrtbrr, err - default: - var abrr AzureBackupRestoreRequest - err := json.Unmarshal(body, &abrr) - return abrr, err - } -} - -func unmarshalBasicAzureBackupRestoreRequestArray(body []byte) ([]BasicAzureBackupRestoreRequest, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - abrrArray := make([]BasicAzureBackupRestoreRequest, len(rawMessages)) - - for index, rawMessage := range rawMessages { - abrr, err := unmarshalBasicAzureBackupRestoreRequest(*rawMessage) - if err != nil { - return nil, err - } - abrrArray[index] = abrr - } - return abrrArray, nil -} - -// MarshalJSON is the custom marshaler for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) MarshalJSON() ([]byte, error) { - abrr.ObjectType = ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest - objectMap := make(map[string]interface{}) - objectMap["restoreTargetInfo"] = abrr.RestoreTargetInfo - if abrr.SourceDataStoreType != "" { - objectMap["sourceDataStoreType"] = abrr.SourceDataStoreType - } - if abrr.ObjectType != "" { - objectMap["objectType"] = abrr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsBasicAzureBackupRecoveryPointBasedRestoreRequest() (BasicAzureBackupRecoveryPointBasedRestoreRequest, bool) { - return nil, false -} - -// AsAzureBackupRestoreWithRehydrationRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) { - return nil, false -} - -// AsAzureBackupRecoveryTimeBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsAzureBackupRecoveryTimeBasedRestoreRequest() (*AzureBackupRecoveryTimeBasedRestoreRequest, bool) { - return nil, false -} - -// AsAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsAzureBackupRestoreRequest() (*AzureBackupRestoreRequest, bool) { - return &abrr, true -} - -// AsBasicAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreRequest. -func (abrr AzureBackupRestoreRequest) AsBasicAzureBackupRestoreRequest() (BasicAzureBackupRestoreRequest, bool) { - return &abrr, true -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRestoreRequest struct. -func (abrr *AzureBackupRestoreRequest) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "restoreTargetInfo": - if v != nil { - restoreTargetInfo, err := unmarshalBasicRestoreTargetInfoBase(*v) - if err != nil { - return err - } - abrr.RestoreTargetInfo = restoreTargetInfo - } - case "sourceDataStoreType": - if v != nil { - var sourceDataStoreType SourceDataStoreType - err = json.Unmarshal(*v, &sourceDataStoreType) - if err != nil { - return err - } - abrr.SourceDataStoreType = sourceDataStoreType - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicAzureBackupRestoreRequest - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - abrr.ObjectType = objectType - } - } - } - - return nil -} - -// AzureBackupRestoreWithRehydrationRequest azureBackup Restore with Rehydration Request -type AzureBackupRestoreWithRehydrationRequest struct { - // RehydrationPriority - Priority to be used for rehydration. Values High or Standard. Possible values include: 'RehydrationPriorityInvalid', 'RehydrationPriorityHigh', 'RehydrationPriorityStandard' - RehydrationPriority RehydrationPriority `json:"rehydrationPriority,omitempty"` - // RehydrationRetentionDuration - Retention duration in ISO 8601 format i.e P10D . - RehydrationRetentionDuration *string `json:"rehydrationRetentionDuration,omitempty"` - RecoveryPointID *string `json:"recoveryPointId,omitempty"` - // RestoreTargetInfo - Gets or sets the restore target information. - RestoreTargetInfo BasicRestoreTargetInfoBase `json:"restoreTargetInfo,omitempty"` - // SourceDataStoreType - Gets or sets the type of the source data store. Possible values include: 'SourceDataStoreTypeArchiveStore', 'SourceDataStoreTypeSnapshotStore', 'SourceDataStoreTypeVaultStore' - SourceDataStoreType SourceDataStoreType `json:"sourceDataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryPointBasedRestoreRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest', 'ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRecoveryTimeBasedRestoreRequest' - ObjectType ObjectTypeBasicAzureBackupRestoreRequest `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) MarshalJSON() ([]byte, error) { - abrwrr.ObjectType = ObjectTypeBasicAzureBackupRestoreRequestObjectTypeAzureBackupRestoreWithRehydrationRequest - objectMap := make(map[string]interface{}) - if abrwrr.RehydrationPriority != "" { - objectMap["rehydrationPriority"] = abrwrr.RehydrationPriority - } - if abrwrr.RehydrationRetentionDuration != nil { - objectMap["rehydrationRetentionDuration"] = abrwrr.RehydrationRetentionDuration - } - if abrwrr.RecoveryPointID != nil { - objectMap["recoveryPointId"] = abrwrr.RecoveryPointID - } - objectMap["restoreTargetInfo"] = abrwrr.RestoreTargetInfo - if abrwrr.SourceDataStoreType != "" { - objectMap["sourceDataStoreType"] = abrwrr.SourceDataStoreType - } - if abrwrr.ObjectType != "" { - objectMap["objectType"] = abrwrr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsAzureBackupRecoveryPointBasedRestoreRequest() (*AzureBackupRecoveryPointBasedRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRecoveryPointBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsBasicAzureBackupRecoveryPointBasedRestoreRequest() (BasicAzureBackupRecoveryPointBasedRestoreRequest, bool) { - return &abrwrr, true -} - -// AsAzureBackupRestoreWithRehydrationRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsAzureBackupRestoreWithRehydrationRequest() (*AzureBackupRestoreWithRehydrationRequest, bool) { - return &abrwrr, true -} - -// AsAzureBackupRecoveryTimeBasedRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsAzureBackupRecoveryTimeBasedRestoreRequest() (*AzureBackupRecoveryTimeBasedRestoreRequest, bool) { - return nil, false -} - -// AsAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsAzureBackupRestoreRequest() (*AzureBackupRestoreRequest, bool) { - return nil, false -} - -// AsBasicAzureBackupRestoreRequest is the BasicAzureBackupRestoreRequest implementation for AzureBackupRestoreWithRehydrationRequest. -func (abrwrr AzureBackupRestoreWithRehydrationRequest) AsBasicAzureBackupRestoreRequest() (BasicAzureBackupRestoreRequest, bool) { - return &abrwrr, true -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRestoreWithRehydrationRequest struct. -func (abrwrr *AzureBackupRestoreWithRehydrationRequest) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "rehydrationPriority": - if v != nil { - var rehydrationPriority RehydrationPriority - err = json.Unmarshal(*v, &rehydrationPriority) - if err != nil { - return err - } - abrwrr.RehydrationPriority = rehydrationPriority - } - case "rehydrationRetentionDuration": - if v != nil { - var rehydrationRetentionDuration string - err = json.Unmarshal(*v, &rehydrationRetentionDuration) - if err != nil { - return err - } - abrwrr.RehydrationRetentionDuration = &rehydrationRetentionDuration - } - case "recoveryPointId": - if v != nil { - var recoveryPointID string - err = json.Unmarshal(*v, &recoveryPointID) - if err != nil { - return err - } - abrwrr.RecoveryPointID = &recoveryPointID - } - case "restoreTargetInfo": - if v != nil { - restoreTargetInfo, err := unmarshalBasicRestoreTargetInfoBase(*v) - if err != nil { - return err - } - abrwrr.RestoreTargetInfo = restoreTargetInfo - } - case "sourceDataStoreType": - if v != nil { - var sourceDataStoreType SourceDataStoreType - err = json.Unmarshal(*v, &sourceDataStoreType) - if err != nil { - return err - } - abrwrr.SourceDataStoreType = sourceDataStoreType - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicAzureBackupRestoreRequest - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - abrwrr.ObjectType = objectType - } - } - } - - return nil -} - -// AzureBackupRule azure backup rule -type AzureBackupRule struct { - BackupParameters BasicBackupParameters `json:"backupParameters,omitempty"` - DataStore *DataStoreInfoBase `json:"dataStore,omitempty"` - Trigger BasicTriggerContext `json:"trigger,omitempty"` - Name *string `json:"name,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule' - ObjectType ObjectTypeBasicBasePolicyRule `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureBackupRule. -func (abr AzureBackupRule) MarshalJSON() ([]byte, error) { - abr.ObjectType = ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule - objectMap := make(map[string]interface{}) - objectMap["backupParameters"] = abr.BackupParameters - if abr.DataStore != nil { - objectMap["dataStore"] = abr.DataStore - } - objectMap["trigger"] = abr.Trigger - if abr.Name != nil { - objectMap["name"] = abr.Name - } - if abr.ObjectType != "" { - objectMap["objectType"] = abr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRule is the BasicBasePolicyRule implementation for AzureBackupRule. -func (abr AzureBackupRule) AsAzureBackupRule() (*AzureBackupRule, bool) { - return &abr, true -} - -// AsAzureRetentionRule is the BasicBasePolicyRule implementation for AzureBackupRule. -func (abr AzureBackupRule) AsAzureRetentionRule() (*AzureRetentionRule, bool) { - return nil, false -} - -// AsBasePolicyRule is the BasicBasePolicyRule implementation for AzureBackupRule. -func (abr AzureBackupRule) AsBasePolicyRule() (*BasePolicyRule, bool) { - return nil, false -} - -// AsBasicBasePolicyRule is the BasicBasePolicyRule implementation for AzureBackupRule. -func (abr AzureBackupRule) AsBasicBasePolicyRule() (BasicBasePolicyRule, bool) { - return &abr, true -} - -// UnmarshalJSON is the custom unmarshaler for AzureBackupRule struct. -func (abr *AzureBackupRule) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "backupParameters": - if v != nil { - backupParameters, err := unmarshalBasicBackupParameters(*v) - if err != nil { - return err - } - abr.BackupParameters = backupParameters - } - case "dataStore": - if v != nil { - var dataStore DataStoreInfoBase - err = json.Unmarshal(*v, &dataStore) - if err != nil { - return err - } - abr.DataStore = &dataStore - } - case "trigger": - if v != nil { - trigger, err := unmarshalBasicTriggerContext(*v) - if err != nil { - return err - } - abr.Trigger = trigger - } - case "name": - if v != nil { - var name string - err = json.Unmarshal(*v, &name) - if err != nil { - return err - } - abr.Name = &name - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicBasePolicyRule - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - abr.ObjectType = objectType - } - } - } - - return nil -} - -// AzureOperationalStoreParameters parameters for Operational-Tier DataStore -type AzureOperationalStoreParameters struct { - // ResourceGroupID - Gets or sets the Snapshot Resource Group Uri. - ResourceGroupID *string `json:"resourceGroupId,omitempty"` - // DataStoreType - type of datastore; Operational/Vault/Archive. Possible values include: 'DataStoreTypesOperationalStore', 'DataStoreTypesVaultStore', 'DataStoreTypesArchiveStore' - DataStoreType DataStoreTypes `json:"dataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters', 'ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters' - ObjectType ObjectTypeBasicDataStoreParameters `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureOperationalStoreParameters. -func (aosp AzureOperationalStoreParameters) MarshalJSON() ([]byte, error) { - aosp.ObjectType = ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters - objectMap := make(map[string]interface{}) - if aosp.ResourceGroupID != nil { - objectMap["resourceGroupId"] = aosp.ResourceGroupID - } - if aosp.DataStoreType != "" { - objectMap["dataStoreType"] = aosp.DataStoreType - } - if aosp.ObjectType != "" { - objectMap["objectType"] = aosp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureOperationalStoreParameters is the BasicDataStoreParameters implementation for AzureOperationalStoreParameters. -func (aosp AzureOperationalStoreParameters) AsAzureOperationalStoreParameters() (*AzureOperationalStoreParameters, bool) { - return &aosp, true -} - -// AsDataStoreParameters is the BasicDataStoreParameters implementation for AzureOperationalStoreParameters. -func (aosp AzureOperationalStoreParameters) AsDataStoreParameters() (*DataStoreParameters, bool) { - return nil, false -} - -// AsBasicDataStoreParameters is the BasicDataStoreParameters implementation for AzureOperationalStoreParameters. -func (aosp AzureOperationalStoreParameters) AsBasicDataStoreParameters() (BasicDataStoreParameters, bool) { - return &aosp, true -} - -// AzureRetentionRule azure retention rule -type AzureRetentionRule struct { - IsDefault *bool `json:"isDefault,omitempty"` - Lifecycles *[]SourceLifeCycle `json:"lifecycles,omitempty"` - Name *string `json:"name,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule' - ObjectType ObjectTypeBasicBasePolicyRule `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for AzureRetentionRule. -func (arr AzureRetentionRule) MarshalJSON() ([]byte, error) { - arr.ObjectType = ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule - objectMap := make(map[string]interface{}) - if arr.IsDefault != nil { - objectMap["isDefault"] = arr.IsDefault - } - if arr.Lifecycles != nil { - objectMap["lifecycles"] = arr.Lifecycles - } - if arr.Name != nil { - objectMap["name"] = arr.Name - } - if arr.ObjectType != "" { - objectMap["objectType"] = arr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRule is the BasicBasePolicyRule implementation for AzureRetentionRule. -func (arr AzureRetentionRule) AsAzureBackupRule() (*AzureBackupRule, bool) { - return nil, false -} - -// AsAzureRetentionRule is the BasicBasePolicyRule implementation for AzureRetentionRule. -func (arr AzureRetentionRule) AsAzureRetentionRule() (*AzureRetentionRule, bool) { - return &arr, true -} - -// AsBasePolicyRule is the BasicBasePolicyRule implementation for AzureRetentionRule. -func (arr AzureRetentionRule) AsBasePolicyRule() (*BasePolicyRule, bool) { - return nil, false -} - -// AsBasicBasePolicyRule is the BasicBasePolicyRule implementation for AzureRetentionRule. -func (arr AzureRetentionRule) AsBasicBasePolicyRule() (BasicBasePolicyRule, bool) { - return &arr, true -} - -// BasicBackupCriteria backupCriteria base class -type BasicBackupCriteria interface { - AsScheduleBasedBackupCriteria() (*ScheduleBasedBackupCriteria, bool) - AsBackupCriteria() (*BackupCriteria, bool) -} - -// BackupCriteria backupCriteria base class -type BackupCriteria struct { - // ObjectType - Possible values include: 'ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria', 'ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria' - ObjectType ObjectTypeBasicBackupCriteria `json:"objectType,omitempty"` -} - -func unmarshalBasicBackupCriteria(body []byte) (BasicBackupCriteria, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria): - var sbbc ScheduleBasedBackupCriteria - err := json.Unmarshal(body, &sbbc) - return sbbc, err - default: - var bc BackupCriteria - err := json.Unmarshal(body, &bc) - return bc, err - } -} - -func unmarshalBasicBackupCriteriaArray(body []byte) ([]BasicBackupCriteria, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - bcArray := make([]BasicBackupCriteria, len(rawMessages)) - - for index, rawMessage := range rawMessages { - bc, err := unmarshalBasicBackupCriteria(*rawMessage) - if err != nil { - return nil, err - } - bcArray[index] = bc - } - return bcArray, nil -} - -// MarshalJSON is the custom marshaler for BackupCriteria. -func (bc BackupCriteria) MarshalJSON() ([]byte, error) { - bc.ObjectType = ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria - objectMap := make(map[string]interface{}) - if bc.ObjectType != "" { - objectMap["objectType"] = bc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsScheduleBasedBackupCriteria is the BasicBackupCriteria implementation for BackupCriteria. -func (bc BackupCriteria) AsScheduleBasedBackupCriteria() (*ScheduleBasedBackupCriteria, bool) { - return nil, false -} - -// AsBackupCriteria is the BasicBackupCriteria implementation for BackupCriteria. -func (bc BackupCriteria) AsBackupCriteria() (*BackupCriteria, bool) { - return &bc, true -} - -// AsBasicBackupCriteria is the BasicBackupCriteria implementation for BackupCriteria. -func (bc BackupCriteria) AsBasicBackupCriteria() (BasicBackupCriteria, bool) { - return &bc, true -} - -// BackupInstance backup Instance -type BackupInstance struct { - // FriendlyName - Gets or sets the Backup Instance friendly name. - FriendlyName *string `json:"friendlyName,omitempty"` - // DataSourceInfo - Gets or sets the data source information. - DataSourceInfo *Datasource `json:"dataSourceInfo,omitempty"` - // DataSourceSetInfo - Gets or sets the data source set information. - DataSourceSetInfo *DatasourceSet `json:"dataSourceSetInfo,omitempty"` - // PolicyInfo - Gets or sets the policy information. - PolicyInfo *PolicyInfo `json:"policyInfo,omitempty"` - // ProtectionStatus - READ-ONLY; Specifies the protection status of the resource - ProtectionStatus *ProtectionStatusDetails `json:"protectionStatus,omitempty"` - // CurrentProtectionState - READ-ONLY; Specifies the current protection state of the resource. Possible values include: 'CurrentProtectionStateInvalid', 'CurrentProtectionStateNotProtected', 'CurrentProtectionStateConfiguringProtection', 'CurrentProtectionStateProtectionConfigured', 'CurrentProtectionStateBackupSchedulesSuspended', 'CurrentProtectionStateRetentionSchedulesSuspended', 'CurrentProtectionStateProtectionStopped', 'CurrentProtectionStateProtectionError', 'CurrentProtectionStateConfiguringProtectionFailed', 'CurrentProtectionStateSoftDeleting', 'CurrentProtectionStateSoftDeleted', 'CurrentProtectionStateUpdatingProtection' - CurrentProtectionState CurrentProtectionState `json:"currentProtectionState,omitempty"` - // ProtectionErrorDetails - READ-ONLY; Specifies the protection error of the resource - ProtectionErrorDetails *UserFacingError `json:"protectionErrorDetails,omitempty"` - // ProvisioningState - READ-ONLY; Specifies the provisioning state of the resource i.e. provisioning/updating/Succeeded/Failed - ProvisioningState *string `json:"provisioningState,omitempty"` - // DatasourceAuthCredentials - Credentials to use to authenticate with data source provider. - DatasourceAuthCredentials BasicAuthCredentials `json:"datasourceAuthCredentials,omitempty"` - ObjectType *string `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for BackupInstance. -func (bi BackupInstance) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if bi.FriendlyName != nil { - objectMap["friendlyName"] = bi.FriendlyName - } - if bi.DataSourceInfo != nil { - objectMap["dataSourceInfo"] = bi.DataSourceInfo - } - if bi.DataSourceSetInfo != nil { - objectMap["dataSourceSetInfo"] = bi.DataSourceSetInfo - } - if bi.PolicyInfo != nil { - objectMap["policyInfo"] = bi.PolicyInfo - } - objectMap["datasourceAuthCredentials"] = bi.DatasourceAuthCredentials - if bi.ObjectType != nil { - objectMap["objectType"] = bi.ObjectType - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for BackupInstance struct. -func (bi *BackupInstance) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "friendlyName": - if v != nil { - var friendlyName string - err = json.Unmarshal(*v, &friendlyName) - if err != nil { - return err - } - bi.FriendlyName = &friendlyName - } - case "dataSourceInfo": - if v != nil { - var dataSourceInfo Datasource - err = json.Unmarshal(*v, &dataSourceInfo) - if err != nil { - return err - } - bi.DataSourceInfo = &dataSourceInfo - } - case "dataSourceSetInfo": - if v != nil { - var dataSourceSetInfo DatasourceSet - err = json.Unmarshal(*v, &dataSourceSetInfo) - if err != nil { - return err - } - bi.DataSourceSetInfo = &dataSourceSetInfo - } - case "policyInfo": - if v != nil { - var policyInfo PolicyInfo - err = json.Unmarshal(*v, &policyInfo) - if err != nil { - return err - } - bi.PolicyInfo = &policyInfo - } - case "protectionStatus": - if v != nil { - var protectionStatus ProtectionStatusDetails - err = json.Unmarshal(*v, &protectionStatus) - if err != nil { - return err - } - bi.ProtectionStatus = &protectionStatus - } - case "currentProtectionState": - if v != nil { - var currentProtectionState CurrentProtectionState - err = json.Unmarshal(*v, ¤tProtectionState) - if err != nil { - return err - } - bi.CurrentProtectionState = currentProtectionState - } - case "protectionErrorDetails": - if v != nil { - var protectionErrorDetails UserFacingError - err = json.Unmarshal(*v, &protectionErrorDetails) - if err != nil { - return err - } - bi.ProtectionErrorDetails = &protectionErrorDetails - } - case "provisioningState": - if v != nil { - var provisioningState string - err = json.Unmarshal(*v, &provisioningState) - if err != nil { - return err - } - bi.ProvisioningState = &provisioningState - } - case "datasourceAuthCredentials": - if v != nil { - datasourceAuthCredentials, err := unmarshalBasicAuthCredentials(*v) - if err != nil { - return err - } - bi.DatasourceAuthCredentials = datasourceAuthCredentials - } - case "objectType": - if v != nil { - var objectType string - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - bi.ObjectType = &objectType - } - } - } - - return nil -} - -// BackupInstanceResource backupInstance Resource -type BackupInstanceResource struct { - autorest.Response `json:"-"` - // Properties - BackupInstanceResource properties - Properties *BackupInstance `json:"properties,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for BackupInstanceResource. -func (bir BackupInstanceResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if bir.Properties != nil { - objectMap["properties"] = bir.Properties - } - if bir.SystemData != nil { - objectMap["systemData"] = bir.SystemData - } - return json.Marshal(objectMap) -} - -// BackupInstanceResourceList backupInstance Resource list response -type BackupInstanceResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]BackupInstanceResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// BackupInstanceResourceListIterator provides access to a complete listing of BackupInstanceResource -// values. -type BackupInstanceResourceListIterator struct { - i int - page BackupInstanceResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *BackupInstanceResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstanceResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *BackupInstanceResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter BackupInstanceResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter BackupInstanceResourceListIterator) Response() BackupInstanceResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter BackupInstanceResourceListIterator) Value() BackupInstanceResource { - if !iter.page.NotDone() { - return BackupInstanceResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the BackupInstanceResourceListIterator type. -func NewBackupInstanceResourceListIterator(page BackupInstanceResourceListPage) BackupInstanceResourceListIterator { - return BackupInstanceResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (birl BackupInstanceResourceList) IsEmpty() bool { - return birl.Value == nil || len(*birl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (birl BackupInstanceResourceList) hasNextLink() bool { - return birl.NextLink != nil && len(*birl.NextLink) != 0 -} - -// backupInstanceResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (birl BackupInstanceResourceList) backupInstanceResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !birl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(birl.NextLink))) -} - -// BackupInstanceResourceListPage contains a page of BackupInstanceResource values. -type BackupInstanceResourceListPage struct { - fn func(context.Context, BackupInstanceResourceList) (BackupInstanceResourceList, error) - birl BackupInstanceResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *BackupInstanceResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupInstanceResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.birl) - if err != nil { - return err - } - page.birl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *BackupInstanceResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page BackupInstanceResourceListPage) NotDone() bool { - return !page.birl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page BackupInstanceResourceListPage) Response() BackupInstanceResourceList { - return page.birl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page BackupInstanceResourceListPage) Values() []BackupInstanceResource { - if page.birl.IsEmpty() { - return nil - } - return *page.birl.Value -} - -// Creates a new instance of the BackupInstanceResourceListPage type. -func NewBackupInstanceResourceListPage(cur BackupInstanceResourceList, getNextPage func(context.Context, BackupInstanceResourceList) (BackupInstanceResourceList, error)) BackupInstanceResourceListPage { - return BackupInstanceResourceListPage{ - fn: getNextPage, - birl: cur, - } -} - -// BackupInstancesAdhocBackupFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesAdhocBackupFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (OperationJobExtendedInfo, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesAdhocBackupFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesAdhocBackupFuture.Result. -func (future *BackupInstancesAdhocBackupFuture) result(client BackupInstancesClient) (ojei OperationJobExtendedInfo, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesAdhocBackupFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ojei.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesAdhocBackupFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if ojei.Response.Response, err = future.GetResult(sender); err == nil && ojei.Response.Response.StatusCode != http.StatusNoContent { - ojei, err = client.AdhocBackupResponder(ojei.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesAdhocBackupFuture", "Result", ojei.Response.Response, "Failure responding to request") - } - } - return -} - -// BackupInstancesCreateOrUpdateFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesCreateOrUpdateFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (BackupInstanceResource, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesCreateOrUpdateFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesCreateOrUpdateFuture.Result. -func (future *BackupInstancesCreateOrUpdateFuture) result(client BackupInstancesClient) (bir BackupInstanceResource, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesCreateOrUpdateFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - bir.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesCreateOrUpdateFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if bir.Response.Response, err = future.GetResult(sender); err == nil && bir.Response.Response.StatusCode != http.StatusNoContent { - bir, err = client.CreateOrUpdateResponder(bir.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesCreateOrUpdateFuture", "Result", bir.Response.Response, "Failure responding to request") - } - } - return -} - -// BackupInstancesDeleteFuture an abstraction for monitoring and retrieving the results of a long-running -// operation. -type BackupInstancesDeleteFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (autorest.Response, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesDeleteFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesDeleteFuture.Result. -func (future *BackupInstancesDeleteFuture) result(client BackupInstancesClient) (ar autorest.Response, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesDeleteFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ar.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesDeleteFuture") - return - } - ar.Response = future.Response() - return -} - -// BackupInstancesTriggerRehydrateFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesTriggerRehydrateFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (autorest.Response, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesTriggerRehydrateFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesTriggerRehydrateFuture.Result. -func (future *BackupInstancesTriggerRehydrateFuture) result(client BackupInstancesClient) (ar autorest.Response, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesTriggerRehydrateFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ar.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesTriggerRehydrateFuture") - return - } - ar.Response = future.Response() - return -} - -// BackupInstancesTriggerRestoreFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesTriggerRestoreFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (OperationJobExtendedInfo, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesTriggerRestoreFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesTriggerRestoreFuture.Result. -func (future *BackupInstancesTriggerRestoreFuture) result(client BackupInstancesClient) (ojei OperationJobExtendedInfo, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesTriggerRestoreFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ojei.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesTriggerRestoreFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if ojei.Response.Response, err = future.GetResult(sender); err == nil && ojei.Response.Response.StatusCode != http.StatusNoContent { - ojei, err = client.TriggerRestoreResponder(ojei.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesTriggerRestoreFuture", "Result", ojei.Response.Response, "Failure responding to request") - } - } - return -} - -// BackupInstancesValidateForBackupFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesValidateForBackupFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (OperationJobExtendedInfo, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesValidateForBackupFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesValidateForBackupFuture.Result. -func (future *BackupInstancesValidateForBackupFuture) result(client BackupInstancesClient) (ojei OperationJobExtendedInfo, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesValidateForBackupFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ojei.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesValidateForBackupFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if ojei.Response.Response, err = future.GetResult(sender); err == nil && ojei.Response.Response.StatusCode != http.StatusNoContent { - ojei, err = client.ValidateForBackupResponder(ojei.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesValidateForBackupFuture", "Result", ojei.Response.Response, "Failure responding to request") - } - } - return -} - -// BackupInstancesValidateForRestoreFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupInstancesValidateForRestoreFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupInstancesClient) (OperationJobExtendedInfo, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupInstancesValidateForRestoreFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupInstancesValidateForRestoreFuture.Result. -func (future *BackupInstancesValidateForRestoreFuture) result(client BackupInstancesClient) (ojei OperationJobExtendedInfo, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesValidateForRestoreFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ojei.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupInstancesValidateForRestoreFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if ojei.Response.Response, err = future.GetResult(sender); err == nil && ojei.Response.Response.StatusCode != http.StatusNoContent { - ojei, err = client.ValidateForRestoreResponder(ojei.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupInstancesValidateForRestoreFuture", "Result", ojei.Response.Response, "Failure responding to request") - } - } - return -} - -// BasicBackupParameters backupParameters base -type BasicBackupParameters interface { - AsAzureBackupParams() (*AzureBackupParams, bool) - AsBackupParameters() (*BackupParameters, bool) -} - -// BackupParameters backupParameters base -type BackupParameters struct { - // ObjectType - Possible values include: 'ObjectTypeBasicBackupParametersObjectTypeBackupParameters', 'ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams' - ObjectType ObjectTypeBasicBackupParameters `json:"objectType,omitempty"` -} - -func unmarshalBasicBackupParameters(body []byte) (BasicBackupParameters, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicBackupParametersObjectTypeAzureBackupParams): - var abp AzureBackupParams - err := json.Unmarshal(body, &abp) - return abp, err - default: - var bp BackupParameters - err := json.Unmarshal(body, &bp) - return bp, err - } -} - -func unmarshalBasicBackupParametersArray(body []byte) ([]BasicBackupParameters, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - bpArray := make([]BasicBackupParameters, len(rawMessages)) - - for index, rawMessage := range rawMessages { - bp, err := unmarshalBasicBackupParameters(*rawMessage) - if err != nil { - return nil, err - } - bpArray[index] = bp - } - return bpArray, nil -} - -// MarshalJSON is the custom marshaler for BackupParameters. -func (bp BackupParameters) MarshalJSON() ([]byte, error) { - bp.ObjectType = ObjectTypeBasicBackupParametersObjectTypeBackupParameters - objectMap := make(map[string]interface{}) - if bp.ObjectType != "" { - objectMap["objectType"] = bp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupParams is the BasicBackupParameters implementation for BackupParameters. -func (bp BackupParameters) AsAzureBackupParams() (*AzureBackupParams, bool) { - return nil, false -} - -// AsBackupParameters is the BasicBackupParameters implementation for BackupParameters. -func (bp BackupParameters) AsBackupParameters() (*BackupParameters, bool) { - return &bp, true -} - -// AsBasicBackupParameters is the BasicBackupParameters implementation for BackupParameters. -func (bp BackupParameters) AsBasicBackupParameters() (BasicBackupParameters, bool) { - return &bp, true -} - -// BackupPolicy rule based backup policy -type BackupPolicy struct { - // PolicyRules - Policy rule dictionary that contains rules for each backuptype i.e Full/Incremental/Logs etc - PolicyRules *[]BasicBasePolicyRule `json:"policyRules,omitempty"` - // DatasourceTypes - Type of datasource for the backup management - DatasourceTypes *[]string `json:"datasourceTypes,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy', 'ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy' - ObjectType ObjectTypeBasicBaseBackupPolicy `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for BackupPolicy. -func (bp BackupPolicy) MarshalJSON() ([]byte, error) { - bp.ObjectType = ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy - objectMap := make(map[string]interface{}) - if bp.PolicyRules != nil { - objectMap["policyRules"] = bp.PolicyRules - } - if bp.DatasourceTypes != nil { - objectMap["datasourceTypes"] = bp.DatasourceTypes - } - if bp.ObjectType != "" { - objectMap["objectType"] = bp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsBackupPolicy is the BasicBaseBackupPolicy implementation for BackupPolicy. -func (bp BackupPolicy) AsBackupPolicy() (*BackupPolicy, bool) { - return &bp, true -} - -// AsBaseBackupPolicy is the BasicBaseBackupPolicy implementation for BackupPolicy. -func (bp BackupPolicy) AsBaseBackupPolicy() (*BaseBackupPolicy, bool) { - return nil, false -} - -// AsBasicBaseBackupPolicy is the BasicBaseBackupPolicy implementation for BackupPolicy. -func (bp BackupPolicy) AsBasicBaseBackupPolicy() (BasicBaseBackupPolicy, bool) { - return &bp, true -} - -// UnmarshalJSON is the custom unmarshaler for BackupPolicy struct. -func (bp *BackupPolicy) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "policyRules": - if v != nil { - policyRules, err := unmarshalBasicBasePolicyRuleArray(*v) - if err != nil { - return err - } - bp.PolicyRules = &policyRules - } - case "datasourceTypes": - if v != nil { - var datasourceTypes []string - err = json.Unmarshal(*v, &datasourceTypes) - if err != nil { - return err - } - bp.DatasourceTypes = &datasourceTypes - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicBaseBackupPolicy - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - bp.ObjectType = objectType - } - } - } - - return nil -} - -// BackupSchedule schedule for backup -type BackupSchedule struct { - // RepeatingTimeIntervals - ISO 8601 repeating time interval format - RepeatingTimeIntervals *[]string `json:"repeatingTimeIntervals,omitempty"` - // TimeZone - Time zone for a schedule. Example: Pacific Standard Time - TimeZone *string `json:"timeZone,omitempty"` -} - -// BackupVault backup Vault -type BackupVault struct { - // ProvisioningState - READ-ONLY; Provisioning state of the BackupVault resource. Possible values include: 'ProvisioningStateFailed', 'ProvisioningStateProvisioning', 'ProvisioningStateSucceeded', 'ProvisioningStateUnknown', 'ProvisioningStateUpdating' - ProvisioningState ProvisioningState `json:"provisioningState,omitempty"` - // ResourceMoveState - READ-ONLY; Resource move state for backup vault. Possible values include: 'ResourceMoveStateUnknown', 'ResourceMoveStateInProgress', 'ResourceMoveStatePrepareFailed', 'ResourceMoveStateCommitFailed', 'ResourceMoveStateFailed', 'ResourceMoveStatePrepareTimedout', 'ResourceMoveStateCommitTimedout', 'ResourceMoveStateCriticalFailure', 'ResourceMoveStatePartialSuccess', 'ResourceMoveStateMoveSucceeded' - ResourceMoveState ResourceMoveState `json:"resourceMoveState,omitempty"` - // ResourceMoveDetails - READ-ONLY; Resource move details for backup vault - ResourceMoveDetails *ResourceMoveDetails `json:"resourceMoveDetails,omitempty"` - // StorageSettings - Storage Settings - StorageSettings *[]StorageSetting `json:"storageSettings,omitempty"` -} - -// MarshalJSON is the custom marshaler for BackupVault. -func (bv BackupVault) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if bv.StorageSettings != nil { - objectMap["storageSettings"] = bv.StorageSettings - } - return json.Marshal(objectMap) -} - -// BackupVaultResource backup Vault Resource -type BackupVaultResource struct { - autorest.Response `json:"-"` - // Properties - BackupVaultResource properties - Properties *BackupVault `json:"properties,omitempty"` - // ETag - Optional ETag. - ETag *string `json:"eTag,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Identity - Input Managed Identity Details - Identity *DppIdentityDetails `json:"identity,omitempty"` - // Location - Resource location. - Location *string `json:"location,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Tags - Resource tags. - Tags map[string]*string `json:"tags"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for BackupVaultResource. -func (bvr BackupVaultResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if bvr.Properties != nil { - objectMap["properties"] = bvr.Properties - } - if bvr.ETag != nil { - objectMap["eTag"] = bvr.ETag - } - if bvr.Identity != nil { - objectMap["identity"] = bvr.Identity - } - if bvr.Location != nil { - objectMap["location"] = bvr.Location - } - if bvr.Tags != nil { - objectMap["tags"] = bvr.Tags - } - if bvr.SystemData != nil { - objectMap["systemData"] = bvr.SystemData - } - return json.Marshal(objectMap) -} - -// BackupVaultResourceList list of BackupVault resources -type BackupVaultResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]BackupVaultResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// BackupVaultResourceListIterator provides access to a complete listing of BackupVaultResource values. -type BackupVaultResourceListIterator struct { - i int - page BackupVaultResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *BackupVaultResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *BackupVaultResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter BackupVaultResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter BackupVaultResourceListIterator) Response() BackupVaultResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter BackupVaultResourceListIterator) Value() BackupVaultResource { - if !iter.page.NotDone() { - return BackupVaultResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the BackupVaultResourceListIterator type. -func NewBackupVaultResourceListIterator(page BackupVaultResourceListPage) BackupVaultResourceListIterator { - return BackupVaultResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (bvrl BackupVaultResourceList) IsEmpty() bool { - return bvrl.Value == nil || len(*bvrl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (bvrl BackupVaultResourceList) hasNextLink() bool { - return bvrl.NextLink != nil && len(*bvrl.NextLink) != 0 -} - -// backupVaultResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (bvrl BackupVaultResourceList) backupVaultResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !bvrl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(bvrl.NextLink))) -} - -// BackupVaultResourceListPage contains a page of BackupVaultResource values. -type BackupVaultResourceListPage struct { - fn func(context.Context, BackupVaultResourceList) (BackupVaultResourceList, error) - bvrl BackupVaultResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *BackupVaultResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BackupVaultResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.bvrl) - if err != nil { - return err - } - page.bvrl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *BackupVaultResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page BackupVaultResourceListPage) NotDone() bool { - return !page.bvrl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page BackupVaultResourceListPage) Response() BackupVaultResourceList { - return page.bvrl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page BackupVaultResourceListPage) Values() []BackupVaultResource { - if page.bvrl.IsEmpty() { - return nil - } - return *page.bvrl.Value -} - -// Creates a new instance of the BackupVaultResourceListPage type. -func NewBackupVaultResourceListPage(cur BackupVaultResourceList, getNextPage func(context.Context, BackupVaultResourceList) (BackupVaultResourceList, error)) BackupVaultResourceListPage { - return BackupVaultResourceListPage{ - fn: getNextPage, - bvrl: cur, - } -} - -// BackupVaultsCreateOrUpdateFuture an abstraction for monitoring and retrieving the results of a -// long-running operation. -type BackupVaultsCreateOrUpdateFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupVaultsClient) (BackupVaultResource, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupVaultsCreateOrUpdateFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupVaultsCreateOrUpdateFuture.Result. -func (future *BackupVaultsCreateOrUpdateFuture) result(client BackupVaultsClient) (bvr BackupVaultResource, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsCreateOrUpdateFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - bvr.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupVaultsCreateOrUpdateFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if bvr.Response.Response, err = future.GetResult(sender); err == nil && bvr.Response.Response.StatusCode != http.StatusNoContent { - bvr, err = client.CreateOrUpdateResponder(bvr.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsCreateOrUpdateFuture", "Result", bvr.Response.Response, "Failure responding to request") - } - } - return -} - -// BackupVaultsUpdateFuture an abstraction for monitoring and retrieving the results of a long-running -// operation. -type BackupVaultsUpdateFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(BackupVaultsClient) (BackupVaultResource, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *BackupVaultsUpdateFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for BackupVaultsUpdateFuture.Result. -func (future *BackupVaultsUpdateFuture) result(client BackupVaultsClient) (bvr BackupVaultResource, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsUpdateFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - bvr.Response.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.BackupVaultsUpdateFuture") - return - } - sender := autorest.DecorateSender(client, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) - if bvr.Response.Response, err = future.GetResult(sender); err == nil && bvr.Response.Response.StatusCode != http.StatusNoContent { - bvr, err = client.UpdateResponder(bvr.Response.Response) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.BackupVaultsUpdateFuture", "Result", bvr.Response.Response, "Failure responding to request") - } - } - return -} - -// BasicBaseBackupPolicy backupPolicy base -type BasicBaseBackupPolicy interface { - AsBackupPolicy() (*BackupPolicy, bool) - AsBaseBackupPolicy() (*BaseBackupPolicy, bool) -} - -// BaseBackupPolicy backupPolicy base -type BaseBackupPolicy struct { - // DatasourceTypes - Type of datasource for the backup management - DatasourceTypes *[]string `json:"datasourceTypes,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy', 'ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy' - ObjectType ObjectTypeBasicBaseBackupPolicy `json:"objectType,omitempty"` -} - -func unmarshalBasicBaseBackupPolicy(body []byte) (BasicBaseBackupPolicy, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicBaseBackupPolicyObjectTypeBackupPolicy): - var bp BackupPolicy - err := json.Unmarshal(body, &bp) - return bp, err - default: - var bbp BaseBackupPolicy - err := json.Unmarshal(body, &bbp) - return bbp, err - } -} - -func unmarshalBasicBaseBackupPolicyArray(body []byte) ([]BasicBaseBackupPolicy, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - bbpArray := make([]BasicBaseBackupPolicy, len(rawMessages)) - - for index, rawMessage := range rawMessages { - bbp, err := unmarshalBasicBaseBackupPolicy(*rawMessage) - if err != nil { - return nil, err - } - bbpArray[index] = bbp - } - return bbpArray, nil -} - -// MarshalJSON is the custom marshaler for BaseBackupPolicy. -func (bbp BaseBackupPolicy) MarshalJSON() ([]byte, error) { - bbp.ObjectType = ObjectTypeBasicBaseBackupPolicyObjectTypeBaseBackupPolicy - objectMap := make(map[string]interface{}) - if bbp.DatasourceTypes != nil { - objectMap["datasourceTypes"] = bbp.DatasourceTypes - } - if bbp.ObjectType != "" { - objectMap["objectType"] = bbp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsBackupPolicy is the BasicBaseBackupPolicy implementation for BaseBackupPolicy. -func (bbp BaseBackupPolicy) AsBackupPolicy() (*BackupPolicy, bool) { - return nil, false -} - -// AsBaseBackupPolicy is the BasicBaseBackupPolicy implementation for BaseBackupPolicy. -func (bbp BaseBackupPolicy) AsBaseBackupPolicy() (*BaseBackupPolicy, bool) { - return &bbp, true -} - -// AsBasicBaseBackupPolicy is the BasicBaseBackupPolicy implementation for BaseBackupPolicy. -func (bbp BaseBackupPolicy) AsBasicBaseBackupPolicy() (BasicBaseBackupPolicy, bool) { - return &bbp, true -} - -// BaseBackupPolicyResource baseBackupPolicy resource -type BaseBackupPolicyResource struct { - autorest.Response `json:"-"` - // Properties - BaseBackupPolicyResource properties - Properties BasicBaseBackupPolicy `json:"properties,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for BaseBackupPolicyResource. -func (bbpr BaseBackupPolicyResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - objectMap["properties"] = bbpr.Properties - if bbpr.SystemData != nil { - objectMap["systemData"] = bbpr.SystemData - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for BaseBackupPolicyResource struct. -func (bbpr *BaseBackupPolicyResource) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "properties": - if v != nil { - properties, err := unmarshalBasicBaseBackupPolicy(*v) - if err != nil { - return err - } - bbpr.Properties = properties - } - case "id": - if v != nil { - var ID string - err = json.Unmarshal(*v, &ID) - if err != nil { - return err - } - bbpr.ID = &ID - } - case "name": - if v != nil { - var name string - err = json.Unmarshal(*v, &name) - if err != nil { - return err - } - bbpr.Name = &name - } - case "type": - if v != nil { - var typeVar string - err = json.Unmarshal(*v, &typeVar) - if err != nil { - return err - } - bbpr.Type = &typeVar - } - case "systemData": - if v != nil { - var systemData SystemData - err = json.Unmarshal(*v, &systemData) - if err != nil { - return err - } - bbpr.SystemData = &systemData - } - } - } - - return nil -} - -// BaseBackupPolicyResourceList list of BaseBackupPolicy resources -type BaseBackupPolicyResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]BaseBackupPolicyResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// BaseBackupPolicyResourceListIterator provides access to a complete listing of BaseBackupPolicyResource -// values. -type BaseBackupPolicyResourceListIterator struct { - i int - page BaseBackupPolicyResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *BaseBackupPolicyResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BaseBackupPolicyResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *BaseBackupPolicyResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter BaseBackupPolicyResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter BaseBackupPolicyResourceListIterator) Response() BaseBackupPolicyResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter BaseBackupPolicyResourceListIterator) Value() BaseBackupPolicyResource { - if !iter.page.NotDone() { - return BaseBackupPolicyResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the BaseBackupPolicyResourceListIterator type. -func NewBaseBackupPolicyResourceListIterator(page BaseBackupPolicyResourceListPage) BaseBackupPolicyResourceListIterator { - return BaseBackupPolicyResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (bbprl BaseBackupPolicyResourceList) IsEmpty() bool { - return bbprl.Value == nil || len(*bbprl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (bbprl BaseBackupPolicyResourceList) hasNextLink() bool { - return bbprl.NextLink != nil && len(*bbprl.NextLink) != 0 -} - -// baseBackupPolicyResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (bbprl BaseBackupPolicyResourceList) baseBackupPolicyResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !bbprl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(bbprl.NextLink))) -} - -// BaseBackupPolicyResourceListPage contains a page of BaseBackupPolicyResource values. -type BaseBackupPolicyResourceListPage struct { - fn func(context.Context, BaseBackupPolicyResourceList) (BaseBackupPolicyResourceList, error) - bbprl BaseBackupPolicyResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *BaseBackupPolicyResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/BaseBackupPolicyResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.bbprl) - if err != nil { - return err - } - page.bbprl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *BaseBackupPolicyResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page BaseBackupPolicyResourceListPage) NotDone() bool { - return !page.bbprl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page BaseBackupPolicyResourceListPage) Response() BaseBackupPolicyResourceList { - return page.bbprl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page BaseBackupPolicyResourceListPage) Values() []BaseBackupPolicyResource { - if page.bbprl.IsEmpty() { - return nil - } - return *page.bbprl.Value -} - -// Creates a new instance of the BaseBackupPolicyResourceListPage type. -func NewBaseBackupPolicyResourceListPage(cur BaseBackupPolicyResourceList, getNextPage func(context.Context, BaseBackupPolicyResourceList) (BaseBackupPolicyResourceList, error)) BaseBackupPolicyResourceListPage { - return BaseBackupPolicyResourceListPage{ - fn: getNextPage, - bbprl: cur, - } -} - -// BasicBasePolicyRule basePolicy Rule -type BasicBasePolicyRule interface { - AsAzureBackupRule() (*AzureBackupRule, bool) - AsAzureRetentionRule() (*AzureRetentionRule, bool) - AsBasePolicyRule() (*BasePolicyRule, bool) -} - -// BasePolicyRule basePolicy Rule -type BasePolicyRule struct { - Name *string `json:"name,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule', 'ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule' - ObjectType ObjectTypeBasicBasePolicyRule `json:"objectType,omitempty"` -} - -func unmarshalBasicBasePolicyRule(body []byte) (BasicBasePolicyRule, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicBasePolicyRuleObjectTypeAzureBackupRule): - var abr AzureBackupRule - err := json.Unmarshal(body, &abr) - return abr, err - case string(ObjectTypeBasicBasePolicyRuleObjectTypeAzureRetentionRule): - var arr AzureRetentionRule - err := json.Unmarshal(body, &arr) - return arr, err - default: - var bpr BasePolicyRule - err := json.Unmarshal(body, &bpr) - return bpr, err - } -} - -func unmarshalBasicBasePolicyRuleArray(body []byte) ([]BasicBasePolicyRule, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - bprArray := make([]BasicBasePolicyRule, len(rawMessages)) - - for index, rawMessage := range rawMessages { - bpr, err := unmarshalBasicBasePolicyRule(*rawMessage) - if err != nil { - return nil, err - } - bprArray[index] = bpr - } - return bprArray, nil -} - -// MarshalJSON is the custom marshaler for BasePolicyRule. -func (bpr BasePolicyRule) MarshalJSON() ([]byte, error) { - bpr.ObjectType = ObjectTypeBasicBasePolicyRuleObjectTypeBasePolicyRule - objectMap := make(map[string]interface{}) - if bpr.Name != nil { - objectMap["name"] = bpr.Name - } - if bpr.ObjectType != "" { - objectMap["objectType"] = bpr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureBackupRule is the BasicBasePolicyRule implementation for BasePolicyRule. -func (bpr BasePolicyRule) AsAzureBackupRule() (*AzureBackupRule, bool) { - return nil, false -} - -// AsAzureRetentionRule is the BasicBasePolicyRule implementation for BasePolicyRule. -func (bpr BasePolicyRule) AsAzureRetentionRule() (*AzureRetentionRule, bool) { - return nil, false -} - -// AsBasePolicyRule is the BasicBasePolicyRule implementation for BasePolicyRule. -func (bpr BasePolicyRule) AsBasePolicyRule() (*BasePolicyRule, bool) { - return &bpr, true -} - -// AsBasicBasePolicyRule is the BasicBasePolicyRule implementation for BasePolicyRule. -func (bpr BasePolicyRule) AsBasicBasePolicyRule() (BasicBasePolicyRule, bool) { - return &bpr, true -} - -// CheckNameAvailabilityRequest checkNameAvailability Request -type CheckNameAvailabilityRequest struct { - // Name - Resource name for which availability needs to be checked - Name *string `json:"name,omitempty"` - // Type - Describes the Resource type: Microsoft.DataProtection/BackupVaults - Type *string `json:"type,omitempty"` -} - -// CheckNameAvailabilityResult checkNameAvailability Result -type CheckNameAvailabilityResult struct { - autorest.Response `json:"-"` - // Message - Gets or sets the message. - Message *string `json:"message,omitempty"` - // NameAvailable - Gets or sets a value indicating whether [name available]. - NameAvailable *bool `json:"nameAvailable,omitempty"` - // Reason - Gets or sets the reason. - Reason *string `json:"reason,omitempty"` -} - -// ClientDiscoveryDisplay localized display information of an operation. -type ClientDiscoveryDisplay struct { - // Description - Description of the operation having details of what operation is about. - Description *string `json:"description,omitempty"` - // Operation - Operations Name itself. - Operation *string `json:"operation,omitempty"` - // Provider - Name of the provider for display purposes - Provider *string `json:"provider,omitempty"` - // Resource - ResourceType for which this Operation can be performed. - Resource *string `json:"resource,omitempty"` -} - -// ClientDiscoveryForLogSpecification class to represent shoebox log specification in json client -// discovery. -type ClientDiscoveryForLogSpecification struct { - // BlobDuration - blob duration of shoebox log specification - BlobDuration *string `json:"blobDuration,omitempty"` - // DisplayName - Localized display name - DisplayName *string `json:"displayName,omitempty"` - // Name - Name for shoebox log specification. - Name *string `json:"name,omitempty"` -} - -// ClientDiscoveryForProperties class to represent shoebox properties in json client discovery. -type ClientDiscoveryForProperties struct { - // ServiceSpecification - Operation properties. - ServiceSpecification *ClientDiscoveryForServiceSpecification `json:"serviceSpecification,omitempty"` -} - -// ClientDiscoveryForServiceSpecification class to represent shoebox service specification in json client -// discovery. -type ClientDiscoveryForServiceSpecification struct { - // LogSpecifications - List of log specifications of this operation. - LogSpecifications *[]ClientDiscoveryForLogSpecification `json:"logSpecifications,omitempty"` -} - -// ClientDiscoveryResponse operations List response which contains list of available APIs. -type ClientDiscoveryResponse struct { - autorest.Response `json:"-"` - // NextLink - Link to the next chunk of Response. - NextLink *string `json:"nextLink,omitempty"` - // Value - List of available operations. - Value *[]ClientDiscoveryValueForSingleAPI `json:"value,omitempty"` -} - -// ClientDiscoveryResponseIterator provides access to a complete listing of -// ClientDiscoveryValueForSingleAPI values. -type ClientDiscoveryResponseIterator struct { - i int - page ClientDiscoveryResponsePage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *ClientDiscoveryResponseIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ClientDiscoveryResponseIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *ClientDiscoveryResponseIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter ClientDiscoveryResponseIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter ClientDiscoveryResponseIterator) Response() ClientDiscoveryResponse { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter ClientDiscoveryResponseIterator) Value() ClientDiscoveryValueForSingleAPI { - if !iter.page.NotDone() { - return ClientDiscoveryValueForSingleAPI{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the ClientDiscoveryResponseIterator type. -func NewClientDiscoveryResponseIterator(page ClientDiscoveryResponsePage) ClientDiscoveryResponseIterator { - return ClientDiscoveryResponseIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (cdr ClientDiscoveryResponse) IsEmpty() bool { - return cdr.Value == nil || len(*cdr.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (cdr ClientDiscoveryResponse) hasNextLink() bool { - return cdr.NextLink != nil && len(*cdr.NextLink) != 0 -} - -// clientDiscoveryResponsePreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (cdr ClientDiscoveryResponse) clientDiscoveryResponsePreparer(ctx context.Context) (*http.Request, error) { - if !cdr.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(cdr.NextLink))) -} - -// ClientDiscoveryResponsePage contains a page of ClientDiscoveryValueForSingleAPI values. -type ClientDiscoveryResponsePage struct { - fn func(context.Context, ClientDiscoveryResponse) (ClientDiscoveryResponse, error) - cdr ClientDiscoveryResponse -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *ClientDiscoveryResponsePage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ClientDiscoveryResponsePage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.cdr) - if err != nil { - return err - } - page.cdr = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *ClientDiscoveryResponsePage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page ClientDiscoveryResponsePage) NotDone() bool { - return !page.cdr.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page ClientDiscoveryResponsePage) Response() ClientDiscoveryResponse { - return page.cdr -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page ClientDiscoveryResponsePage) Values() []ClientDiscoveryValueForSingleAPI { - if page.cdr.IsEmpty() { - return nil - } - return *page.cdr.Value -} - -// Creates a new instance of the ClientDiscoveryResponsePage type. -func NewClientDiscoveryResponsePage(cur ClientDiscoveryResponse, getNextPage func(context.Context, ClientDiscoveryResponse) (ClientDiscoveryResponse, error)) ClientDiscoveryResponsePage { - return ClientDiscoveryResponsePage{ - fn: getNextPage, - cdr: cur, - } -} - -// ClientDiscoveryValueForSingleAPI available operation details. -type ClientDiscoveryValueForSingleAPI struct { - // Display - Contains the localized display information for this particular operation - Display *ClientDiscoveryDisplay `json:"display,omitempty"` - // Name - Name of the Operation. - Name *string `json:"name,omitempty"` - // IsDataAction - Indicates whether the operation is a data action - IsDataAction *bool `json:"isDataAction,omitempty"` - // Origin - The intended executor of the operation;governs the display of the operation in the RBAC UX and the audit logs UX - Origin *string `json:"origin,omitempty"` - // Properties - Properties for the given operation. - Properties *ClientDiscoveryForProperties `json:"properties,omitempty"` -} - -// CloudError an error response from Azure Backup. -type CloudError struct { - Error *Error `json:"error,omitempty"` -} - -// CopyOnExpiryOption copy on Expiry Option -type CopyOnExpiryOption struct { - // ObjectType - Possible values include: 'ObjectTypeBasicCopyOptionObjectTypeCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption', 'ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption' - ObjectType ObjectTypeBasicCopyOption `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) MarshalJSON() ([]byte, error) { - coeo.ObjectType = ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption - objectMap := make(map[string]interface{}) - if coeo.ObjectType != "" { - objectMap["objectType"] = coeo.ObjectType - } - return json.Marshal(objectMap) -} - -// AsCopyOnExpiryOption is the BasicCopyOption implementation for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) AsCopyOnExpiryOption() (*CopyOnExpiryOption, bool) { - return &coeo, true -} - -// AsCustomCopyOption is the BasicCopyOption implementation for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) AsCustomCopyOption() (*CustomCopyOption, bool) { - return nil, false -} - -// AsImmediateCopyOption is the BasicCopyOption implementation for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) AsImmediateCopyOption() (*ImmediateCopyOption, bool) { - return nil, false -} - -// AsCopyOption is the BasicCopyOption implementation for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) AsCopyOption() (*CopyOption, bool) { - return nil, false -} - -// AsBasicCopyOption is the BasicCopyOption implementation for CopyOnExpiryOption. -func (coeo CopyOnExpiryOption) AsBasicCopyOption() (BasicCopyOption, bool) { - return &coeo, true -} - -// BasicCopyOption options to copy -type BasicCopyOption interface { - AsCopyOnExpiryOption() (*CopyOnExpiryOption, bool) - AsCustomCopyOption() (*CustomCopyOption, bool) - AsImmediateCopyOption() (*ImmediateCopyOption, bool) - AsCopyOption() (*CopyOption, bool) -} - -// CopyOption options to copy -type CopyOption struct { - // ObjectType - Possible values include: 'ObjectTypeBasicCopyOptionObjectTypeCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption', 'ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption' - ObjectType ObjectTypeBasicCopyOption `json:"objectType,omitempty"` -} - -func unmarshalBasicCopyOption(body []byte) (BasicCopyOption, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption): - var coeo CopyOnExpiryOption - err := json.Unmarshal(body, &coeo) - return coeo, err - case string(ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption): - var cco CustomCopyOption - err := json.Unmarshal(body, &cco) - return cco, err - case string(ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption): - var ico ImmediateCopyOption - err := json.Unmarshal(body, &ico) - return ico, err - default: - var co CopyOption - err := json.Unmarshal(body, &co) - return co, err - } -} - -func unmarshalBasicCopyOptionArray(body []byte) ([]BasicCopyOption, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - coArray := make([]BasicCopyOption, len(rawMessages)) - - for index, rawMessage := range rawMessages { - co, err := unmarshalBasicCopyOption(*rawMessage) - if err != nil { - return nil, err - } - coArray[index] = co - } - return coArray, nil -} - -// MarshalJSON is the custom marshaler for CopyOption. -func (co CopyOption) MarshalJSON() ([]byte, error) { - co.ObjectType = ObjectTypeBasicCopyOptionObjectTypeCopyOption - objectMap := make(map[string]interface{}) - if co.ObjectType != "" { - objectMap["objectType"] = co.ObjectType - } - return json.Marshal(objectMap) -} - -// AsCopyOnExpiryOption is the BasicCopyOption implementation for CopyOption. -func (co CopyOption) AsCopyOnExpiryOption() (*CopyOnExpiryOption, bool) { - return nil, false -} - -// AsCustomCopyOption is the BasicCopyOption implementation for CopyOption. -func (co CopyOption) AsCustomCopyOption() (*CustomCopyOption, bool) { - return nil, false -} - -// AsImmediateCopyOption is the BasicCopyOption implementation for CopyOption. -func (co CopyOption) AsImmediateCopyOption() (*ImmediateCopyOption, bool) { - return nil, false -} - -// AsCopyOption is the BasicCopyOption implementation for CopyOption. -func (co CopyOption) AsCopyOption() (*CopyOption, bool) { - return &co, true -} - -// AsBasicCopyOption is the BasicCopyOption implementation for CopyOption. -func (co CopyOption) AsBasicCopyOption() (BasicCopyOption, bool) { - return &co, true -} - -// CustomCopyOption duration based custom options to copy -type CustomCopyOption struct { - // Duration - Data copied after given timespan - Duration *string `json:"duration,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicCopyOptionObjectTypeCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption', 'ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption' - ObjectType ObjectTypeBasicCopyOption `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for CustomCopyOption. -func (cco CustomCopyOption) MarshalJSON() ([]byte, error) { - cco.ObjectType = ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption - objectMap := make(map[string]interface{}) - if cco.Duration != nil { - objectMap["duration"] = cco.Duration - } - if cco.ObjectType != "" { - objectMap["objectType"] = cco.ObjectType - } - return json.Marshal(objectMap) -} - -// AsCopyOnExpiryOption is the BasicCopyOption implementation for CustomCopyOption. -func (cco CustomCopyOption) AsCopyOnExpiryOption() (*CopyOnExpiryOption, bool) { - return nil, false -} - -// AsCustomCopyOption is the BasicCopyOption implementation for CustomCopyOption. -func (cco CustomCopyOption) AsCustomCopyOption() (*CustomCopyOption, bool) { - return &cco, true -} - -// AsImmediateCopyOption is the BasicCopyOption implementation for CustomCopyOption. -func (cco CustomCopyOption) AsImmediateCopyOption() (*ImmediateCopyOption, bool) { - return nil, false -} - -// AsCopyOption is the BasicCopyOption implementation for CustomCopyOption. -func (cco CustomCopyOption) AsCopyOption() (*CopyOption, bool) { - return nil, false -} - -// AsBasicCopyOption is the BasicCopyOption implementation for CustomCopyOption. -func (cco CustomCopyOption) AsBasicCopyOption() (BasicCopyOption, bool) { - return &cco, true -} - -// Datasource datasource to be backed up -type Datasource struct { - // DatasourceType - DatasourceType of the resource. - DatasourceType *string `json:"datasourceType,omitempty"` - // ObjectType - Type of Datasource object, used to initialize the right inherited type - ObjectType *string `json:"objectType,omitempty"` - // ResourceID - Full ARM ID of the resource. For azure resources, this is ARM ID. For non azure resources, this will be the ID created by backup service via Fabric/Vault. - ResourceID *string `json:"resourceID,omitempty"` - // ResourceLocation - Location of datasource. - ResourceLocation *string `json:"resourceLocation,omitempty"` - // ResourceName - Unique identifier of the resource in the context of parent. - ResourceName *string `json:"resourceName,omitempty"` - // ResourceType - Resource Type of Datasource. - ResourceType *string `json:"resourceType,omitempty"` - // ResourceURI - Uri of the resource. - ResourceURI *string `json:"resourceUri,omitempty"` -} - -// DatasourceSet datasourceSet details of datasource to be backed up -type DatasourceSet struct { - // DatasourceType - DatasourceType of the resource. - DatasourceType *string `json:"datasourceType,omitempty"` - // ObjectType - Type of Datasource object, used to initialize the right inherited type - ObjectType *string `json:"objectType,omitempty"` - // ResourceID - Full ARM ID of the resource. For azure resources, this is ARM ID. For non azure resources, this will be the ID created by backup service via Fabric/Vault. - ResourceID *string `json:"resourceID,omitempty"` - // ResourceLocation - Location of datasource. - ResourceLocation *string `json:"resourceLocation,omitempty"` - // ResourceName - Unique identifier of the resource in the context of parent. - ResourceName *string `json:"resourceName,omitempty"` - // ResourceType - Resource Type of Datasource. - ResourceType *string `json:"resourceType,omitempty"` - // ResourceURI - Uri of the resource. - ResourceURI *string `json:"resourceUri,omitempty"` -} - -// DataStoreInfoBase dataStoreInfo base -type DataStoreInfoBase struct { - // DataStoreType - type of datastore; Operational/Vault/Archive. Possible values include: 'DataStoreTypesOperationalStore', 'DataStoreTypesVaultStore', 'DataStoreTypesArchiveStore' - DataStoreType DataStoreTypes `json:"dataStoreType,omitempty"` - // ObjectType - Type of Datasource object, used to initialize the right inherited type - ObjectType *string `json:"objectType,omitempty"` -} - -// BasicDataStoreParameters parameters for DataStore -type BasicDataStoreParameters interface { - AsAzureOperationalStoreParameters() (*AzureOperationalStoreParameters, bool) - AsDataStoreParameters() (*DataStoreParameters, bool) -} - -// DataStoreParameters parameters for DataStore -type DataStoreParameters struct { - // DataStoreType - type of datastore; Operational/Vault/Archive. Possible values include: 'DataStoreTypesOperationalStore', 'DataStoreTypesVaultStore', 'DataStoreTypesArchiveStore' - DataStoreType DataStoreTypes `json:"dataStoreType,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters', 'ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters' - ObjectType ObjectTypeBasicDataStoreParameters `json:"objectType,omitempty"` -} - -func unmarshalBasicDataStoreParameters(body []byte) (BasicDataStoreParameters, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicDataStoreParametersObjectTypeAzureOperationalStoreParameters): - var aosp AzureOperationalStoreParameters - err := json.Unmarshal(body, &aosp) - return aosp, err - default: - var dsp DataStoreParameters - err := json.Unmarshal(body, &dsp) - return dsp, err - } -} - -func unmarshalBasicDataStoreParametersArray(body []byte) ([]BasicDataStoreParameters, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - dspArray := make([]BasicDataStoreParameters, len(rawMessages)) - - for index, rawMessage := range rawMessages { - dsp, err := unmarshalBasicDataStoreParameters(*rawMessage) - if err != nil { - return nil, err - } - dspArray[index] = dsp - } - return dspArray, nil -} - -// MarshalJSON is the custom marshaler for DataStoreParameters. -func (dsp DataStoreParameters) MarshalJSON() ([]byte, error) { - dsp.ObjectType = ObjectTypeBasicDataStoreParametersObjectTypeDataStoreParameters - objectMap := make(map[string]interface{}) - if dsp.DataStoreType != "" { - objectMap["dataStoreType"] = dsp.DataStoreType - } - if dsp.ObjectType != "" { - objectMap["objectType"] = dsp.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAzureOperationalStoreParameters is the BasicDataStoreParameters implementation for DataStoreParameters. -func (dsp DataStoreParameters) AsAzureOperationalStoreParameters() (*AzureOperationalStoreParameters, bool) { - return nil, false -} - -// AsDataStoreParameters is the BasicDataStoreParameters implementation for DataStoreParameters. -func (dsp DataStoreParameters) AsDataStoreParameters() (*DataStoreParameters, bool) { - return &dsp, true -} - -// AsBasicDataStoreParameters is the BasicDataStoreParameters implementation for DataStoreParameters. -func (dsp DataStoreParameters) AsBasicDataStoreParameters() (BasicDataStoreParameters, bool) { - return &dsp, true -} - -// Day day of the week -type Day struct { - // Date - Date of the month - Date *int32 `json:"date,omitempty"` - // IsLast - Whether Date is last date of month - IsLast *bool `json:"isLast,omitempty"` -} - -// BasicDeleteOption delete Option -type BasicDeleteOption interface { - AsAbsoluteDeleteOption() (*AbsoluteDeleteOption, bool) - AsDeleteOption() (*DeleteOption, bool) -} - -// DeleteOption delete Option -type DeleteOption struct { - // Duration - Duration of deletion after given timespan - Duration *string `json:"duration,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicDeleteOptionObjectTypeDeleteOption', 'ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption' - ObjectType ObjectTypeBasicDeleteOption `json:"objectType,omitempty"` -} - -func unmarshalBasicDeleteOption(body []byte) (BasicDeleteOption, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicDeleteOptionObjectTypeAbsoluteDeleteOption): - var ado AbsoluteDeleteOption - err := json.Unmarshal(body, &ado) - return ado, err - default: - var do DeleteOption - err := json.Unmarshal(body, &do) - return do, err - } -} - -func unmarshalBasicDeleteOptionArray(body []byte) ([]BasicDeleteOption, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - doArray := make([]BasicDeleteOption, len(rawMessages)) - - for index, rawMessage := range rawMessages { - do, err := unmarshalBasicDeleteOption(*rawMessage) - if err != nil { - return nil, err - } - doArray[index] = do - } - return doArray, nil -} - -// MarshalJSON is the custom marshaler for DeleteOption. -func (do DeleteOption) MarshalJSON() ([]byte, error) { - do.ObjectType = ObjectTypeBasicDeleteOptionObjectTypeDeleteOption - objectMap := make(map[string]interface{}) - if do.Duration != nil { - objectMap["duration"] = do.Duration - } - if do.ObjectType != "" { - objectMap["objectType"] = do.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAbsoluteDeleteOption is the BasicDeleteOption implementation for DeleteOption. -func (do DeleteOption) AsAbsoluteDeleteOption() (*AbsoluteDeleteOption, bool) { - return nil, false -} - -// AsDeleteOption is the BasicDeleteOption implementation for DeleteOption. -func (do DeleteOption) AsDeleteOption() (*DeleteOption, bool) { - return &do, true -} - -// AsBasicDeleteOption is the BasicDeleteOption implementation for DeleteOption. -func (do DeleteOption) AsBasicDeleteOption() (BasicDeleteOption, bool) { - return &do, true -} - -// DppBaseResource base resource under Microsoft.DataProtection provider namespace -type DppBaseResource struct { - autorest.Response `json:"-"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` -} - -// MarshalJSON is the custom marshaler for DppBaseResource. -func (dbr DppBaseResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// DppBaseResourceList base for all lists of V2 resources. -type DppBaseResourceList struct { - autorest.Response `json:"-"` - // Value - List of Dpp resources. - Value *[]DppBaseResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// DppBaseResourceListIterator provides access to a complete listing of DppBaseResource values. -type DppBaseResourceListIterator struct { - i int - page DppBaseResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *DppBaseResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/DppBaseResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *DppBaseResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter DppBaseResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter DppBaseResourceListIterator) Response() DppBaseResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter DppBaseResourceListIterator) Value() DppBaseResource { - if !iter.page.NotDone() { - return DppBaseResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the DppBaseResourceListIterator type. -func NewDppBaseResourceListIterator(page DppBaseResourceListPage) DppBaseResourceListIterator { - return DppBaseResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (dbrl DppBaseResourceList) IsEmpty() bool { - return dbrl.Value == nil || len(*dbrl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (dbrl DppBaseResourceList) hasNextLink() bool { - return dbrl.NextLink != nil && len(*dbrl.NextLink) != 0 -} - -// dppBaseResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (dbrl DppBaseResourceList) dppBaseResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !dbrl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(dbrl.NextLink))) -} - -// DppBaseResourceListPage contains a page of DppBaseResource values. -type DppBaseResourceListPage struct { - fn func(context.Context, DppBaseResourceList) (DppBaseResourceList, error) - dbrl DppBaseResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *DppBaseResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/DppBaseResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.dbrl) - if err != nil { - return err - } - page.dbrl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *DppBaseResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page DppBaseResourceListPage) NotDone() bool { - return !page.dbrl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page DppBaseResourceListPage) Response() DppBaseResourceList { - return page.dbrl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page DppBaseResourceListPage) Values() []DppBaseResource { - if page.dbrl.IsEmpty() { - return nil - } - return *page.dbrl.Value -} - -// Creates a new instance of the DppBaseResourceListPage type. -func NewDppBaseResourceListPage(cur DppBaseResourceList, getNextPage func(context.Context, DppBaseResourceList) (DppBaseResourceList, error)) DppBaseResourceListPage { - return DppBaseResourceListPage{ - fn: getNextPage, - dbrl: cur, - } -} - -// DppIdentityDetails identity details -type DppIdentityDetails struct { - // PrincipalID - READ-ONLY; The object ID of the service principal object for the managed identity that is used to grant role-based access to an Azure resource. - PrincipalID *string `json:"principalId,omitempty"` - // TenantID - READ-ONLY; A Globally Unique Identifier (GUID) that represents the Azure AD tenant where the resource is now a member. - TenantID *string `json:"tenantId,omitempty"` - // Type - The identityType which can be either SystemAssigned or None - Type *string `json:"type,omitempty"` -} - -// MarshalJSON is the custom marshaler for DppIdentityDetails. -func (did DppIdentityDetails) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if did.Type != nil { - objectMap["type"] = did.Type - } - return json.Marshal(objectMap) -} - -// DppResource resource class -type DppResource struct { - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for DppResource. -func (dr DppResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if dr.SystemData != nil { - objectMap["systemData"] = dr.SystemData - } - return json.Marshal(objectMap) -} - -// DppResourceList listResource -type DppResourceList struct { - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// DppTrackedResource ... -type DppTrackedResource struct { - // ETag - Optional ETag. - ETag *string `json:"eTag,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Identity - Input Managed Identity Details - Identity *DppIdentityDetails `json:"identity,omitempty"` - // Location - Resource location. - Location *string `json:"location,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Tags - Resource tags. - Tags map[string]*string `json:"tags"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for DppTrackedResource. -func (dtr DppTrackedResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if dtr.ETag != nil { - objectMap["eTag"] = dtr.ETag - } - if dtr.Identity != nil { - objectMap["identity"] = dtr.Identity - } - if dtr.Location != nil { - objectMap["location"] = dtr.Location - } - if dtr.Tags != nil { - objectMap["tags"] = dtr.Tags - } - if dtr.SystemData != nil { - objectMap["systemData"] = dtr.SystemData - } - return json.Marshal(objectMap) -} - -// DppTrackedResourceList ... -type DppTrackedResourceList struct { - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// DppWorkerRequest ... -type DppWorkerRequest struct { - SubscriptionID *string `json:"subscriptionId,omitempty"` - URI *string `json:"uri,omitempty"` - Headers map[string][]string `json:"headers"` - SupportedGroupVersions *[]string `json:"supportedGroupVersions,omitempty"` - CultureInfo *string `json:"cultureInfo,omitempty"` - Parameters map[string]*string `json:"parameters"` - HTTPMethod *string `json:"httpMethod,omitempty"` -} - -// MarshalJSON is the custom marshaler for DppWorkerRequest. -func (dwr DppWorkerRequest) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if dwr.SubscriptionID != nil { - objectMap["subscriptionId"] = dwr.SubscriptionID - } - if dwr.URI != nil { - objectMap["uri"] = dwr.URI - } - if dwr.Headers != nil { - objectMap["headers"] = dwr.Headers - } - if dwr.SupportedGroupVersions != nil { - objectMap["supportedGroupVersions"] = dwr.SupportedGroupVersions - } - if dwr.CultureInfo != nil { - objectMap["cultureInfo"] = dwr.CultureInfo - } - if dwr.Parameters != nil { - objectMap["parameters"] = dwr.Parameters - } - if dwr.HTTPMethod != nil { - objectMap["httpMethod"] = dwr.HTTPMethod - } - return json.Marshal(objectMap) -} - -// Error the resource management error response. -type Error struct { - // AdditionalInfo - READ-ONLY; The error additional info. - AdditionalInfo *[]ErrorAdditionalInfo `json:"additionalInfo,omitempty"` - // Code - READ-ONLY; The error code. - Code *string `json:"code,omitempty"` - // Details - READ-ONLY; The error details. - Details *[]Error `json:"details,omitempty"` - // Message - READ-ONLY; The error message. - Message *string `json:"message,omitempty"` - // Target - READ-ONLY; The error target. - Target *string `json:"target,omitempty"` -} - -// MarshalJSON is the custom marshaler for Error. -func (e Error) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// ErrorAdditionalInfo the resource management error additional info. -type ErrorAdditionalInfo struct { - // Info - READ-ONLY; The additional info. - Info interface{} `json:"info,omitempty"` - // Type - READ-ONLY; The additional info type. - Type *string `json:"type,omitempty"` -} - -// MarshalJSON is the custom marshaler for ErrorAdditionalInfo. -func (eai ErrorAdditionalInfo) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// ExportJobsResult the result for export jobs containing blob details. -type ExportJobsResult struct { - autorest.Response `json:"-"` - // BlobURL - READ-ONLY; URL of the blob into which the serialized string of list of jobs is exported. - BlobURL *string `json:"blobUrl,omitempty"` - // BlobSasKey - READ-ONLY; SAS key to access the blob. - BlobSasKey *string `json:"blobSasKey,omitempty"` - // ExcelFileBlobURL - READ-ONLY; URL of the blob into which the ExcelFile is uploaded. - ExcelFileBlobURL *string `json:"excelFileBlobUrl,omitempty"` - // ExcelFileBlobSasKey - READ-ONLY; SAS key to access the ExcelFile blob. - ExcelFileBlobSasKey *string `json:"excelFileBlobSasKey,omitempty"` -} - -// MarshalJSON is the custom marshaler for ExportJobsResult. -func (ejr ExportJobsResult) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// ExportJobsTriggerFuture an abstraction for monitoring and retrieving the results of a long-running -// operation. -type ExportJobsTriggerFuture struct { - azure.FutureAPI - // Result returns the result of the asynchronous operation. - // If the operation has not completed it will return an error. - Result func(ExportJobsClient) (autorest.Response, error) -} - -// UnmarshalJSON is the custom unmarshaller for CreateFuture. -func (future *ExportJobsTriggerFuture) UnmarshalJSON(body []byte) error { - var azFuture azure.Future - if err := json.Unmarshal(body, &azFuture); err != nil { - return err - } - future.FutureAPI = &azFuture - future.Result = future.result - return nil -} - -// result is the default implementation for ExportJobsTriggerFuture.Result. -func (future *ExportJobsTriggerFuture) result(client ExportJobsClient) (ar autorest.Response, err error) { - var done bool - done, err = future.DoneWithContext(context.Background(), client) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ExportJobsTriggerFuture", "Result", future.Response(), "Polling failure") - return - } - if !done { - ar.Response = future.Response() - err = azure.NewAsyncOpIncompleteError("dataprotection.ExportJobsTriggerFuture") - return - } - ar.Response = future.Response() - return -} - -// FeatureValidationRequest base class for feature object -type FeatureValidationRequest struct { - // FeatureType - backup support feature type. Possible values include: 'FeatureTypeInvalid', 'FeatureTypeDataSourceType' - FeatureType FeatureType `json:"featureType,omitempty"` - // FeatureName - backup support feature name. - FeatureName *string `json:"featureName,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase', 'ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest' - ObjectType ObjectTypeBasicFeatureValidationRequestBase `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for FeatureValidationRequest. -func (fvr FeatureValidationRequest) MarshalJSON() ([]byte, error) { - fvr.ObjectType = ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest - objectMap := make(map[string]interface{}) - if fvr.FeatureType != "" { - objectMap["featureType"] = fvr.FeatureType - } - if fvr.FeatureName != nil { - objectMap["featureName"] = fvr.FeatureName - } - if fvr.ObjectType != "" { - objectMap["objectType"] = fvr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsFeatureValidationRequest is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequest. -func (fvr FeatureValidationRequest) AsFeatureValidationRequest() (*FeatureValidationRequest, bool) { - return &fvr, true -} - -// AsFeatureValidationRequestBase is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequest. -func (fvr FeatureValidationRequest) AsFeatureValidationRequestBase() (*FeatureValidationRequestBase, bool) { - return nil, false -} - -// AsBasicFeatureValidationRequestBase is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequest. -func (fvr FeatureValidationRequest) AsBasicFeatureValidationRequestBase() (BasicFeatureValidationRequestBase, bool) { - return &fvr, true -} - -// BasicFeatureValidationRequestBase base class for Backup Feature support -type BasicFeatureValidationRequestBase interface { - AsFeatureValidationRequest() (*FeatureValidationRequest, bool) - AsFeatureValidationRequestBase() (*FeatureValidationRequestBase, bool) -} - -// FeatureValidationRequestBase base class for Backup Feature support -type FeatureValidationRequestBase struct { - // ObjectType - Possible values include: 'ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase', 'ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest' - ObjectType ObjectTypeBasicFeatureValidationRequestBase `json:"objectType,omitempty"` -} - -func unmarshalBasicFeatureValidationRequestBase(body []byte) (BasicFeatureValidationRequestBase, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequest): - var fvr FeatureValidationRequest - err := json.Unmarshal(body, &fvr) - return fvr, err - default: - var fvrb FeatureValidationRequestBase - err := json.Unmarshal(body, &fvrb) - return fvrb, err - } -} - -func unmarshalBasicFeatureValidationRequestBaseArray(body []byte) ([]BasicFeatureValidationRequestBase, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - fvrbArray := make([]BasicFeatureValidationRequestBase, len(rawMessages)) - - for index, rawMessage := range rawMessages { - fvrb, err := unmarshalBasicFeatureValidationRequestBase(*rawMessage) - if err != nil { - return nil, err - } - fvrbArray[index] = fvrb - } - return fvrbArray, nil -} - -// MarshalJSON is the custom marshaler for FeatureValidationRequestBase. -func (fvrb FeatureValidationRequestBase) MarshalJSON() ([]byte, error) { - fvrb.ObjectType = ObjectTypeBasicFeatureValidationRequestBaseObjectTypeFeatureValidationRequestBase - objectMap := make(map[string]interface{}) - if fvrb.ObjectType != "" { - objectMap["objectType"] = fvrb.ObjectType - } - return json.Marshal(objectMap) -} - -// AsFeatureValidationRequest is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequestBase. -func (fvrb FeatureValidationRequestBase) AsFeatureValidationRequest() (*FeatureValidationRequest, bool) { - return nil, false -} - -// AsFeatureValidationRequestBase is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequestBase. -func (fvrb FeatureValidationRequestBase) AsFeatureValidationRequestBase() (*FeatureValidationRequestBase, bool) { - return &fvrb, true -} - -// AsBasicFeatureValidationRequestBase is the BasicFeatureValidationRequestBase implementation for FeatureValidationRequestBase. -func (fvrb FeatureValidationRequestBase) AsBasicFeatureValidationRequestBase() (BasicFeatureValidationRequestBase, bool) { - return &fvrb, true -} - -// FeatureValidationResponse feature Validation Response -type FeatureValidationResponse struct { - // FeatureType - backup support feature type. Possible values include: 'FeatureTypeInvalid', 'FeatureTypeDataSourceType' - FeatureType FeatureType `json:"featureType,omitempty"` - // Features - Response features - Features *[]SupportedFeature `json:"features,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase', 'ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse' - ObjectType ObjectTypeBasicFeatureValidationResponseBase `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for FeatureValidationResponse. -func (fvr FeatureValidationResponse) MarshalJSON() ([]byte, error) { - fvr.ObjectType = ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse - objectMap := make(map[string]interface{}) - if fvr.FeatureType != "" { - objectMap["featureType"] = fvr.FeatureType - } - if fvr.Features != nil { - objectMap["features"] = fvr.Features - } - if fvr.ObjectType != "" { - objectMap["objectType"] = fvr.ObjectType - } - return json.Marshal(objectMap) -} - -// AsFeatureValidationResponse is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponse. -func (fvr FeatureValidationResponse) AsFeatureValidationResponse() (*FeatureValidationResponse, bool) { - return &fvr, true -} - -// AsFeatureValidationResponseBase is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponse. -func (fvr FeatureValidationResponse) AsFeatureValidationResponseBase() (*FeatureValidationResponseBase, bool) { - return nil, false -} - -// AsBasicFeatureValidationResponseBase is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponse. -func (fvr FeatureValidationResponse) AsBasicFeatureValidationResponseBase() (BasicFeatureValidationResponseBase, bool) { - return &fvr, true -} - -// BasicFeatureValidationResponseBase base class for Backup Feature support -type BasicFeatureValidationResponseBase interface { - AsFeatureValidationResponse() (*FeatureValidationResponse, bool) - AsFeatureValidationResponseBase() (*FeatureValidationResponseBase, bool) -} - -// FeatureValidationResponseBase base class for Backup Feature support -type FeatureValidationResponseBase struct { - autorest.Response `json:"-"` - // ObjectType - Possible values include: 'ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase', 'ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse' - ObjectType ObjectTypeBasicFeatureValidationResponseBase `json:"objectType,omitempty"` -} - -func unmarshalBasicFeatureValidationResponseBase(body []byte) (BasicFeatureValidationResponseBase, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponse): - var fvr FeatureValidationResponse - err := json.Unmarshal(body, &fvr) - return fvr, err - default: - var fvrb FeatureValidationResponseBase - err := json.Unmarshal(body, &fvrb) - return fvrb, err - } -} - -func unmarshalBasicFeatureValidationResponseBaseArray(body []byte) ([]BasicFeatureValidationResponseBase, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - fvrbArray := make([]BasicFeatureValidationResponseBase, len(rawMessages)) - - for index, rawMessage := range rawMessages { - fvrb, err := unmarshalBasicFeatureValidationResponseBase(*rawMessage) - if err != nil { - return nil, err - } - fvrbArray[index] = fvrb - } - return fvrbArray, nil -} - -// MarshalJSON is the custom marshaler for FeatureValidationResponseBase. -func (fvrb FeatureValidationResponseBase) MarshalJSON() ([]byte, error) { - fvrb.ObjectType = ObjectTypeBasicFeatureValidationResponseBaseObjectTypeFeatureValidationResponseBase - objectMap := make(map[string]interface{}) - if fvrb.ObjectType != "" { - objectMap["objectType"] = fvrb.ObjectType - } - return json.Marshal(objectMap) -} - -// AsFeatureValidationResponse is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponseBase. -func (fvrb FeatureValidationResponseBase) AsFeatureValidationResponse() (*FeatureValidationResponse, bool) { - return nil, false -} - -// AsFeatureValidationResponseBase is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponseBase. -func (fvrb FeatureValidationResponseBase) AsFeatureValidationResponseBase() (*FeatureValidationResponseBase, bool) { - return &fvrb, true -} - -// AsBasicFeatureValidationResponseBase is the BasicFeatureValidationResponseBase implementation for FeatureValidationResponseBase. -func (fvrb FeatureValidationResponseBase) AsBasicFeatureValidationResponseBase() (BasicFeatureValidationResponseBase, bool) { - return &fvrb, true -} - -// FeatureValidationResponseBaseModel ... -type FeatureValidationResponseBaseModel struct { - autorest.Response `json:"-"` - Value BasicFeatureValidationResponseBase `json:"value,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for FeatureValidationResponseBaseModel struct. -func (fvrbm *FeatureValidationResponseBaseModel) UnmarshalJSON(body []byte) error { - fvrb, err := unmarshalBasicFeatureValidationResponseBase(body) - if err != nil { - return err - } - fvrbm.Value = fvrb - - return nil -} - -// ImmediateCopyOption immediate copy Option -type ImmediateCopyOption struct { - // ObjectType - Possible values include: 'ObjectTypeBasicCopyOptionObjectTypeCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeCopyOnExpiryOption', 'ObjectTypeBasicCopyOptionObjectTypeCustomCopyOption', 'ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption' - ObjectType ObjectTypeBasicCopyOption `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for ImmediateCopyOption. -func (ico ImmediateCopyOption) MarshalJSON() ([]byte, error) { - ico.ObjectType = ObjectTypeBasicCopyOptionObjectTypeImmediateCopyOption - objectMap := make(map[string]interface{}) - if ico.ObjectType != "" { - objectMap["objectType"] = ico.ObjectType - } - return json.Marshal(objectMap) -} - -// AsCopyOnExpiryOption is the BasicCopyOption implementation for ImmediateCopyOption. -func (ico ImmediateCopyOption) AsCopyOnExpiryOption() (*CopyOnExpiryOption, bool) { - return nil, false -} - -// AsCustomCopyOption is the BasicCopyOption implementation for ImmediateCopyOption. -func (ico ImmediateCopyOption) AsCustomCopyOption() (*CustomCopyOption, bool) { - return nil, false -} - -// AsImmediateCopyOption is the BasicCopyOption implementation for ImmediateCopyOption. -func (ico ImmediateCopyOption) AsImmediateCopyOption() (*ImmediateCopyOption, bool) { - return &ico, true -} - -// AsCopyOption is the BasicCopyOption implementation for ImmediateCopyOption. -func (ico ImmediateCopyOption) AsCopyOption() (*CopyOption, bool) { - return nil, false -} - -// AsBasicCopyOption is the BasicCopyOption implementation for ImmediateCopyOption. -func (ico ImmediateCopyOption) AsBasicCopyOption() (BasicCopyOption, bool) { - return &ico, true -} - -// InnerError inner Error -type InnerError struct { - // AdditionalInfo - Any Key value pairs that can be provided to the client for additional verbose information. - AdditionalInfo map[string]*string `json:"additionalInfo"` - // Code - Unique code for this error - Code *string `json:"code,omitempty"` - // EmbeddedInnerError - Child Inner Error, to allow Nesting. - EmbeddedInnerError *InnerError `json:"embeddedInnerError,omitempty"` -} - -// MarshalJSON is the custom marshaler for InnerError. -func (ie InnerError) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if ie.AdditionalInfo != nil { - objectMap["additionalInfo"] = ie.AdditionalInfo - } - if ie.Code != nil { - objectMap["code"] = ie.Code - } - if ie.EmbeddedInnerError != nil { - objectMap["embeddedInnerError"] = ie.EmbeddedInnerError - } - return json.Marshal(objectMap) -} - -// BasicItemLevelRestoreCriteria class to contain criteria for item level restore -type BasicItemLevelRestoreCriteria interface { - AsRangeBasedItemLevelRestoreCriteria() (*RangeBasedItemLevelRestoreCriteria, bool) - AsItemLevelRestoreCriteria() (*ItemLevelRestoreCriteria, bool) -} - -// ItemLevelRestoreCriteria class to contain criteria for item level restore -type ItemLevelRestoreCriteria struct { - // ObjectType - Possible values include: 'ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria', 'ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria' - ObjectType ObjectTypeBasicItemLevelRestoreCriteria `json:"objectType,omitempty"` -} - -func unmarshalBasicItemLevelRestoreCriteria(body []byte) (BasicItemLevelRestoreCriteria, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria): - var rbilrc RangeBasedItemLevelRestoreCriteria - err := json.Unmarshal(body, &rbilrc) - return rbilrc, err - default: - var ilrc ItemLevelRestoreCriteria - err := json.Unmarshal(body, &ilrc) - return ilrc, err - } -} - -func unmarshalBasicItemLevelRestoreCriteriaArray(body []byte) ([]BasicItemLevelRestoreCriteria, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - ilrcArray := make([]BasicItemLevelRestoreCriteria, len(rawMessages)) - - for index, rawMessage := range rawMessages { - ilrc, err := unmarshalBasicItemLevelRestoreCriteria(*rawMessage) - if err != nil { - return nil, err - } - ilrcArray[index] = ilrc - } - return ilrcArray, nil -} - -// MarshalJSON is the custom marshaler for ItemLevelRestoreCriteria. -func (ilrc ItemLevelRestoreCriteria) MarshalJSON() ([]byte, error) { - ilrc.ObjectType = ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria - objectMap := make(map[string]interface{}) - if ilrc.ObjectType != "" { - objectMap["objectType"] = ilrc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsRangeBasedItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for ItemLevelRestoreCriteria. -func (ilrc ItemLevelRestoreCriteria) AsRangeBasedItemLevelRestoreCriteria() (*RangeBasedItemLevelRestoreCriteria, bool) { - return nil, false -} - -// AsItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for ItemLevelRestoreCriteria. -func (ilrc ItemLevelRestoreCriteria) AsItemLevelRestoreCriteria() (*ItemLevelRestoreCriteria, bool) { - return &ilrc, true -} - -// AsBasicItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for ItemLevelRestoreCriteria. -func (ilrc ItemLevelRestoreCriteria) AsBasicItemLevelRestoreCriteria() (BasicItemLevelRestoreCriteria, bool) { - return &ilrc, true -} - -// ItemLevelRestoreTargetInfo restore target info for Item level restore operation -type ItemLevelRestoreTargetInfo struct { - // RestoreCriteria - Restore Criteria - RestoreCriteria *[]BasicItemLevelRestoreCriteria `json:"restoreCriteria,omitempty"` - // DatasourceInfo - Information of target DS - DatasourceInfo *Datasource `json:"datasourceInfo,omitempty"` - // DatasourceSetInfo - Information of target DS Set - DatasourceSetInfo *DatasourceSet `json:"datasourceSetInfo,omitempty"` - // DatasourceAuthCredentials - Credentials to use to authenticate with data source provider. - DatasourceAuthCredentials BasicAuthCredentials `json:"datasourceAuthCredentials,omitempty"` - // RecoveryOption - Recovery Option - RecoveryOption *string `json:"recoveryOption,omitempty"` - // RestoreLocation - Target Restore region - RestoreLocation *string `json:"restoreLocation,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo' - ObjectType ObjectTypeBasicRestoreTargetInfoBase `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) MarshalJSON() ([]byte, error) { - ilrti.ObjectType = ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo - objectMap := make(map[string]interface{}) - if ilrti.RestoreCriteria != nil { - objectMap["restoreCriteria"] = ilrti.RestoreCriteria - } - if ilrti.DatasourceInfo != nil { - objectMap["datasourceInfo"] = ilrti.DatasourceInfo - } - if ilrti.DatasourceSetInfo != nil { - objectMap["datasourceSetInfo"] = ilrti.DatasourceSetInfo - } - objectMap["datasourceAuthCredentials"] = ilrti.DatasourceAuthCredentials - if ilrti.RecoveryOption != nil { - objectMap["recoveryOption"] = ilrti.RecoveryOption - } - if ilrti.RestoreLocation != nil { - objectMap["restoreLocation"] = ilrti.RestoreLocation - } - if ilrti.ObjectType != "" { - objectMap["objectType"] = ilrti.ObjectType - } - return json.Marshal(objectMap) -} - -// AsItemLevelRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) AsItemLevelRestoreTargetInfo() (*ItemLevelRestoreTargetInfo, bool) { - return &ilrti, true -} - -// AsRestoreFilesTargetInfo is the BasicRestoreTargetInfoBase implementation for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) AsRestoreFilesTargetInfo() (*RestoreFilesTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) AsRestoreTargetInfo() (*RestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) AsRestoreTargetInfoBase() (*RestoreTargetInfoBase, bool) { - return nil, false -} - -// AsBasicRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for ItemLevelRestoreTargetInfo. -func (ilrti ItemLevelRestoreTargetInfo) AsBasicRestoreTargetInfoBase() (BasicRestoreTargetInfoBase, bool) { - return &ilrti, true -} - -// UnmarshalJSON is the custom unmarshaler for ItemLevelRestoreTargetInfo struct. -func (ilrti *ItemLevelRestoreTargetInfo) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "restoreCriteria": - if v != nil { - restoreCriteria, err := unmarshalBasicItemLevelRestoreCriteriaArray(*v) - if err != nil { - return err - } - ilrti.RestoreCriteria = &restoreCriteria - } - case "datasourceInfo": - if v != nil { - var datasourceInfo Datasource - err = json.Unmarshal(*v, &datasourceInfo) - if err != nil { - return err - } - ilrti.DatasourceInfo = &datasourceInfo - } - case "datasourceSetInfo": - if v != nil { - var datasourceSetInfo DatasourceSet - err = json.Unmarshal(*v, &datasourceSetInfo) - if err != nil { - return err - } - ilrti.DatasourceSetInfo = &datasourceSetInfo - } - case "datasourceAuthCredentials": - if v != nil { - datasourceAuthCredentials, err := unmarshalBasicAuthCredentials(*v) - if err != nil { - return err - } - ilrti.DatasourceAuthCredentials = datasourceAuthCredentials - } - case "recoveryOption": - if v != nil { - var recoveryOption string - err = json.Unmarshal(*v, &recoveryOption) - if err != nil { - return err - } - ilrti.RecoveryOption = &recoveryOption - } - case "restoreLocation": - if v != nil { - var restoreLocation string - err = json.Unmarshal(*v, &restoreLocation) - if err != nil { - return err - } - ilrti.RestoreLocation = &restoreLocation - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicRestoreTargetInfoBase - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - ilrti.ObjectType = objectType - } - } - } - - return nil -} - -// JobExtendedInfo extended Information about the job -type JobExtendedInfo struct { - // AdditionalDetails - Job's Additional Details - AdditionalDetails map[string]*string `json:"additionalDetails"` - // BackupInstanceState - READ-ONLY; State of the Backup Instance - BackupInstanceState *string `json:"backupInstanceState,omitempty"` - // DataTransferredInBytes - READ-ONLY; Number of bytes transferred - DataTransferredInBytes *float64 `json:"dataTransferredInBytes,omitempty"` - // RecoveryDestination - READ-ONLY; Destination where restore is done - RecoveryDestination *string `json:"recoveryDestination,omitempty"` - // SourceRecoverPoint - READ-ONLY; Details of the Source Recovery Point - SourceRecoverPoint *RestoreJobRecoveryPointDetails `json:"sourceRecoverPoint,omitempty"` - // SubTasks - READ-ONLY; List of Sub Tasks of the job - SubTasks *[]JobSubTask `json:"subTasks,omitempty"` - // TargetRecoverPoint - READ-ONLY; Details of the Target Recovery Point - TargetRecoverPoint *RestoreJobRecoveryPointDetails `json:"targetRecoverPoint,omitempty"` -} - -// MarshalJSON is the custom marshaler for JobExtendedInfo. -func (jei JobExtendedInfo) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if jei.AdditionalDetails != nil { - objectMap["additionalDetails"] = jei.AdditionalDetails - } - return json.Marshal(objectMap) -} - -// JobSubTask details of Job's Sub Task -type JobSubTask struct { - // AdditionalDetails - Additional details of Sub Tasks - AdditionalDetails map[string]*string `json:"additionalDetails"` - // TaskID - Task Id of the Sub Task - TaskID *int32 `json:"taskId,omitempty"` - // TaskName - Name of the Sub Task - TaskName *string `json:"taskName,omitempty"` - // TaskProgress - READ-ONLY; Progress of the Sub Task - TaskProgress *string `json:"taskProgress,omitempty"` - // TaskStatus - Status of the Sub Task - TaskStatus *string `json:"taskStatus,omitempty"` -} - -// MarshalJSON is the custom marshaler for JobSubTask. -func (jst JobSubTask) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if jst.AdditionalDetails != nil { - objectMap["additionalDetails"] = jst.AdditionalDetails - } - if jst.TaskID != nil { - objectMap["taskId"] = jst.TaskID - } - if jst.TaskName != nil { - objectMap["taskName"] = jst.TaskName - } - if jst.TaskStatus != nil { - objectMap["taskStatus"] = jst.TaskStatus - } - return json.Marshal(objectMap) -} - -// BasicOperationExtendedInfo operation Extended Info -type BasicOperationExtendedInfo interface { - AsOperationJobExtendedInfo() (*OperationJobExtendedInfo, bool) - AsOperationExtendedInfo() (*OperationExtendedInfo, bool) -} - -// OperationExtendedInfo operation Extended Info -type OperationExtendedInfo struct { - // ObjectType - Possible values include: 'ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo', 'ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo' - ObjectType ObjectTypeBasicOperationExtendedInfo `json:"objectType,omitempty"` -} - -func unmarshalBasicOperationExtendedInfo(body []byte) (BasicOperationExtendedInfo, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo): - var ojei OperationJobExtendedInfo - err := json.Unmarshal(body, &ojei) - return ojei, err - default: - var oei OperationExtendedInfo - err := json.Unmarshal(body, &oei) - return oei, err - } -} - -func unmarshalBasicOperationExtendedInfoArray(body []byte) ([]BasicOperationExtendedInfo, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - oeiArray := make([]BasicOperationExtendedInfo, len(rawMessages)) - - for index, rawMessage := range rawMessages { - oei, err := unmarshalBasicOperationExtendedInfo(*rawMessage) - if err != nil { - return nil, err - } - oeiArray[index] = oei - } - return oeiArray, nil -} - -// MarshalJSON is the custom marshaler for OperationExtendedInfo. -func (oei OperationExtendedInfo) MarshalJSON() ([]byte, error) { - oei.ObjectType = ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo - objectMap := make(map[string]interface{}) - if oei.ObjectType != "" { - objectMap["objectType"] = oei.ObjectType - } - return json.Marshal(objectMap) -} - -// AsOperationJobExtendedInfo is the BasicOperationExtendedInfo implementation for OperationExtendedInfo. -func (oei OperationExtendedInfo) AsOperationJobExtendedInfo() (*OperationJobExtendedInfo, bool) { - return nil, false -} - -// AsOperationExtendedInfo is the BasicOperationExtendedInfo implementation for OperationExtendedInfo. -func (oei OperationExtendedInfo) AsOperationExtendedInfo() (*OperationExtendedInfo, bool) { - return &oei, true -} - -// AsBasicOperationExtendedInfo is the BasicOperationExtendedInfo implementation for OperationExtendedInfo. -func (oei OperationExtendedInfo) AsBasicOperationExtendedInfo() (BasicOperationExtendedInfo, bool) { - return &oei, true -} - -// OperationJobExtendedInfo operation Job Extended Info -type OperationJobExtendedInfo struct { - autorest.Response `json:"-"` - // JobID - Arm Id of the job created for this operation. - JobID *string `json:"jobId,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicOperationExtendedInfoObjectTypeOperationExtendedInfo', 'ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo' - ObjectType ObjectTypeBasicOperationExtendedInfo `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for OperationJobExtendedInfo. -func (ojei OperationJobExtendedInfo) MarshalJSON() ([]byte, error) { - ojei.ObjectType = ObjectTypeBasicOperationExtendedInfoObjectTypeOperationJobExtendedInfo - objectMap := make(map[string]interface{}) - if ojei.JobID != nil { - objectMap["jobId"] = ojei.JobID - } - if ojei.ObjectType != "" { - objectMap["objectType"] = ojei.ObjectType - } - return json.Marshal(objectMap) -} - -// AsOperationJobExtendedInfo is the BasicOperationExtendedInfo implementation for OperationJobExtendedInfo. -func (ojei OperationJobExtendedInfo) AsOperationJobExtendedInfo() (*OperationJobExtendedInfo, bool) { - return &ojei, true -} - -// AsOperationExtendedInfo is the BasicOperationExtendedInfo implementation for OperationJobExtendedInfo. -func (ojei OperationJobExtendedInfo) AsOperationExtendedInfo() (*OperationExtendedInfo, bool) { - return nil, false -} - -// AsBasicOperationExtendedInfo is the BasicOperationExtendedInfo implementation for OperationJobExtendedInfo. -func (ojei OperationJobExtendedInfo) AsBasicOperationExtendedInfo() (BasicOperationExtendedInfo, bool) { - return &ojei, true -} - -// OperationResource operation Resource -type OperationResource struct { - autorest.Response `json:"-"` - // EndTime - End time of the operation - EndTime *date.Time `json:"endTime,omitempty"` - // Error - Required if status == failed or status == canceled. This is the OData v4 error format, used by the RPC and will go into the v2.2 Azure REST API guidelines. - // The full set of optional properties (e.g. inner errors / details) can be found in the "Error Response" section. - Error *Error `json:"error,omitempty"` - // ID - It should match what is used to GET the operation result - ID *string `json:"id,omitempty"` - // Name - It must match the last segment of the "id" field, and will typically be a GUID / system generated value - Name *string `json:"name,omitempty"` - // Properties - End time of the operation - Properties BasicOperationExtendedInfo `json:"properties,omitempty"` - // StartTime - Start time of the operation - StartTime *date.Time `json:"startTime,omitempty"` - Status *string `json:"status,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for OperationResource struct. -func (or *OperationResource) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "endTime": - if v != nil { - var endTime date.Time - err = json.Unmarshal(*v, &endTime) - if err != nil { - return err - } - or.EndTime = &endTime - } - case "error": - if v != nil { - var errorVar Error - err = json.Unmarshal(*v, &errorVar) - if err != nil { - return err - } - or.Error = &errorVar - } - case "id": - if v != nil { - var ID string - err = json.Unmarshal(*v, &ID) - if err != nil { - return err - } - or.ID = &ID - } - case "name": - if v != nil { - var name string - err = json.Unmarshal(*v, &name) - if err != nil { - return err - } - or.Name = &name - } - case "properties": - if v != nil { - properties, err := unmarshalBasicOperationExtendedInfo(*v) - if err != nil { - return err - } - or.Properties = properties - } - case "startTime": - if v != nil { - var startTime date.Time - err = json.Unmarshal(*v, &startTime) - if err != nil { - return err - } - or.StartTime = &startTime - } - case "status": - if v != nil { - var status string - err = json.Unmarshal(*v, &status) - if err != nil { - return err - } - or.Status = &status - } - } - } - - return nil -} - -// PatchResourceRequestInput patch Request content for Microsoft.DataProtection resources -type PatchResourceRequestInput struct { - // Identity - Input Managed Identity Details - Identity *DppIdentityDetails `json:"identity,omitempty"` - // Tags - Resource tags. - Tags map[string]*string `json:"tags"` -} - -// MarshalJSON is the custom marshaler for PatchResourceRequestInput. -func (prri PatchResourceRequestInput) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if prri.Identity != nil { - objectMap["identity"] = prri.Identity - } - if prri.Tags != nil { - objectMap["tags"] = prri.Tags - } - return json.Marshal(objectMap) -} - -// PolicyInfo policy Info in backupInstance -type PolicyInfo struct { - PolicyID *string `json:"policyId,omitempty"` - // PolicyVersion - READ-ONLY - PolicyVersion *string `json:"policyVersion,omitempty"` - // PolicyParameters - Policy parameters for the backup instance - PolicyParameters *PolicyParameters `json:"policyParameters,omitempty"` -} - -// MarshalJSON is the custom marshaler for PolicyInfo. -func (pi PolicyInfo) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if pi.PolicyID != nil { - objectMap["policyId"] = pi.PolicyID - } - if pi.PolicyParameters != nil { - objectMap["policyParameters"] = pi.PolicyParameters - } - return json.Marshal(objectMap) -} - -// PolicyParameters parameters in Policy -type PolicyParameters struct { - // DataStoreParametersList - Gets or sets the DataStore Parameters - DataStoreParametersList *[]BasicDataStoreParameters `json:"dataStoreParametersList,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for PolicyParameters struct. -func (pp *PolicyParameters) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "dataStoreParametersList": - if v != nil { - dataStoreParametersList, err := unmarshalBasicDataStoreParametersArray(*v) - if err != nil { - return err - } - pp.DataStoreParametersList = &dataStoreParametersList - } - } - } - - return nil -} - -// ProtectionStatusDetails protection status details -type ProtectionStatusDetails struct { - // ErrorDetails - Specifies the protection status error of the resource - ErrorDetails *UserFacingError `json:"errorDetails,omitempty"` - // Status - Specifies the protection status of the resource. Possible values include: 'StatusConfiguringProtection', 'StatusConfiguringProtectionFailed', 'StatusProtectionConfigured', 'StatusProtectionStopped', 'StatusSoftDeleted', 'StatusSoftDeleting' - Status Status `json:"status,omitempty"` -} - -// RangeBasedItemLevelRestoreCriteria item Level target info for restore operation -type RangeBasedItemLevelRestoreCriteria struct { - // MinMatchingValue - minimum value for range prefix match - MinMatchingValue *string `json:"minMatchingValue,omitempty"` - // MaxMatchingValue - maximum value for range prefix match - MaxMatchingValue *string `json:"maxMatchingValue,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeItemLevelRestoreCriteria', 'ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria' - ObjectType ObjectTypeBasicItemLevelRestoreCriteria `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for RangeBasedItemLevelRestoreCriteria. -func (rbilrc RangeBasedItemLevelRestoreCriteria) MarshalJSON() ([]byte, error) { - rbilrc.ObjectType = ObjectTypeBasicItemLevelRestoreCriteriaObjectTypeRangeBasedItemLevelRestoreCriteria - objectMap := make(map[string]interface{}) - if rbilrc.MinMatchingValue != nil { - objectMap["minMatchingValue"] = rbilrc.MinMatchingValue - } - if rbilrc.MaxMatchingValue != nil { - objectMap["maxMatchingValue"] = rbilrc.MaxMatchingValue - } - if rbilrc.ObjectType != "" { - objectMap["objectType"] = rbilrc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsRangeBasedItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for RangeBasedItemLevelRestoreCriteria. -func (rbilrc RangeBasedItemLevelRestoreCriteria) AsRangeBasedItemLevelRestoreCriteria() (*RangeBasedItemLevelRestoreCriteria, bool) { - return &rbilrc, true -} - -// AsItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for RangeBasedItemLevelRestoreCriteria. -func (rbilrc RangeBasedItemLevelRestoreCriteria) AsItemLevelRestoreCriteria() (*ItemLevelRestoreCriteria, bool) { - return nil, false -} - -// AsBasicItemLevelRestoreCriteria is the BasicItemLevelRestoreCriteria implementation for RangeBasedItemLevelRestoreCriteria. -func (rbilrc RangeBasedItemLevelRestoreCriteria) AsBasicItemLevelRestoreCriteria() (BasicItemLevelRestoreCriteria, bool) { - return &rbilrc, true -} - -// RecoveryPointDataStoreDetails recoveryPoint datastore details -type RecoveryPointDataStoreDetails struct { - CreationTime *date.Time `json:"creationTime,omitempty"` - ExpiryTime *date.Time `json:"expiryTime,omitempty"` - ID *string `json:"id,omitempty"` - MetaData *string `json:"metaData,omitempty"` - State *string `json:"state,omitempty"` - Type *string `json:"type,omitempty"` - Visible *bool `json:"visible,omitempty"` - // RehydrationExpiryTime - READ-ONLY - RehydrationExpiryTime *date.Time `json:"rehydrationExpiryTime,omitempty"` - // RehydrationStatus - READ-ONLY; Possible values include: 'RehydrationStatusCREATEINPROGRESS', 'RehydrationStatusCOMPLETED', 'RehydrationStatusDELETEINPROGRESS', 'RehydrationStatusDELETED', 'RehydrationStatusFAILED' - RehydrationStatus RehydrationStatus `json:"rehydrationStatus,omitempty"` -} - -// MarshalJSON is the custom marshaler for RecoveryPointDataStoreDetails. -func (rpdsd RecoveryPointDataStoreDetails) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if rpdsd.CreationTime != nil { - objectMap["creationTime"] = rpdsd.CreationTime - } - if rpdsd.ExpiryTime != nil { - objectMap["expiryTime"] = rpdsd.ExpiryTime - } - if rpdsd.ID != nil { - objectMap["id"] = rpdsd.ID - } - if rpdsd.MetaData != nil { - objectMap["metaData"] = rpdsd.MetaData - } - if rpdsd.State != nil { - objectMap["state"] = rpdsd.State - } - if rpdsd.Type != nil { - objectMap["type"] = rpdsd.Type - } - if rpdsd.Visible != nil { - objectMap["visible"] = rpdsd.Visible - } - return json.Marshal(objectMap) -} - -// RecoveryPointsFilters ... -type RecoveryPointsFilters struct { - RestorePointDataStoreID *string `json:"restorePointDataStoreId,omitempty"` - IsVisible *bool `json:"isVisible,omitempty"` - StartDate *string `json:"startDate,omitempty"` - EndDate *string `json:"endDate,omitempty"` - ExtendedInfo *bool `json:"extendedInfo,omitempty"` - RestorePointState *string `json:"restorePointState,omitempty"` -} - -// ResourceGuard ... -type ResourceGuard struct { - // ProvisioningState - READ-ONLY; Provisioning state of the BackupVault resource. Possible values include: 'ProvisioningStateFailed', 'ProvisioningStateProvisioning', 'ProvisioningStateSucceeded', 'ProvisioningStateUnknown', 'ProvisioningStateUpdating' - ProvisioningState ProvisioningState `json:"provisioningState,omitempty"` - // AllowAutoApprovals - READ-ONLY; This flag indicates whether auto approval is allowed or not. - AllowAutoApprovals *bool `json:"allowAutoApprovals,omitempty"` - // ResourceGuardOperations - READ-ONLY; {readonly} List of operation details those are protected by the ResourceGuard resource - ResourceGuardOperations *[]ResourceGuardOperation `json:"resourceGuardOperations,omitempty"` - // VaultCriticalOperationExclusionList - READ-ONLY; List of critical operations which are not protected by this resourceGuard - VaultCriticalOperationExclusionList *[]string `json:"vaultCriticalOperationExclusionList,omitempty"` - // Description - READ-ONLY; Description about the pre-req steps to perform all the critical operations. - Description *string `json:"description,omitempty"` -} - -// MarshalJSON is the custom marshaler for ResourceGuard. -func (rg ResourceGuard) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// ResourceGuardOperation this class contains all the details about a critical operation. -type ResourceGuardOperation struct { - // VaultCriticalOperation - READ-ONLY; Name of the critical operation. - VaultCriticalOperation *string `json:"vaultCriticalOperation,omitempty"` - // RequestResourceType - READ-ONLY; Type of resource request. - RequestResourceType *string `json:"requestResourceType,omitempty"` -} - -// MarshalJSON is the custom marshaler for ResourceGuardOperation. -func (rgo ResourceGuardOperation) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - return json.Marshal(objectMap) -} - -// ResourceGuardResource ... -type ResourceGuardResource struct { - autorest.Response `json:"-"` - // Properties - ResourceGuardResource properties - Properties *ResourceGuard `json:"properties,omitempty"` - // ETag - Optional ETag. - ETag *string `json:"eTag,omitempty"` - // ID - READ-ONLY; Resource Id represents the complete path to the resource. - ID *string `json:"id,omitempty"` - // Identity - Input Managed Identity Details - Identity *DppIdentityDetails `json:"identity,omitempty"` - // Location - Resource location. - Location *string `json:"location,omitempty"` - // Name - READ-ONLY; Resource name associated with the resource. - Name *string `json:"name,omitempty"` - // Tags - Resource tags. - Tags map[string]*string `json:"tags"` - // Type - READ-ONLY; Resource type represents the complete path of the form Namespace/ResourceType/ResourceType/... - Type *string `json:"type,omitempty"` - SystemData *SystemData `json:"systemData,omitempty"` -} - -// MarshalJSON is the custom marshaler for ResourceGuardResource. -func (rgr ResourceGuardResource) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if rgr.Properties != nil { - objectMap["properties"] = rgr.Properties - } - if rgr.ETag != nil { - objectMap["eTag"] = rgr.ETag - } - if rgr.Identity != nil { - objectMap["identity"] = rgr.Identity - } - if rgr.Location != nil { - objectMap["location"] = rgr.Location - } - if rgr.Tags != nil { - objectMap["tags"] = rgr.Tags - } - if rgr.SystemData != nil { - objectMap["systemData"] = rgr.SystemData - } - return json.Marshal(objectMap) -} - -// ResourceGuardResourceList list of ResourceGuard resources -type ResourceGuardResourceList struct { - autorest.Response `json:"-"` - // Value - List of resources. - Value *[]ResourceGuardResource `json:"value,omitempty"` - // NextLink - The uri to fetch the next page of resources. Call ListNext() fetches next page of resources. - NextLink *string `json:"nextLink,omitempty"` -} - -// ResourceGuardResourceListIterator provides access to a complete listing of ResourceGuardResource values. -type ResourceGuardResourceListIterator struct { - i int - page ResourceGuardResourceListPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *ResourceGuardResourceListIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardResourceListIterator.NextWithContext") - defer func() { - sc := -1 - if iter.Response().Response.Response != nil { - sc = iter.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - iter.i++ - if iter.i < len(iter.page.Values()) { - return nil - } - err = iter.page.NextWithContext(ctx) - if err != nil { - iter.i-- - return err - } - iter.i = 0 - return nil -} - -// Next advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (iter *ResourceGuardResourceListIterator) Next() error { - return iter.NextWithContext(context.Background()) -} - -// NotDone returns true if the enumeration should be started or is not yet complete. -func (iter ResourceGuardResourceListIterator) NotDone() bool { - return iter.page.NotDone() && iter.i < len(iter.page.Values()) -} - -// Response returns the raw server response from the last page request. -func (iter ResourceGuardResourceListIterator) Response() ResourceGuardResourceList { - return iter.page.Response() -} - -// Value returns the current value or a zero-initialized value if the -// iterator has advanced beyond the end of the collection. -func (iter ResourceGuardResourceListIterator) Value() ResourceGuardResource { - if !iter.page.NotDone() { - return ResourceGuardResource{} - } - return iter.page.Values()[iter.i] -} - -// Creates a new instance of the ResourceGuardResourceListIterator type. -func NewResourceGuardResourceListIterator(page ResourceGuardResourceListPage) ResourceGuardResourceListIterator { - return ResourceGuardResourceListIterator{page: page} -} - -// IsEmpty returns true if the ListResult contains no values. -func (rgrl ResourceGuardResourceList) IsEmpty() bool { - return rgrl.Value == nil || len(*rgrl.Value) == 0 -} - -// hasNextLink returns true if the NextLink is not empty. -func (rgrl ResourceGuardResourceList) hasNextLink() bool { - return rgrl.NextLink != nil && len(*rgrl.NextLink) != 0 -} - -// resourceGuardResourceListPreparer prepares a request to retrieve the next set of results. -// It returns nil if no more results exist. -func (rgrl ResourceGuardResourceList) resourceGuardResourceListPreparer(ctx context.Context) (*http.Request, error) { - if !rgrl.hasNextLink() { - return nil, nil - } - return autorest.Prepare((&http.Request{}).WithContext(ctx), - autorest.AsJSON(), - autorest.AsGet(), - autorest.WithBaseURL(to.String(rgrl.NextLink))) -} - -// ResourceGuardResourceListPage contains a page of ResourceGuardResource values. -type ResourceGuardResourceListPage struct { - fn func(context.Context, ResourceGuardResourceList) (ResourceGuardResourceList, error) - rgrl ResourceGuardResourceList -} - -// NextWithContext advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -func (page *ResourceGuardResourceListPage) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardResourceListPage.NextWithContext") - defer func() { - sc := -1 - if page.Response().Response.Response != nil { - sc = page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - for { - next, err := page.fn(ctx, page.rgrl) - if err != nil { - return err - } - page.rgrl = next - if !next.hasNextLink() || !next.IsEmpty() { - break - } - } - return nil -} - -// Next advances to the next page of values. If there was an error making -// the request the page does not advance and the error is returned. -// Deprecated: Use NextWithContext() instead. -func (page *ResourceGuardResourceListPage) Next() error { - return page.NextWithContext(context.Background()) -} - -// NotDone returns true if the page enumeration should be started or is not yet complete. -func (page ResourceGuardResourceListPage) NotDone() bool { - return !page.rgrl.IsEmpty() -} - -// Response returns the raw server response from the last page request. -func (page ResourceGuardResourceListPage) Response() ResourceGuardResourceList { - return page.rgrl -} - -// Values returns the slice of values for the current page or nil if there are no values. -func (page ResourceGuardResourceListPage) Values() []ResourceGuardResource { - if page.rgrl.IsEmpty() { - return nil - } - return *page.rgrl.Value -} - -// Creates a new instance of the ResourceGuardResourceListPage type. -func NewResourceGuardResourceListPage(cur ResourceGuardResourceList, getNextPage func(context.Context, ResourceGuardResourceList) (ResourceGuardResourceList, error)) ResourceGuardResourceListPage { - return ResourceGuardResourceListPage{ - fn: getNextPage, - rgrl: cur, - } -} - -// ResourceMoveDetails resourceMoveDetails will be returned in response to GetResource call from ARM -type ResourceMoveDetails struct { - // OperationID - CorrelationId of latest ResourceMove operation attempted - OperationID *string `json:"operationId,omitempty"` - // StartTimeUtc - Start time in UTC of latest ResourceMove operation attempted. ISO 8601 format. - StartTimeUtc *string `json:"startTimeUtc,omitempty"` - // CompletionTimeUtc - Completion time in UTC of latest ResourceMove operation attempted. ISO 8601 format. - CompletionTimeUtc *string `json:"completionTimeUtc,omitempty"` - // SourceResourcePath - ARM resource path of source resource - SourceResourcePath *string `json:"sourceResourcePath,omitempty"` - // TargetResourcePath - ARM resource path of target resource used in latest ResourceMove operation - TargetResourcePath *string `json:"targetResourcePath,omitempty"` -} - -// RestorableTimeRange ... -type RestorableTimeRange struct { - // StartTime - Start time for the available restore range - StartTime *string `json:"startTime,omitempty"` - // EndTime - End time for the available restore range - EndTime *string `json:"endTime,omitempty"` - ObjectType *string `json:"objectType,omitempty"` -} - -// RestoreFilesTargetInfo class encapsulating restore as files target parameters -type RestoreFilesTargetInfo struct { - // TargetDetails - Destination of RestoreAsFiles operation, when destination is not a datasource - TargetDetails *TargetDetails `json:"targetDetails,omitempty"` - // RecoveryOption - Recovery Option - RecoveryOption *string `json:"recoveryOption,omitempty"` - // RestoreLocation - Target Restore region - RestoreLocation *string `json:"restoreLocation,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo' - ObjectType ObjectTypeBasicRestoreTargetInfoBase `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) MarshalJSON() ([]byte, error) { - rfti.ObjectType = ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo - objectMap := make(map[string]interface{}) - if rfti.TargetDetails != nil { - objectMap["targetDetails"] = rfti.TargetDetails - } - if rfti.RecoveryOption != nil { - objectMap["recoveryOption"] = rfti.RecoveryOption - } - if rfti.RestoreLocation != nil { - objectMap["restoreLocation"] = rfti.RestoreLocation - } - if rfti.ObjectType != "" { - objectMap["objectType"] = rfti.ObjectType - } - return json.Marshal(objectMap) -} - -// AsItemLevelRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) AsItemLevelRestoreTargetInfo() (*ItemLevelRestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreFilesTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) AsRestoreFilesTargetInfo() (*RestoreFilesTargetInfo, bool) { - return &rfti, true -} - -// AsRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) AsRestoreTargetInfo() (*RestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) AsRestoreTargetInfoBase() (*RestoreTargetInfoBase, bool) { - return nil, false -} - -// AsBasicRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreFilesTargetInfo. -func (rfti RestoreFilesTargetInfo) AsBasicRestoreTargetInfoBase() (BasicRestoreTargetInfoBase, bool) { - return &rfti, true -} - -// RestoreJobRecoveryPointDetails ... -type RestoreJobRecoveryPointDetails struct { - RecoveryPointID *string `json:"recoveryPointID,omitempty"` - RecoveryPointTime *date.Time `json:"recoveryPointTime,omitempty"` -} - -// RestoreTargetInfo class encapsulating restore target parameters -type RestoreTargetInfo struct { - // DatasourceInfo - Information of target DS - DatasourceInfo *Datasource `json:"datasourceInfo,omitempty"` - // DatasourceSetInfo - Information of target DS Set - DatasourceSetInfo *DatasourceSet `json:"datasourceSetInfo,omitempty"` - // DatasourceAuthCredentials - Credentials to use to authenticate with data source provider. - DatasourceAuthCredentials BasicAuthCredentials `json:"datasourceAuthCredentials,omitempty"` - // RecoveryOption - Recovery Option - RecoveryOption *string `json:"recoveryOption,omitempty"` - // RestoreLocation - Target Restore region - RestoreLocation *string `json:"restoreLocation,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo' - ObjectType ObjectTypeBasicRestoreTargetInfoBase `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for RestoreTargetInfo. -func (rti RestoreTargetInfo) MarshalJSON() ([]byte, error) { - rti.ObjectType = ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo - objectMap := make(map[string]interface{}) - if rti.DatasourceInfo != nil { - objectMap["datasourceInfo"] = rti.DatasourceInfo - } - if rti.DatasourceSetInfo != nil { - objectMap["datasourceSetInfo"] = rti.DatasourceSetInfo - } - objectMap["datasourceAuthCredentials"] = rti.DatasourceAuthCredentials - if rti.RecoveryOption != nil { - objectMap["recoveryOption"] = rti.RecoveryOption - } - if rti.RestoreLocation != nil { - objectMap["restoreLocation"] = rti.RestoreLocation - } - if rti.ObjectType != "" { - objectMap["objectType"] = rti.ObjectType - } - return json.Marshal(objectMap) -} - -// AsItemLevelRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfo. -func (rti RestoreTargetInfo) AsItemLevelRestoreTargetInfo() (*ItemLevelRestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreFilesTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfo. -func (rti RestoreTargetInfo) AsRestoreFilesTargetInfo() (*RestoreFilesTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfo. -func (rti RestoreTargetInfo) AsRestoreTargetInfo() (*RestoreTargetInfo, bool) { - return &rti, true -} - -// AsRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfo. -func (rti RestoreTargetInfo) AsRestoreTargetInfoBase() (*RestoreTargetInfoBase, bool) { - return nil, false -} - -// AsBasicRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfo. -func (rti RestoreTargetInfo) AsBasicRestoreTargetInfoBase() (BasicRestoreTargetInfoBase, bool) { - return &rti, true -} - -// UnmarshalJSON is the custom unmarshaler for RestoreTargetInfo struct. -func (rti *RestoreTargetInfo) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "datasourceInfo": - if v != nil { - var datasourceInfo Datasource - err = json.Unmarshal(*v, &datasourceInfo) - if err != nil { - return err - } - rti.DatasourceInfo = &datasourceInfo - } - case "datasourceSetInfo": - if v != nil { - var datasourceSetInfo DatasourceSet - err = json.Unmarshal(*v, &datasourceSetInfo) - if err != nil { - return err - } - rti.DatasourceSetInfo = &datasourceSetInfo - } - case "datasourceAuthCredentials": - if v != nil { - datasourceAuthCredentials, err := unmarshalBasicAuthCredentials(*v) - if err != nil { - return err - } - rti.DatasourceAuthCredentials = datasourceAuthCredentials - } - case "recoveryOption": - if v != nil { - var recoveryOption string - err = json.Unmarshal(*v, &recoveryOption) - if err != nil { - return err - } - rti.RecoveryOption = &recoveryOption - } - case "restoreLocation": - if v != nil { - var restoreLocation string - err = json.Unmarshal(*v, &restoreLocation) - if err != nil { - return err - } - rti.RestoreLocation = &restoreLocation - } - case "objectType": - if v != nil { - var objectType ObjectTypeBasicRestoreTargetInfoBase - err = json.Unmarshal(*v, &objectType) - if err != nil { - return err - } - rti.ObjectType = objectType - } - } - } - - return nil -} - -// BasicRestoreTargetInfoBase base class common to RestoreTargetInfo and RestoreFilesTargetInfo -type BasicRestoreTargetInfoBase interface { - AsItemLevelRestoreTargetInfo() (*ItemLevelRestoreTargetInfo, bool) - AsRestoreFilesTargetInfo() (*RestoreFilesTargetInfo, bool) - AsRestoreTargetInfo() (*RestoreTargetInfo, bool) - AsRestoreTargetInfoBase() (*RestoreTargetInfoBase, bool) -} - -// RestoreTargetInfoBase base class common to RestoreTargetInfo and RestoreFilesTargetInfo -type RestoreTargetInfoBase struct { - // RecoveryOption - Recovery Option - RecoveryOption *string `json:"recoveryOption,omitempty"` - // RestoreLocation - Target Restore region - RestoreLocation *string `json:"restoreLocation,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo', 'ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo' - ObjectType ObjectTypeBasicRestoreTargetInfoBase `json:"objectType,omitempty"` -} - -func unmarshalBasicRestoreTargetInfoBase(body []byte) (BasicRestoreTargetInfoBase, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicRestoreTargetInfoBaseObjectTypeItemLevelRestoreTargetInfo): - var ilrti ItemLevelRestoreTargetInfo - err := json.Unmarshal(body, &ilrti) - return ilrti, err - case string(ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreFilesTargetInfo): - var rfti RestoreFilesTargetInfo - err := json.Unmarshal(body, &rfti) - return rfti, err - case string(ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfo): - var rti RestoreTargetInfo - err := json.Unmarshal(body, &rti) - return rti, err - default: - var rtib RestoreTargetInfoBase - err := json.Unmarshal(body, &rtib) - return rtib, err - } -} - -func unmarshalBasicRestoreTargetInfoBaseArray(body []byte) ([]BasicRestoreTargetInfoBase, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - rtibArray := make([]BasicRestoreTargetInfoBase, len(rawMessages)) - - for index, rawMessage := range rawMessages { - rtib, err := unmarshalBasicRestoreTargetInfoBase(*rawMessage) - if err != nil { - return nil, err - } - rtibArray[index] = rtib - } - return rtibArray, nil -} - -// MarshalJSON is the custom marshaler for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) MarshalJSON() ([]byte, error) { - rtib.ObjectType = ObjectTypeBasicRestoreTargetInfoBaseObjectTypeRestoreTargetInfoBase - objectMap := make(map[string]interface{}) - if rtib.RecoveryOption != nil { - objectMap["recoveryOption"] = rtib.RecoveryOption - } - if rtib.RestoreLocation != nil { - objectMap["restoreLocation"] = rtib.RestoreLocation - } - if rtib.ObjectType != "" { - objectMap["objectType"] = rtib.ObjectType - } - return json.Marshal(objectMap) -} - -// AsItemLevelRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) AsItemLevelRestoreTargetInfo() (*ItemLevelRestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreFilesTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) AsRestoreFilesTargetInfo() (*RestoreFilesTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfo is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) AsRestoreTargetInfo() (*RestoreTargetInfo, bool) { - return nil, false -} - -// AsRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) AsRestoreTargetInfoBase() (*RestoreTargetInfoBase, bool) { - return &rtib, true -} - -// AsBasicRestoreTargetInfoBase is the BasicRestoreTargetInfoBase implementation for RestoreTargetInfoBase. -func (rtib RestoreTargetInfoBase) AsBasicRestoreTargetInfoBase() (BasicRestoreTargetInfoBase, bool) { - return &rtib, true -} - -// RetentionTag retention tag -type RetentionTag struct { - // ETag - READ-ONLY; Retention Tag version. - ETag *string `json:"eTag,omitempty"` - // ID - READ-ONLY; Retention Tag version. - ID *string `json:"id,omitempty"` - // TagName - Retention Tag Name to relate it to retention rule. - TagName *string `json:"tagName,omitempty"` -} - -// MarshalJSON is the custom marshaler for RetentionTag. -func (rt RetentionTag) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if rt.TagName != nil { - objectMap["tagName"] = rt.TagName - } - return json.Marshal(objectMap) -} - -// ScheduleBasedBackupCriteria schedule based backup criteria -type ScheduleBasedBackupCriteria struct { - // AbsoluteCriteria - it contains absolute values like "AllBackup" / "FirstOfDay" / "FirstOfWeek" / "FirstOfMonth" - // and should be part of AbsoluteMarker enum - AbsoluteCriteria *[]AbsoluteMarker `json:"absoluteCriteria,omitempty"` - // DaysOfMonth - This is day of the month from 1 to 28 other wise last of month - DaysOfMonth *[]Day `json:"daysOfMonth,omitempty"` - // DaysOfTheWeek - It should be Sunday/Monday/T..../Saturday - DaysOfTheWeek *[]DayOfWeek `json:"daysOfTheWeek,omitempty"` - // MonthsOfYear - It should be January/February/....../December - MonthsOfYear *[]Month `json:"monthsOfYear,omitempty"` - // ScheduleTimes - List of schedule times for backup - ScheduleTimes *[]date.Time `json:"scheduleTimes,omitempty"` - // WeeksOfTheMonth - It should be First/Second/Third/Fourth/Last - WeeksOfTheMonth *[]WeekNumber `json:"weeksOfTheMonth,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicBackupCriteriaObjectTypeBackupCriteria', 'ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria' - ObjectType ObjectTypeBasicBackupCriteria `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for ScheduleBasedBackupCriteria. -func (sbbc ScheduleBasedBackupCriteria) MarshalJSON() ([]byte, error) { - sbbc.ObjectType = ObjectTypeBasicBackupCriteriaObjectTypeScheduleBasedBackupCriteria - objectMap := make(map[string]interface{}) - if sbbc.AbsoluteCriteria != nil { - objectMap["absoluteCriteria"] = sbbc.AbsoluteCriteria - } - if sbbc.DaysOfMonth != nil { - objectMap["daysOfMonth"] = sbbc.DaysOfMonth - } - if sbbc.DaysOfTheWeek != nil { - objectMap["daysOfTheWeek"] = sbbc.DaysOfTheWeek - } - if sbbc.MonthsOfYear != nil { - objectMap["monthsOfYear"] = sbbc.MonthsOfYear - } - if sbbc.ScheduleTimes != nil { - objectMap["scheduleTimes"] = sbbc.ScheduleTimes - } - if sbbc.WeeksOfTheMonth != nil { - objectMap["weeksOfTheMonth"] = sbbc.WeeksOfTheMonth - } - if sbbc.ObjectType != "" { - objectMap["objectType"] = sbbc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsScheduleBasedBackupCriteria is the BasicBackupCriteria implementation for ScheduleBasedBackupCriteria. -func (sbbc ScheduleBasedBackupCriteria) AsScheduleBasedBackupCriteria() (*ScheduleBasedBackupCriteria, bool) { - return &sbbc, true -} - -// AsBackupCriteria is the BasicBackupCriteria implementation for ScheduleBasedBackupCriteria. -func (sbbc ScheduleBasedBackupCriteria) AsBackupCriteria() (*BackupCriteria, bool) { - return nil, false -} - -// AsBasicBackupCriteria is the BasicBackupCriteria implementation for ScheduleBasedBackupCriteria. -func (sbbc ScheduleBasedBackupCriteria) AsBasicBackupCriteria() (BasicBackupCriteria, bool) { - return &sbbc, true -} - -// ScheduleBasedTriggerContext schedule based trigger context -type ScheduleBasedTriggerContext struct { - // Schedule - Schedule for this backup - Schedule *BackupSchedule `json:"schedule,omitempty"` - // TaggingCriteria - List of tags that can be applicable for given schedule. - TaggingCriteria *[]TaggingCriteria `json:"taggingCriteria,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeBasicTriggerContextObjectTypeTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext' - ObjectType ObjectTypeBasicTriggerContext `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for ScheduleBasedTriggerContext. -func (sbtc ScheduleBasedTriggerContext) MarshalJSON() ([]byte, error) { - sbtc.ObjectType = ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext - objectMap := make(map[string]interface{}) - if sbtc.Schedule != nil { - objectMap["schedule"] = sbtc.Schedule - } - if sbtc.TaggingCriteria != nil { - objectMap["taggingCriteria"] = sbtc.TaggingCriteria - } - if sbtc.ObjectType != "" { - objectMap["objectType"] = sbtc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAdhocBasedTriggerContext is the BasicTriggerContext implementation for ScheduleBasedTriggerContext. -func (sbtc ScheduleBasedTriggerContext) AsAdhocBasedTriggerContext() (*AdhocBasedTriggerContext, bool) { - return nil, false -} - -// AsScheduleBasedTriggerContext is the BasicTriggerContext implementation for ScheduleBasedTriggerContext. -func (sbtc ScheduleBasedTriggerContext) AsScheduleBasedTriggerContext() (*ScheduleBasedTriggerContext, bool) { - return &sbtc, true -} - -// AsTriggerContext is the BasicTriggerContext implementation for ScheduleBasedTriggerContext. -func (sbtc ScheduleBasedTriggerContext) AsTriggerContext() (*TriggerContext, bool) { - return nil, false -} - -// AsBasicTriggerContext is the BasicTriggerContext implementation for ScheduleBasedTriggerContext. -func (sbtc ScheduleBasedTriggerContext) AsBasicTriggerContext() (BasicTriggerContext, bool) { - return &sbtc, true -} - -// SecretStoreBasedAuthCredentials secret store based authentication credentials. -type SecretStoreBasedAuthCredentials struct { - // SecretStoreResource - Secret store resource - SecretStoreResource *SecretStoreResource `json:"secretStoreResource,omitempty"` - // ObjectType - Possible values include: 'ObjectTypeAuthCredentials', 'ObjectTypeSecretStoreBasedAuthCredentials' - ObjectType ObjectType `json:"objectType,omitempty"` -} - -// MarshalJSON is the custom marshaler for SecretStoreBasedAuthCredentials. -func (ssbac SecretStoreBasedAuthCredentials) MarshalJSON() ([]byte, error) { - ssbac.ObjectType = ObjectTypeSecretStoreBasedAuthCredentials - objectMap := make(map[string]interface{}) - if ssbac.SecretStoreResource != nil { - objectMap["secretStoreResource"] = ssbac.SecretStoreResource - } - if ssbac.ObjectType != "" { - objectMap["objectType"] = ssbac.ObjectType - } - return json.Marshal(objectMap) -} - -// AsSecretStoreBasedAuthCredentials is the BasicAuthCredentials implementation for SecretStoreBasedAuthCredentials. -func (ssbac SecretStoreBasedAuthCredentials) AsSecretStoreBasedAuthCredentials() (*SecretStoreBasedAuthCredentials, bool) { - return &ssbac, true -} - -// AsAuthCredentials is the BasicAuthCredentials implementation for SecretStoreBasedAuthCredentials. -func (ssbac SecretStoreBasedAuthCredentials) AsAuthCredentials() (*AuthCredentials, bool) { - return nil, false -} - -// AsBasicAuthCredentials is the BasicAuthCredentials implementation for SecretStoreBasedAuthCredentials. -func (ssbac SecretStoreBasedAuthCredentials) AsBasicAuthCredentials() (BasicAuthCredentials, bool) { - return &ssbac, true -} - -// SecretStoreResource class representing a secret store resource. -type SecretStoreResource struct { - // URI - Uri to get to the resource - URI *string `json:"uri,omitempty"` - // SecretStoreType - Gets or sets the type of secret store. Possible values include: 'SecretStoreTypeInvalid', 'SecretStoreTypeAzureKeyVault' - SecretStoreType SecretStoreType `json:"secretStoreType,omitempty"` -} - -// SourceLifeCycle source LifeCycle -type SourceLifeCycle struct { - DeleteAfter BasicDeleteOption `json:"deleteAfter,omitempty"` - SourceDataStore *DataStoreInfoBase `json:"sourceDataStore,omitempty"` - TargetDataStoreCopySettings *[]TargetCopySetting `json:"targetDataStoreCopySettings,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for SourceLifeCycle struct. -func (slc *SourceLifeCycle) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "deleteAfter": - if v != nil { - deleteAfter, err := unmarshalBasicDeleteOption(*v) - if err != nil { - return err - } - slc.DeleteAfter = deleteAfter - } - case "sourceDataStore": - if v != nil { - var sourceDataStore DataStoreInfoBase - err = json.Unmarshal(*v, &sourceDataStore) - if err != nil { - return err - } - slc.SourceDataStore = &sourceDataStore - } - case "targetDataStoreCopySettings": - if v != nil { - var targetDataStoreCopySettings []TargetCopySetting - err = json.Unmarshal(*v, &targetDataStoreCopySettings) - if err != nil { - return err - } - slc.TargetDataStoreCopySettings = &targetDataStoreCopySettings - } - } - } - - return nil -} - -// StorageSetting storage setting -type StorageSetting struct { - // DatastoreType - Gets or sets the type of the datastore. Possible values include: 'StorageSettingStoreTypesArchiveStore', 'StorageSettingStoreTypesSnapshotStore', 'StorageSettingStoreTypesVaultStore' - DatastoreType StorageSettingStoreTypes `json:"datastoreType,omitempty"` - // Type - Gets or sets the type. Possible values include: 'StorageSettingTypesGeoRedundant', 'StorageSettingTypesLocallyRedundant' - Type StorageSettingTypes `json:"type,omitempty"` -} - -// SupportedFeature elements class for feature request -type SupportedFeature struct { - // FeatureName - support feature type. - FeatureName *string `json:"featureName,omitempty"` - // SupportStatus - feature support status. Possible values include: 'FeatureSupportStatusInvalid', 'FeatureSupportStatusNotSupported', 'FeatureSupportStatusAlphaPreview', 'FeatureSupportStatusPrivatePreview', 'FeatureSupportStatusPublicPreview', 'FeatureSupportStatusGenerallyAvailable' - SupportStatus FeatureSupportStatus `json:"supportStatus,omitempty"` - // ExposureControlledFeatures - support feature type. - ExposureControlledFeatures *[]string `json:"exposureControlledFeatures,omitempty"` -} - -// SystemData metadata pertaining to creation and last modification of the resource. -type SystemData struct { - // CreatedBy - The identity that created the resource. - CreatedBy *string `json:"createdBy,omitempty"` - // CreatedByType - The type of identity that created the resource. Possible values include: 'CreatedByTypeUser', 'CreatedByTypeApplication', 'CreatedByTypeManagedIdentity', 'CreatedByTypeKey' - CreatedByType CreatedByType `json:"createdByType,omitempty"` - // CreatedAt - The timestamp of resource creation (UTC). - CreatedAt *date.Time `json:"createdAt,omitempty"` - // LastModifiedBy - The identity that last modified the resource. - LastModifiedBy *string `json:"lastModifiedBy,omitempty"` - // LastModifiedByType - The type of identity that last modified the resource. Possible values include: 'CreatedByTypeUser', 'CreatedByTypeApplication', 'CreatedByTypeManagedIdentity', 'CreatedByTypeKey' - LastModifiedByType CreatedByType `json:"lastModifiedByType,omitempty"` - // LastModifiedAt - The type of identity that last modified the resource. - LastModifiedAt *date.Time `json:"lastModifiedAt,omitempty"` -} - -// TaggingCriteria tagging criteria -type TaggingCriteria struct { - // Criteria - Criteria which decides whether the tag can be applied to a triggered backup. - Criteria *[]BasicBackupCriteria `json:"criteria,omitempty"` - // IsDefault - Specifies if tag is default. - IsDefault *bool `json:"isDefault,omitempty"` - // TaggingPriority - Retention Tag priority. - TaggingPriority *int64 `json:"taggingPriority,omitempty"` - // TagInfo - Retention tag information - TagInfo *RetentionTag `json:"tagInfo,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for TaggingCriteria struct. -func (tc *TaggingCriteria) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "criteria": - if v != nil { - criteria, err := unmarshalBasicBackupCriteriaArray(*v) - if err != nil { - return err - } - tc.Criteria = &criteria - } - case "isDefault": - if v != nil { - var isDefault bool - err = json.Unmarshal(*v, &isDefault) - if err != nil { - return err - } - tc.IsDefault = &isDefault - } - case "taggingPriority": - if v != nil { - var taggingPriority int64 - err = json.Unmarshal(*v, &taggingPriority) - if err != nil { - return err - } - tc.TaggingPriority = &taggingPriority - } - case "tagInfo": - if v != nil { - var tagInfo RetentionTag - err = json.Unmarshal(*v, &tagInfo) - if err != nil { - return err - } - tc.TagInfo = &tagInfo - } - } - } - - return nil -} - -// TargetCopySetting target copy settings -type TargetCopySetting struct { - // CopyAfter - It can be CustomCopyOption or ImmediateCopyOption. - CopyAfter BasicCopyOption `json:"copyAfter,omitempty"` - // DataStore - Info of target datastore - DataStore *DataStoreInfoBase `json:"dataStore,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for TargetCopySetting struct. -func (tcs *TargetCopySetting) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "copyAfter": - if v != nil { - copyAfter, err := unmarshalBasicCopyOption(*v) - if err != nil { - return err - } - tcs.CopyAfter = copyAfter - } - case "dataStore": - if v != nil { - var dataStore DataStoreInfoBase - err = json.Unmarshal(*v, &dataStore) - if err != nil { - return err - } - tcs.DataStore = &dataStore - } - } - } - - return nil -} - -// TargetDetails class encapsulating target details, used where the destination is not a datasource -type TargetDetails struct { - // FilePrefix - Restore operation may create multiple files inside location pointed by Url - // Below will be the common prefix for all of them - FilePrefix *string `json:"filePrefix,omitempty"` - // RestoreTargetLocationType - Denotes the target location where the data will be restored, - // string value for the enum {Microsoft.Internal.AzureBackup.DataProtection.Common.Interface.RestoreTargetLocationType}. Possible values include: 'RestoreTargetLocationTypeInvalid', 'RestoreTargetLocationTypeAzureBlobs', 'RestoreTargetLocationTypeAzureFiles' - RestoreTargetLocationType RestoreTargetLocationType `json:"restoreTargetLocationType,omitempty"` - // URL - Url denoting the restore destination. It can point to container / file share etc - URL *string `json:"url,omitempty"` -} - -// TriggerBackupRequest trigger backup request -type TriggerBackupRequest struct { - // BackupRuleOptions - Name for the Rule of the Policy which needs to be applied for this backup - BackupRuleOptions *AdHocBackupRuleOptions `json:"backupRuleOptions,omitempty"` -} - -// BasicTriggerContext trigger context -type BasicTriggerContext interface { - AsAdhocBasedTriggerContext() (*AdhocBasedTriggerContext, bool) - AsScheduleBasedTriggerContext() (*ScheduleBasedTriggerContext, bool) - AsTriggerContext() (*TriggerContext, bool) -} - -// TriggerContext trigger context -type TriggerContext struct { - // ObjectType - Possible values include: 'ObjectTypeBasicTriggerContextObjectTypeTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext', 'ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext' - ObjectType ObjectTypeBasicTriggerContext `json:"objectType,omitempty"` -} - -func unmarshalBasicTriggerContext(body []byte) (BasicTriggerContext, error) { - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - - switch m["objectType"] { - case string(ObjectTypeBasicTriggerContextObjectTypeAdhocBasedTriggerContext): - var abtc AdhocBasedTriggerContext - err := json.Unmarshal(body, &abtc) - return abtc, err - case string(ObjectTypeBasicTriggerContextObjectTypeScheduleBasedTriggerContext): - var sbtc ScheduleBasedTriggerContext - err := json.Unmarshal(body, &sbtc) - return sbtc, err - default: - var tc TriggerContext - err := json.Unmarshal(body, &tc) - return tc, err - } -} - -func unmarshalBasicTriggerContextArray(body []byte) ([]BasicTriggerContext, error) { - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - - tcArray := make([]BasicTriggerContext, len(rawMessages)) - - for index, rawMessage := range rawMessages { - tc, err := unmarshalBasicTriggerContext(*rawMessage) - if err != nil { - return nil, err - } - tcArray[index] = tc - } - return tcArray, nil -} - -// MarshalJSON is the custom marshaler for TriggerContext. -func (tc TriggerContext) MarshalJSON() ([]byte, error) { - tc.ObjectType = ObjectTypeBasicTriggerContextObjectTypeTriggerContext - objectMap := make(map[string]interface{}) - if tc.ObjectType != "" { - objectMap["objectType"] = tc.ObjectType - } - return json.Marshal(objectMap) -} - -// AsAdhocBasedTriggerContext is the BasicTriggerContext implementation for TriggerContext. -func (tc TriggerContext) AsAdhocBasedTriggerContext() (*AdhocBasedTriggerContext, bool) { - return nil, false -} - -// AsScheduleBasedTriggerContext is the BasicTriggerContext implementation for TriggerContext. -func (tc TriggerContext) AsScheduleBasedTriggerContext() (*ScheduleBasedTriggerContext, bool) { - return nil, false -} - -// AsTriggerContext is the BasicTriggerContext implementation for TriggerContext. -func (tc TriggerContext) AsTriggerContext() (*TriggerContext, bool) { - return &tc, true -} - -// AsBasicTriggerContext is the BasicTriggerContext implementation for TriggerContext. -func (tc TriggerContext) AsBasicTriggerContext() (BasicTriggerContext, bool) { - return &tc, true -} - -// UserFacingError error object used by layers that have access to localized content, and propagate that to -// user -type UserFacingError struct { - // Code - Unique code for this error - Code *string `json:"code,omitempty"` - // Details - Additional related Errors - Details *[]UserFacingError `json:"details,omitempty"` - // InnerError - Inner Error - InnerError *InnerError `json:"innerError,omitempty"` - // IsRetryable - Whether the operation will be retryable or not - IsRetryable *bool `json:"isRetryable,omitempty"` - // IsUserError - Whether the operation is due to a user error or service error - IsUserError *bool `json:"isUserError,omitempty"` - // Properties - Any key value pairs that can be injected inside error object - Properties map[string]*string `json:"properties"` - Message *string `json:"message,omitempty"` - // RecommendedAction - RecommendedAction � localized. - RecommendedAction *[]string `json:"recommendedAction,omitempty"` - // Target - Target of the error. - Target *string `json:"target,omitempty"` -} - -// MarshalJSON is the custom marshaler for UserFacingError. -func (ufe UserFacingError) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if ufe.Code != nil { - objectMap["code"] = ufe.Code - } - if ufe.Details != nil { - objectMap["details"] = ufe.Details - } - if ufe.InnerError != nil { - objectMap["innerError"] = ufe.InnerError - } - if ufe.IsRetryable != nil { - objectMap["isRetryable"] = ufe.IsRetryable - } - if ufe.IsUserError != nil { - objectMap["isUserError"] = ufe.IsUserError - } - if ufe.Properties != nil { - objectMap["properties"] = ufe.Properties - } - if ufe.Message != nil { - objectMap["message"] = ufe.Message - } - if ufe.RecommendedAction != nil { - objectMap["recommendedAction"] = ufe.RecommendedAction - } - if ufe.Target != nil { - objectMap["target"] = ufe.Target - } - return json.Marshal(objectMap) -} - -// ValidateForBackupRequest validate for backup request -type ValidateForBackupRequest struct { - BackupInstance *BackupInstance `json:"backupInstance,omitempty"` -} - -// ValidateRestoreRequestObject validate restore request object -type ValidateRestoreRequestObject struct { - // RestoreRequestObject - Gets or sets the restore request object. - RestoreRequestObject BasicAzureBackupRestoreRequest `json:"restoreRequestObject,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for ValidateRestoreRequestObject struct. -func (vrro *ValidateRestoreRequestObject) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - case "restoreRequestObject": - if v != nil { - restoreRequestObject, err := unmarshalBasicAzureBackupRestoreRequest(*v) - if err != nil { - return err - } - vrro.RestoreRequestObject = restoreRequestObject - } - } - } - - return nil -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/operationresult.go b/internal/services/dataprotection/legacysdk/dataprotection/operationresult.go deleted file mode 100644 index f7a730cee05f..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/operationresult.go +++ /dev/null @@ -1,105 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// OperationResultClient is the open API 2.0 Specs for Azure Data Protection service -type OperationResultClient struct { - BaseClient -} - -// NewOperationResultClient creates an instance of the OperationResultClient client. -func NewOperationResultClient(subscriptionID string) OperationResultClient { - return NewOperationResultClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewOperationResultClientWithBaseURI creates an instance of the OperationResultClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewOperationResultClientWithBaseURI(baseURI string, subscriptionID string) OperationResultClient { - return OperationResultClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get gets the operation result for a resource -func (client OperationResultClient) Get(ctx context.Context, operationID string, location string) (result OperationJobExtendedInfo, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OperationResultClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, operationID, location) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationResultClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.OperationResultClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationResultClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client OperationResultClient) GetPreparer(ctx context.Context, operationID string, location string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "location": autorest.Encode("path", location), - "operationId": autorest.Encode("path", operationID), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.DataProtection/locations/{location}/operationResults/{operationId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client OperationResultClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client OperationResultClient) GetResponder(resp *http.Response) (result OperationJobExtendedInfo, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/operations.go b/internal/services/dataprotection/legacysdk/dataprotection/operations.go deleted file mode 100644 index c2f1b1c58e0b..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/operations.go +++ /dev/null @@ -1,141 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// OperationsClient is the open API 2.0 Specs for Azure Data Protection service -type OperationsClient struct { - BaseClient -} - -// NewOperationsClient creates an instance of the OperationsClient client. -func NewOperationsClient(subscriptionID string) OperationsClient { - return NewOperationsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewOperationsClientWithBaseURI creates an instance of the OperationsClient client using a custom endpoint. Use this -// when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewOperationsClientWithBaseURI(baseURI string, subscriptionID string) OperationsClient { - return OperationsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// List returns the list of available operations. -func (client OperationsClient) List(ctx context.Context) (result ClientDiscoveryResponsePage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OperationsClient.List") - defer func() { - sc := -1 - if result.cdr.Response.Response != nil { - sc = result.cdr.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.listNextResults - req, err := client.ListPreparer(ctx) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "List", nil, "Failure preparing request") - return - } - - resp, err := client.ListSender(req) - if err != nil { - result.cdr.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "List", resp, "Failure sending request") - return - } - - result.cdr, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "List", resp, "Failure responding to request") - return - } - if result.cdr.hasNextLink() && result.cdr.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// ListPreparer prepares the List request. -func (client OperationsClient) ListPreparer(ctx context.Context) (*http.Request, error) { - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPath("/providers/Microsoft.DataProtection/operations"), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ListSender sends the List request. The method will close the -// http.Response Body if it receives an error. -func (client OperationsClient) ListSender(req *http.Request) (*http.Response, error) { - return client.Send(req, autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) -} - -// ListResponder handles the response to the List request. The method always -// closes the http.Response Body. -func (client OperationsClient) ListResponder(resp *http.Response) (result ClientDiscoveryResponse, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// listNextResults retrieves the next set of results, if any. -func (client OperationsClient) listNextResults(ctx context.Context, lastResults ClientDiscoveryResponse) (result ClientDiscoveryResponse, err error) { - req, err := lastResults.clientDiscoveryResponsePreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "listNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.ListSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "listNextResults", resp, "Failure sending next results request") - } - result, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationsClient", "listNextResults", resp, "Failure responding to next results request") - } - return -} - -// ListComplete enumerates all values, automatically crossing page boundaries as required. -func (client OperationsClient) ListComplete(ctx context.Context) (result ClientDiscoveryResponseIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OperationsClient.List") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.List(ctx) - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/operationstatus.go b/internal/services/dataprotection/legacysdk/dataprotection/operationstatus.go deleted file mode 100644 index f39eb64e4343..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/operationstatus.go +++ /dev/null @@ -1,105 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// OperationStatusClient is the open API 2.0 Specs for Azure Data Protection service -type OperationStatusClient struct { - BaseClient -} - -// NewOperationStatusClient creates an instance of the OperationStatusClient client. -func NewOperationStatusClient(subscriptionID string) OperationStatusClient { - return NewOperationStatusClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewOperationStatusClientWithBaseURI creates an instance of the OperationStatusClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewOperationStatusClientWithBaseURI(baseURI string, subscriptionID string) OperationStatusClient { - return OperationStatusClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get sends the get request. -func (client OperationStatusClient) Get(ctx context.Context, location string, operationID string) (result OperationResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OperationStatusClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, location, operationID) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationStatusClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.OperationStatusClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.OperationStatusClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client OperationStatusClient) GetPreparer(ctx context.Context, location string, operationID string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "location": autorest.Encode("path", location), - "operationId": autorest.Encode("path", operationID), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.DataProtection/locations/{location}/operationStatus/{operationId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client OperationStatusClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client OperationStatusClient) GetResponder(resp *http.Response) (result OperationResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/recoverypoints.go b/internal/services/dataprotection/legacysdk/dataprotection/recoverypoints.go deleted file mode 100644 index 3b3cbbda7fbf..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/recoverypoints.go +++ /dev/null @@ -1,239 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// RecoveryPointsClient is the open API 2.0 Specs for Azure Data Protection service -type RecoveryPointsClient struct { - BaseClient -} - -// NewRecoveryPointsClient creates an instance of the RecoveryPointsClient client. -func NewRecoveryPointsClient(subscriptionID string) RecoveryPointsClient { - return NewRecoveryPointsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewRecoveryPointsClientWithBaseURI creates an instance of the RecoveryPointsClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewRecoveryPointsClientWithBaseURI(baseURI string, subscriptionID string) RecoveryPointsClient { - return RecoveryPointsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Get gets a Recovery Point using recoveryPointId for a Datasource. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -func (client RecoveryPointsClient) Get(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, recoveryPointID string) (result AzureBackupRecoveryPointResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/RecoveryPointsClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, vaultName, resourceGroupName, backupInstanceName, recoveryPointID) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client RecoveryPointsClient) GetPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, recoveryPointID string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "recoveryPointId": autorest.Encode("path", recoveryPointID), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/recoveryPoints/{recoveryPointId}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client RecoveryPointsClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client RecoveryPointsClient) GetResponder(resp *http.Response) (result AzureBackupRecoveryPointResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// List returns a list of Recovery Points for a DataSource in a vault. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// filter - oData filter options. -// skipToken - skipToken Filter. -func (client RecoveryPointsClient) List(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, filter string, skipToken string) (result AzureBackupRecoveryPointResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/RecoveryPointsClient.List") - defer func() { - sc := -1 - if result.abrprl.Response.Response != nil { - sc = result.abrprl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.listNextResults - req, err := client.ListPreparer(ctx, vaultName, resourceGroupName, backupInstanceName, filter, skipToken) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "List", nil, "Failure preparing request") - return - } - - resp, err := client.ListSender(req) - if err != nil { - result.abrprl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "List", resp, "Failure sending request") - return - } - - result.abrprl, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "List", resp, "Failure responding to request") - return - } - if result.abrprl.hasNextLink() && result.abrprl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// ListPreparer prepares the List request. -func (client RecoveryPointsClient) ListPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, filter string, skipToken string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - if len(filter) > 0 { - queryParameters["$filter"] = autorest.Encode("query", filter) - } - if len(skipToken) > 0 { - queryParameters["$skipToken"] = autorest.Encode("query", skipToken) - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/recoveryPoints", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// ListSender sends the List request. The method will close the -// http.Response Body if it receives an error. -func (client RecoveryPointsClient) ListSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// ListResponder handles the response to the List request. The method always -// closes the http.Response Body. -func (client RecoveryPointsClient) ListResponder(resp *http.Response) (result AzureBackupRecoveryPointResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// listNextResults retrieves the next set of results, if any. -func (client RecoveryPointsClient) listNextResults(ctx context.Context, lastResults AzureBackupRecoveryPointResourceList) (result AzureBackupRecoveryPointResourceList, err error) { - req, err := lastResults.azureBackupRecoveryPointResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "listNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.ListSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "listNextResults", resp, "Failure sending next results request") - } - result, err = client.ListResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RecoveryPointsClient", "listNextResults", resp, "Failure responding to next results request") - } - return -} - -// ListComplete enumerates all values, automatically crossing page boundaries as required. -func (client RecoveryPointsClient) ListComplete(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, filter string, skipToken string) (result AzureBackupRecoveryPointResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/RecoveryPointsClient.List") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.List(ctx, vaultName, resourceGroupName, backupInstanceName, filter, skipToken) - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/resourceguards.go b/internal/services/dataprotection/legacysdk/dataprotection/resourceguards.go deleted file mode 100644 index e745da009ab4..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/resourceguards.go +++ /dev/null @@ -1,1730 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// ResourceGuardsClient is the open API 2.0 Specs for Azure Data Protection service -type ResourceGuardsClient struct { - BaseClient -} - -// NewResourceGuardsClient creates an instance of the ResourceGuardsClient client. -func NewResourceGuardsClient(subscriptionID string) ResourceGuardsClient { - return NewResourceGuardsClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewResourceGuardsClientWithBaseURI creates an instance of the ResourceGuardsClient client using a custom endpoint. -// Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure stack). -func NewResourceGuardsClientWithBaseURI(baseURI string, subscriptionID string) ResourceGuardsClient { - return ResourceGuardsClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Delete sends the delete request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// resourceGuardsName - the name of ResourceGuard -func (client ResourceGuardsClient) Delete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result autorest.Response, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.Delete") - defer func() { - sc := -1 - if result.Response != nil { - sc = result.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.DeletePreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Delete", nil, "Failure preparing request") - return - } - - resp, err := client.DeleteSender(req) - if err != nil { - result.Response = resp - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Delete", resp, "Failure sending request") - return - } - - result, err = client.DeleteResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Delete", resp, "Failure responding to request") - return - } - - return -} - -// DeletePreparer prepares the Delete request. -func (client ResourceGuardsClient) DeletePreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsDelete(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// DeleteSender sends the Delete request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) DeleteSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// DeleteResponder handles the response to the Delete request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), - autorest.ByClosing()) - result.Response = resp - return -} - -// Get sends the get request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// resourceGuardsName - the name of ResourceGuard -func (client ResourceGuardsClient) Get(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result ResourceGuardResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Get", resp, "Failure responding to request") - return - } - - return -} - -// GetPreparer prepares the Get request. -func (client ResourceGuardsClient) GetPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetResponder(resp *http.Response) (result ResourceGuardResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetBackupSecurityPINRequestsObjects sends the get backup security pin requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetBackupSecurityPINRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetBackupSecurityPINRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getBackupSecurityPINRequestsObjectsNextResults - req, err := client.GetBackupSecurityPINRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetBackupSecurityPINRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetBackupSecurityPINRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetBackupSecurityPINRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetBackupSecurityPINRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetBackupSecurityPINRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetBackupSecurityPINRequestsObjectsPreparer prepares the GetBackupSecurityPINRequestsObjects request. -func (client ResourceGuardsClient) GetBackupSecurityPINRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/getBackupSecurityPINRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetBackupSecurityPINRequestsObjectsSender sends the GetBackupSecurityPINRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetBackupSecurityPINRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetBackupSecurityPINRequestsObjectsResponder handles the response to the GetBackupSecurityPINRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetBackupSecurityPINRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getBackupSecurityPINRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getBackupSecurityPINRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getBackupSecurityPINRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetBackupSecurityPINRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getBackupSecurityPINRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetBackupSecurityPINRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getBackupSecurityPINRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetBackupSecurityPINRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetBackupSecurityPINRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetBackupSecurityPINRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetBackupSecurityPINRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// GetDefaultBackupSecurityPINRequestsObject sends the get default backup security pin requests object request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultBackupSecurityPINRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultBackupSecurityPINRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultBackupSecurityPINRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultBackupSecurityPINRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultBackupSecurityPINRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultBackupSecurityPINRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultBackupSecurityPINRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultBackupSecurityPINRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultBackupSecurityPINRequestsObjectPreparer prepares the GetDefaultBackupSecurityPINRequestsObject request. -func (client ResourceGuardsClient) GetDefaultBackupSecurityPINRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/getBackupSecurityPINRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultBackupSecurityPINRequestsObjectSender sends the GetDefaultBackupSecurityPINRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultBackupSecurityPINRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultBackupSecurityPINRequestsObjectResponder handles the response to the GetDefaultBackupSecurityPINRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultBackupSecurityPINRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDefaultDeleteProtectedItemRequestsObject sends the get default delete protected item requests object request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultDeleteProtectedItemRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultDeleteProtectedItemRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultDeleteProtectedItemRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteProtectedItemRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultDeleteProtectedItemRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteProtectedItemRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultDeleteProtectedItemRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteProtectedItemRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultDeleteProtectedItemRequestsObjectPreparer prepares the GetDefaultDeleteProtectedItemRequestsObject request. -func (client ResourceGuardsClient) GetDefaultDeleteProtectedItemRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/deleteProtectedItemRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultDeleteProtectedItemRequestsObjectSender sends the GetDefaultDeleteProtectedItemRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultDeleteProtectedItemRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultDeleteProtectedItemRequestsObjectResponder handles the response to the GetDefaultDeleteProtectedItemRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultDeleteProtectedItemRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDefaultDeleteResourceGuardProxyRequestsObject sends the get default delete resource guard proxy requests object -// request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultDeleteResourceGuardProxyRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultDeleteResourceGuardProxyRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultDeleteResourceGuardProxyRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteResourceGuardProxyRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultDeleteResourceGuardProxyRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteResourceGuardProxyRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultDeleteResourceGuardProxyRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDeleteResourceGuardProxyRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultDeleteResourceGuardProxyRequestsObjectPreparer prepares the GetDefaultDeleteResourceGuardProxyRequestsObject request. -func (client ResourceGuardsClient) GetDefaultDeleteResourceGuardProxyRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/deleteResourceGuardProxyRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultDeleteResourceGuardProxyRequestsObjectSender sends the GetDefaultDeleteResourceGuardProxyRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultDeleteResourceGuardProxyRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultDeleteResourceGuardProxyRequestsObjectResponder handles the response to the GetDefaultDeleteResourceGuardProxyRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultDeleteResourceGuardProxyRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDefaultDisableSoftDeleteRequestsObject sends the get default disable soft delete requests object request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultDisableSoftDeleteRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultDisableSoftDeleteRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultDisableSoftDeleteRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDisableSoftDeleteRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultDisableSoftDeleteRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDisableSoftDeleteRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultDisableSoftDeleteRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultDisableSoftDeleteRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultDisableSoftDeleteRequestsObjectPreparer prepares the GetDefaultDisableSoftDeleteRequestsObject request. -func (client ResourceGuardsClient) GetDefaultDisableSoftDeleteRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/disableSoftDeleteRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultDisableSoftDeleteRequestsObjectSender sends the GetDefaultDisableSoftDeleteRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultDisableSoftDeleteRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultDisableSoftDeleteRequestsObjectResponder handles the response to the GetDefaultDisableSoftDeleteRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultDisableSoftDeleteRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDefaultUpdateProtectedItemRequestsObject sends the get default update protected item requests object request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultUpdateProtectedItemRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultUpdateProtectedItemRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultUpdateProtectedItemRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectedItemRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultUpdateProtectedItemRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectedItemRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultUpdateProtectedItemRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectedItemRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultUpdateProtectedItemRequestsObjectPreparer prepares the GetDefaultUpdateProtectedItemRequestsObject request. -func (client ResourceGuardsClient) GetDefaultUpdateProtectedItemRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/updateProtectedItemRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultUpdateProtectedItemRequestsObjectSender sends the GetDefaultUpdateProtectedItemRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultUpdateProtectedItemRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultUpdateProtectedItemRequestsObjectResponder handles the response to the GetDefaultUpdateProtectedItemRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultUpdateProtectedItemRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDefaultUpdateProtectionPolicyRequestsObject sends the get default update protection policy requests object -// request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDefaultUpdateProtectionPolicyRequestsObject(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (result DppBaseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDefaultUpdateProtectionPolicyRequestsObject") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetDefaultUpdateProtectionPolicyRequestsObjectPreparer(ctx, resourceGroupName, resourceGuardsName, requestName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectionPolicyRequestsObject", nil, "Failure preparing request") - return - } - - resp, err := client.GetDefaultUpdateProtectionPolicyRequestsObjectSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectionPolicyRequestsObject", resp, "Failure sending request") - return - } - - result, err = client.GetDefaultUpdateProtectionPolicyRequestsObjectResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDefaultUpdateProtectionPolicyRequestsObject", resp, "Failure responding to request") - return - } - - return -} - -// GetDefaultUpdateProtectionPolicyRequestsObjectPreparer prepares the GetDefaultUpdateProtectionPolicyRequestsObject request. -func (client ResourceGuardsClient) GetDefaultUpdateProtectionPolicyRequestsObjectPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, requestName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "requestName": autorest.Encode("path", requestName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/updateProtectionPolicyRequests/{requestName}", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDefaultUpdateProtectionPolicyRequestsObjectSender sends the GetDefaultUpdateProtectionPolicyRequestsObject request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDefaultUpdateProtectionPolicyRequestsObjectSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDefaultUpdateProtectionPolicyRequestsObjectResponder handles the response to the GetDefaultUpdateProtectionPolicyRequestsObject request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDefaultUpdateProtectionPolicyRequestsObjectResponder(resp *http.Response) (result DppBaseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// GetDeleteProtectedItemRequestsObjects sends the get delete protected item requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDeleteProtectedItemRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDeleteProtectedItemRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getDeleteProtectedItemRequestsObjectsNextResults - req, err := client.GetDeleteProtectedItemRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteProtectedItemRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetDeleteProtectedItemRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteProtectedItemRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetDeleteProtectedItemRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteProtectedItemRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetDeleteProtectedItemRequestsObjectsPreparer prepares the GetDeleteProtectedItemRequestsObjects request. -func (client ResourceGuardsClient) GetDeleteProtectedItemRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/deleteProtectedItemRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDeleteProtectedItemRequestsObjectsSender sends the GetDeleteProtectedItemRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDeleteProtectedItemRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDeleteProtectedItemRequestsObjectsResponder handles the response to the GetDeleteProtectedItemRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDeleteProtectedItemRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getDeleteProtectedItemRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getDeleteProtectedItemRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteProtectedItemRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetDeleteProtectedItemRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteProtectedItemRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetDeleteProtectedItemRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteProtectedItemRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetDeleteProtectedItemRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetDeleteProtectedItemRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDeleteProtectedItemRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetDeleteProtectedItemRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// GetDeleteResourceGuardProxyRequestsObjects sends the get delete resource guard proxy requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDeleteResourceGuardProxyRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDeleteResourceGuardProxyRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getDeleteResourceGuardProxyRequestsObjectsNextResults - req, err := client.GetDeleteResourceGuardProxyRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteResourceGuardProxyRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetDeleteResourceGuardProxyRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteResourceGuardProxyRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetDeleteResourceGuardProxyRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDeleteResourceGuardProxyRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetDeleteResourceGuardProxyRequestsObjectsPreparer prepares the GetDeleteResourceGuardProxyRequestsObjects request. -func (client ResourceGuardsClient) GetDeleteResourceGuardProxyRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/deleteResourceGuardProxyRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDeleteResourceGuardProxyRequestsObjectsSender sends the GetDeleteResourceGuardProxyRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDeleteResourceGuardProxyRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDeleteResourceGuardProxyRequestsObjectsResponder handles the response to the GetDeleteResourceGuardProxyRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDeleteResourceGuardProxyRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getDeleteResourceGuardProxyRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getDeleteResourceGuardProxyRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteResourceGuardProxyRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetDeleteResourceGuardProxyRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteResourceGuardProxyRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetDeleteResourceGuardProxyRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDeleteResourceGuardProxyRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetDeleteResourceGuardProxyRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetDeleteResourceGuardProxyRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDeleteResourceGuardProxyRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetDeleteResourceGuardProxyRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// GetDisableSoftDeleteRequestsObjects sends the get disable soft delete requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetDisableSoftDeleteRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDisableSoftDeleteRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getDisableSoftDeleteRequestsObjectsNextResults - req, err := client.GetDisableSoftDeleteRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDisableSoftDeleteRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetDisableSoftDeleteRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDisableSoftDeleteRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetDisableSoftDeleteRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetDisableSoftDeleteRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetDisableSoftDeleteRequestsObjectsPreparer prepares the GetDisableSoftDeleteRequestsObjects request. -func (client ResourceGuardsClient) GetDisableSoftDeleteRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/disableSoftDeleteRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetDisableSoftDeleteRequestsObjectsSender sends the GetDisableSoftDeleteRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetDisableSoftDeleteRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetDisableSoftDeleteRequestsObjectsResponder handles the response to the GetDisableSoftDeleteRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetDisableSoftDeleteRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getDisableSoftDeleteRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getDisableSoftDeleteRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDisableSoftDeleteRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetDisableSoftDeleteRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDisableSoftDeleteRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetDisableSoftDeleteRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getDisableSoftDeleteRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetDisableSoftDeleteRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetDisableSoftDeleteRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetDisableSoftDeleteRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetDisableSoftDeleteRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// GetResourcesInResourceGroup sends the get resources in resource group request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetResourcesInResourceGroup(ctx context.Context, resourceGroupName string) (result ResourceGuardResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetResourcesInResourceGroup") - defer func() { - sc := -1 - if result.rgrl.Response.Response != nil { - sc = result.rgrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getResourcesInResourceGroupNextResults - req, err := client.GetResourcesInResourceGroupPreparer(ctx, resourceGroupName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInResourceGroup", nil, "Failure preparing request") - return - } - - resp, err := client.GetResourcesInResourceGroupSender(req) - if err != nil { - result.rgrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInResourceGroup", resp, "Failure sending request") - return - } - - result.rgrl, err = client.GetResourcesInResourceGroupResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInResourceGroup", resp, "Failure responding to request") - return - } - if result.rgrl.hasNextLink() && result.rgrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetResourcesInResourceGroupPreparer prepares the GetResourcesInResourceGroup request. -func (client ResourceGuardsClient) GetResourcesInResourceGroupPreparer(ctx context.Context, resourceGroupName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetResourcesInResourceGroupSender sends the GetResourcesInResourceGroup request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetResourcesInResourceGroupSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResourcesInResourceGroupResponder handles the response to the GetResourcesInResourceGroup request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetResourcesInResourceGroupResponder(resp *http.Response) (result ResourceGuardResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getResourcesInResourceGroupNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getResourcesInResourceGroupNextResults(ctx context.Context, lastResults ResourceGuardResourceList) (result ResourceGuardResourceList, err error) { - req, err := lastResults.resourceGuardResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInResourceGroupNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetResourcesInResourceGroupSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInResourceGroupNextResults", resp, "Failure sending next results request") - } - result, err = client.GetResourcesInResourceGroupResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInResourceGroupNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetResourcesInResourceGroupComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetResourcesInResourceGroupComplete(ctx context.Context, resourceGroupName string) (result ResourceGuardResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetResourcesInResourceGroup") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetResourcesInResourceGroup(ctx, resourceGroupName) - return -} - -// GetResourcesInSubscription sends the get resources in subscription request. -func (client ResourceGuardsClient) GetResourcesInSubscription(ctx context.Context) (result ResourceGuardResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetResourcesInSubscription") - defer func() { - sc := -1 - if result.rgrl.Response.Response != nil { - sc = result.rgrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getResourcesInSubscriptionNextResults - req, err := client.GetResourcesInSubscriptionPreparer(ctx) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInSubscription", nil, "Failure preparing request") - return - } - - resp, err := client.GetResourcesInSubscriptionSender(req) - if err != nil { - result.rgrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInSubscription", resp, "Failure sending request") - return - } - - result.rgrl, err = client.GetResourcesInSubscriptionResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetResourcesInSubscription", resp, "Failure responding to request") - return - } - if result.rgrl.hasNextLink() && result.rgrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetResourcesInSubscriptionPreparer prepares the GetResourcesInSubscription request. -func (client ResourceGuardsClient) GetResourcesInSubscriptionPreparer(ctx context.Context) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.DataProtection/resourceGuards", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetResourcesInSubscriptionSender sends the GetResourcesInSubscription request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetResourcesInSubscriptionSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetResourcesInSubscriptionResponder handles the response to the GetResourcesInSubscription request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetResourcesInSubscriptionResponder(resp *http.Response) (result ResourceGuardResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getResourcesInSubscriptionNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getResourcesInSubscriptionNextResults(ctx context.Context, lastResults ResourceGuardResourceList) (result ResourceGuardResourceList, err error) { - req, err := lastResults.resourceGuardResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInSubscriptionNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetResourcesInSubscriptionSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInSubscriptionNextResults", resp, "Failure sending next results request") - } - result, err = client.GetResourcesInSubscriptionResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getResourcesInSubscriptionNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetResourcesInSubscriptionComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetResourcesInSubscriptionComplete(ctx context.Context) (result ResourceGuardResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetResourcesInSubscription") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetResourcesInSubscription(ctx) - return -} - -// GetUpdateProtectedItemRequestsObjects sends the get update protected item requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetUpdateProtectedItemRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetUpdateProtectedItemRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getUpdateProtectedItemRequestsObjectsNextResults - req, err := client.GetUpdateProtectedItemRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectedItemRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetUpdateProtectedItemRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectedItemRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetUpdateProtectedItemRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectedItemRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetUpdateProtectedItemRequestsObjectsPreparer prepares the GetUpdateProtectedItemRequestsObjects request. -func (client ResourceGuardsClient) GetUpdateProtectedItemRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/updateProtectedItemRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetUpdateProtectedItemRequestsObjectsSender sends the GetUpdateProtectedItemRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetUpdateProtectedItemRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetUpdateProtectedItemRequestsObjectsResponder handles the response to the GetUpdateProtectedItemRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetUpdateProtectedItemRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getUpdateProtectedItemRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getUpdateProtectedItemRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectedItemRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetUpdateProtectedItemRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectedItemRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetUpdateProtectedItemRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectedItemRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetUpdateProtectedItemRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetUpdateProtectedItemRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetUpdateProtectedItemRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetUpdateProtectedItemRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// GetUpdateProtectionPolicyRequestsObjects sends the get update protection policy requests objects request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -func (client ResourceGuardsClient) GetUpdateProtectionPolicyRequestsObjects(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListPage, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetUpdateProtectionPolicyRequestsObjects") - defer func() { - sc := -1 - if result.dbrl.Response.Response != nil { - sc = result.dbrl.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.fn = client.getUpdateProtectionPolicyRequestsObjectsNextResults - req, err := client.GetUpdateProtectionPolicyRequestsObjectsPreparer(ctx, resourceGroupName, resourceGuardsName) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectionPolicyRequestsObjects", nil, "Failure preparing request") - return - } - - resp, err := client.GetUpdateProtectionPolicyRequestsObjectsSender(req) - if err != nil { - result.dbrl.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectionPolicyRequestsObjects", resp, "Failure sending request") - return - } - - result.dbrl, err = client.GetUpdateProtectionPolicyRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "GetUpdateProtectionPolicyRequestsObjects", resp, "Failure responding to request") - return - } - if result.dbrl.hasNextLink() && result.dbrl.IsEmpty() { - err = result.NextWithContext(ctx) - return - } - - return -} - -// GetUpdateProtectionPolicyRequestsObjectsPreparer prepares the GetUpdateProtectionPolicyRequestsObjects request. -func (client ResourceGuardsClient) GetUpdateProtectionPolicyRequestsObjectsPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}/updateProtectionPolicyRequests", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetUpdateProtectionPolicyRequestsObjectsSender sends the GetUpdateProtectionPolicyRequestsObjects request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) GetUpdateProtectionPolicyRequestsObjectsSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// GetUpdateProtectionPolicyRequestsObjectsResponder handles the response to the GetUpdateProtectionPolicyRequestsObjects request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) GetUpdateProtectionPolicyRequestsObjectsResponder(resp *http.Response) (result DppBaseResourceList, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// getUpdateProtectionPolicyRequestsObjectsNextResults retrieves the next set of results, if any. -func (client ResourceGuardsClient) getUpdateProtectionPolicyRequestsObjectsNextResults(ctx context.Context, lastResults DppBaseResourceList) (result DppBaseResourceList, err error) { - req, err := lastResults.dppBaseResourceListPreparer(ctx) - if err != nil { - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectionPolicyRequestsObjectsNextResults", nil, "Failure preparing next results request") - } - if req == nil { - return - } - resp, err := client.GetUpdateProtectionPolicyRequestsObjectsSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - return result, autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectionPolicyRequestsObjectsNextResults", resp, "Failure sending next results request") - } - result, err = client.GetUpdateProtectionPolicyRequestsObjectsResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "getUpdateProtectionPolicyRequestsObjectsNextResults", resp, "Failure responding to next results request") - } - return -} - -// GetUpdateProtectionPolicyRequestsObjectsComplete enumerates all values, automatically crossing page boundaries as required. -func (client ResourceGuardsClient) GetUpdateProtectionPolicyRequestsObjectsComplete(ctx context.Context, resourceGroupName string, resourceGuardsName string) (result DppBaseResourceListIterator, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.GetUpdateProtectionPolicyRequestsObjects") - defer func() { - sc := -1 - if result.Response().Response.Response != nil { - sc = result.page.Response().Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - result.page, err = client.GetUpdateProtectionPolicyRequestsObjects(ctx, resourceGroupName, resourceGuardsName) - return -} - -// Patch sends the patch request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// resourceGuardsName - the name of ResourceGuard -// parameters - request body for operation -func (client ResourceGuardsClient) Patch(ctx context.Context, resourceGroupName string, resourceGuardsName string, parameters PatchResourceRequestInput) (result ResourceGuardResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.Patch") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.PatchPreparer(ctx, resourceGroupName, resourceGuardsName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Patch", nil, "Failure preparing request") - return - } - - resp, err := client.PatchSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Patch", resp, "Failure sending request") - return - } - - result, err = client.PatchResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Patch", resp, "Failure responding to request") - return - } - - return -} - -// PatchPreparer prepares the Patch request. -func (client ResourceGuardsClient) PatchPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, parameters PatchResourceRequestInput) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPatch(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// PatchSender sends the Patch request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) PatchSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// PatchResponder handles the response to the Patch request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) PatchResponder(resp *http.Response) (result ResourceGuardResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// Put sends the put request. -// Parameters: -// resourceGroupName - the name of the resource group where the backup vault is present. -// resourceGuardsName - the name of ResourceGuard -// parameters - request body for operation -func (client ResourceGuardsClient) Put(ctx context.Context, resourceGroupName string, resourceGuardsName string, parameters ResourceGuardResource) (result ResourceGuardResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ResourceGuardsClient.Put") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.PutPreparer(ctx, resourceGroupName, resourceGuardsName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Put", nil, "Failure preparing request") - return - } - - resp, err := client.PutSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Put", resp, "Failure sending request") - return - } - - result, err = client.PutResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.ResourceGuardsClient", "Put", resp, "Failure responding to request") - return - } - - return -} - -// PutPreparer prepares the Put request. -func (client ResourceGuardsClient) PutPreparer(ctx context.Context, resourceGroupName string, resourceGuardsName string, parameters ResourceGuardResource) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "resourceGuardsName": autorest.Encode("path", resourceGuardsName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPut(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/resourceGuards/{resourceGuardsName}", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// PutSender sends the Put request. The method will close the -// http.Response Body if it receives an error. -func (client ResourceGuardsClient) PutSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// PutResponder handles the response to the Put request. The method always -// closes the http.Response Body. -func (client ResourceGuardsClient) PutResponder(resp *http.Response) (result ResourceGuardResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/restorabletimeranges.go b/internal/services/dataprotection/legacysdk/dataprotection/restorabletimeranges.go deleted file mode 100644 index 4450de5f4e32..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/restorabletimeranges.go +++ /dev/null @@ -1,114 +0,0 @@ -package dataprotection - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" -) - -// RestorableTimeRangesClient is the open API 2.0 Specs for Azure Data Protection service -type RestorableTimeRangesClient struct { - BaseClient -} - -// NewRestorableTimeRangesClient creates an instance of the RestorableTimeRangesClient client. -func NewRestorableTimeRangesClient(subscriptionID string) RestorableTimeRangesClient { - return NewRestorableTimeRangesClientWithBaseURI(DefaultBaseURI, subscriptionID) -} - -// NewRestorableTimeRangesClientWithBaseURI creates an instance of the RestorableTimeRangesClient client using a custom -// endpoint. Use this when interacting with an Azure cloud that uses a non-standard base URI (sovereign clouds, Azure -// stack). -func NewRestorableTimeRangesClientWithBaseURI(baseURI string, subscriptionID string) RestorableTimeRangesClient { - return RestorableTimeRangesClient{NewWithBaseURI(baseURI, subscriptionID)} -} - -// Find sends the find request. -// Parameters: -// vaultName - the name of the backup vault. -// resourceGroupName - the name of the resource group where the backup vault is present. -// backupInstanceName - the name of the backup instance -// parameters - request body for operation -func (client RestorableTimeRangesClient) Find(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters AzureBackupFindRestorableTimeRangesRequest) (result AzureBackupFindRestorableTimeRangesResponseResource, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/RestorableTimeRangesClient.Find") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.FindPreparer(ctx, vaultName, resourceGroupName, backupInstanceName, parameters) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RestorableTimeRangesClient", "Find", nil, "Failure preparing request") - return - } - - resp, err := client.FindSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "dataprotection.RestorableTimeRangesClient", "Find", resp, "Failure sending request") - return - } - - result, err = client.FindResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "dataprotection.RestorableTimeRangesClient", "Find", resp, "Failure responding to request") - return - } - - return -} - -// FindPreparer prepares the Find request. -func (client RestorableTimeRangesClient) FindPreparer(ctx context.Context, vaultName string, resourceGroupName string, backupInstanceName string, parameters AzureBackupFindRestorableTimeRangesRequest) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "backupInstanceName": autorest.Encode("path", backupInstanceName), - "resourceGroupName": autorest.Encode("path", resourceGroupName), - "subscriptionId": autorest.Encode("path", client.SubscriptionID), - "vaultName": autorest.Encode("path", vaultName), - } - - const APIVersion = "2021-07-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DataProtection/backupVaults/{vaultName}/backupInstances/{backupInstanceName}/findRestorableTimeRanges", pathParameters), - autorest.WithJSON(parameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// FindSender sends the Find request. The method will close the -// http.Response Body if it receives an error. -func (client RestorableTimeRangesClient) FindSender(req *http.Request) (*http.Response, error) { - return client.Send(req, azure.DoRetryWithRegistration(client.Client)) -} - -// FindResponder handles the response to the Find request. The method always -// closes the http.Response Body. -func (client RestorableTimeRangesClient) FindResponder(resp *http.Response) (result AzureBackupFindRestorableTimeRangesResponseResource, err error) { - err = autorest.Respond( - resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/internal/services/dataprotection/legacysdk/dataprotection/version.go b/internal/services/dataprotection/legacysdk/dataprotection/version.go deleted file mode 100644 index ca2052fa9faa..000000000000 --- a/internal/services/dataprotection/legacysdk/dataprotection/version.go +++ /dev/null @@ -1,19 +0,0 @@ -package dataprotection - -import "github.com/Azure/azure-sdk-for-go/version" - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -// UserAgent returns the UserAgent string to use when sending http.Requests. -func UserAgent() string { - return "Azure-SDK-For-Go/" + Version() + " dataprotection/2021-07-01" -} - -// Version returns the semantic version (see http://semver.org) of the client. -func Version() string { - return version.Number -} diff --git a/internal/services/dataprotection/parse/backup_instance.go b/internal/services/dataprotection/parse/backup_instance.go deleted file mode 100644 index f257a1a68b91..000000000000 --- a/internal/services/dataprotection/parse/backup_instance.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type BackupInstanceId struct { - SubscriptionId string - ResourceGroup string - BackupVaultName string - Name string -} - -func NewBackupInstanceID(subscriptionId, resourceGroup, backupVaultName, name string) BackupInstanceId { - return BackupInstanceId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - BackupVaultName: backupVaultName, - Name: name, - } -} - -func (id BackupInstanceId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Backup Vault Name %q", id.BackupVaultName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Backup Instance", segmentsStr) -} - -func (id BackupInstanceId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DataProtection/backupVaults/%s/backupInstances/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.BackupVaultName, id.Name) -} - -// BackupInstanceID parses a BackupInstance ID into an BackupInstanceId struct -func BackupInstanceID(input string) (*BackupInstanceId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := BackupInstanceId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.BackupVaultName, err = id.PopSegment("backupVaults"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("backupInstances"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dataprotection/parse/backup_instance_test.go b/internal/services/dataprotection/parse/backup_instance_test.go deleted file mode 100644 index a8575d6214f8..000000000000 --- a/internal/services/dataprotection/parse/backup_instance_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = BackupInstanceId{} - -func TestBackupInstanceIDFormatter(t *testing.T) { - actual := NewBackupInstanceID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "vault1", "backupInstance1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/backupInstance1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestBackupInstanceID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *BackupInstanceId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/", - Error: true, - }, - - { - // missing value for BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/backupInstance1", - Expected: &BackupInstanceId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", - BackupVaultName: "vault1", - Name: "backupInstance1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATAPROTECTION/BACKUPVAULTS/VAULT1/BACKUPINSTANCES/BACKUPINSTANCE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := BackupInstanceID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.BackupVaultName != v.Expected.BackupVaultName { - t.Fatalf("Expected %q but got %q for BackupVaultName", v.Expected.BackupVaultName, actual.BackupVaultName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/dataprotection/parse/backup_policy.go b/internal/services/dataprotection/parse/backup_policy.go deleted file mode 100644 index 284f7e1e0f83..000000000000 --- a/internal/services/dataprotection/parse/backup_policy.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type BackupPolicyId struct { - SubscriptionId string - ResourceGroup string - BackupVaultName string - Name string -} - -func NewBackupPolicyID(subscriptionId, resourceGroup, backupVaultName, name string) BackupPolicyId { - return BackupPolicyId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - BackupVaultName: backupVaultName, - Name: name, - } -} - -func (id BackupPolicyId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Backup Vault Name %q", id.BackupVaultName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Backup Policy", segmentsStr) -} - -func (id BackupPolicyId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DataProtection/backupVaults/%s/backupPolicies/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.BackupVaultName, id.Name) -} - -// BackupPolicyID parses a BackupPolicy ID into an BackupPolicyId struct -func BackupPolicyID(input string) (*BackupPolicyId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := BackupPolicyId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.BackupVaultName, err = id.PopSegment("backupVaults"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("backupPolicies"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dataprotection/parse/backup_policy_test.go b/internal/services/dataprotection/parse/backup_policy_test.go deleted file mode 100644 index 3c094fb4cc74..000000000000 --- a/internal/services/dataprotection/parse/backup_policy_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = BackupPolicyId{} - -func TestBackupPolicyIDFormatter(t *testing.T) { - actual := NewBackupPolicyID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "vault1", "backupPolicy1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestBackupPolicyID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *BackupPolicyId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/", - Error: true, - }, - - { - // missing value for BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1", - Expected: &BackupPolicyId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resourceGroup1", - BackupVaultName: "vault1", - Name: "backupPolicy1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATAPROTECTION/BACKUPVAULTS/VAULT1/BACKUPPOLICIES/BACKUPPOLICY1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := BackupPolicyID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.BackupVaultName != v.Expected.BackupVaultName { - t.Fatalf("Expected %q but got %q for BackupVaultName", v.Expected.BackupVaultName, actual.BackupVaultName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/dataprotection/resourceids.go b/internal/services/dataprotection/resourceids.go deleted file mode 100644 index 08d621225861..000000000000 --- a/internal/services/dataprotection/resourceids.go +++ /dev/null @@ -1,5 +0,0 @@ -package dataprotection - -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=BackupVault -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=BackupPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=BackupInstance -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/backupInstance1 diff --git a/internal/services/dataprotection/transition.go b/internal/services/dataprotection/transition.go new file mode 100644 index 000000000000..f28fff4beb98 --- /dev/null +++ b/internal/services/dataprotection/transition.go @@ -0,0 +1,23 @@ +package dataprotection + +import "github.com/hashicorp/terraform-provider-azurerm/utils" + +func expandTags(input map[string]interface{}) *map[string]string { + output := make(map[string]string) + for k, v := range input { + output[k] = v.(string) + } + return &output +} + +func flattenTags(input *map[string]string) map[string]*string { + output := make(map[string]*string) + + if input != nil { + for k, v := range *input { + output[k] = utils.String(v) + } + } + + return output +} diff --git a/internal/services/dataprotection/validate/backup_instance_id.go b/internal/services/dataprotection/validate/backup_instance_id.go deleted file mode 100644 index 52a66795d026..000000000000 --- a/internal/services/dataprotection/validate/backup_instance_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" -) - -func BackupInstanceID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.BackupInstanceID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dataprotection/validate/backup_instance_id_test.go b/internal/services/dataprotection/validate/backup_instance_id_test.go deleted file mode 100644 index 198efb5bd556..000000000000 --- a/internal/services/dataprotection/validate/backup_instance_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestBackupInstanceID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/", - Valid: false, - }, - - { - // missing value for BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/backupInstance1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATAPROTECTION/BACKUPVAULTS/VAULT1/BACKUPINSTANCES/BACKUPINSTANCE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := BackupInstanceID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dataprotection/validate/backup_policy_id.go b/internal/services/dataprotection/validate/backup_policy_id.go deleted file mode 100644 index 31b35c392b36..000000000000 --- a/internal/services/dataprotection/validate/backup_policy_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" -) - -func BackupPolicyID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.BackupPolicyID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dataprotection/validate/backup_policy_id_test.go b/internal/services/dataprotection/validate/backup_policy_id_test.go deleted file mode 100644 index 33353158919c..000000000000 --- a/internal/services/dataprotection/validate/backup_policy_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestBackupPolicyID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/", - Valid: false, - }, - - { - // missing value for BackupVaultName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1/backupPolicies/backupPolicy1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATAPROTECTION/BACKUPVAULTS/VAULT1/BACKUPPOLICIES/BACKUPPOLICY1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := BackupPolicyID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dataprotection/validate/backup_vault_id.go b/internal/services/dataprotection/validate/backup_vault_id.go deleted file mode 100644 index 0e7c5a727f11..000000000000 --- a/internal/services/dataprotection/validate/backup_vault_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dataprotection/parse" -) - -func BackupVaultID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.BackupVaultID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dataprotection/validate/backup_vault_id_test.go b/internal/services/dataprotection/validate/backup_vault_id_test.go deleted file mode 100644 index aa463cc3f948..000000000000 --- a/internal/services/dataprotection/validate/backup_vault_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestBackupVaultID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.DataProtection/backupVaults/vault1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.DATAPROTECTION/BACKUPVAULTS/VAULT1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := BackupVaultID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/disks/disk_pool_iscsi_target_lun_resource_test.go b/internal/services/disks/disk_pool_iscsi_target_lun_resource_test.go index 3f5527a027ec..90b12d8793ce 100644 --- a/internal/services/disks/disk_pool_iscsi_target_lun_resource_test.go +++ b/internal/services/disks/disk_pool_iscsi_target_lun_resource_test.go @@ -217,9 +217,9 @@ func (r DisksPoolIscsiTargetLunResource) multipleLuns(data acceptance.TestData, %[1]s resource "azurerm_disk_pool_iscsi_target_lun" "test%[2]d" { - iscsi_target_id = azurerm_disk_pool_iscsi_target.test.id + iscsi_target_id = azurerm_disk_pool_iscsi_target.test.id disk_pool_managed_disk_attachment_id = azurerm_disk_pool_managed_disk_attachment.test[%[2]d].id - name = "test-%[2]d" + name = "test-%[2]d" } `, tfCode, i) } diff --git a/internal/services/dns/client/client.go b/internal/services/dns/client/client.go index d1dc5bea72b8..0f34c6a66b83 100644 --- a/internal/services/dns/client/client.go +++ b/internal/services/dns/client/client.go @@ -1,24 +1,14 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/Azure/go-autorest/autorest" + dns_v2018_05_01 "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) -type Client struct { - RecordSetsClient *dns.RecordSetsClient - ZonesClient *dns.ZonesClient -} - -func NewClient(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, - } +func NewClient(o *common.ClientOptions) *dns_v2018_05_01.Client { + client := dns_v2018_05_01.NewClientWithBaseURI(o.ResourceManagerEndpoint, func(c *autorest.Client) { + c.Authorizer = o.ResourceManagerAuthorizer + }) + return &client } diff --git a/internal/services/dns/dns_a_record_data_source.go b/internal/services/dns/dns_a_record_data_source.go new file mode 100644 index 000000000000..097da3483707 --- /dev/null +++ b/internal/services/dns/dns_a_record_data_source.go @@ -0,0 +1,106 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsARecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsARecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "target_resource_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsARecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeA, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmDnsARecords(props.ARecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_a_record_data_source_test.go b/internal/services/dns/dns_a_record_data_source_test.go new file mode 100644 index 000000000000..26f2d0631c37 --- /dev/null +++ b/internal/services/dns/dns_a_record_data_source_test.go @@ -0,0 +1,44 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type TestAccDnsARecordDataSource struct{} + +func TestAccDataSourceDnsARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_a_record", "test") + r := TestAccDnsARecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + check.That(data.ResourceName).Key("target_resource_id").HasValue(""), + ), + }, + }) +} + +func (TestAccDnsARecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_a_record" "test" { + name = azurerm_dns_a_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, TestAccDnsARecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_a_record_resource.go b/internal/services/dns/dns_a_record_resource.go index 12b55baeaff8..de9bf144acb3 100644 --- a/internal/services/dns/dns_a_record_resource.go +++ b/internal/services/dns/dns_a_record_resource.go @@ -2,15 +2,15 @@ package dns import ( "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -30,8 +30,14 @@ func resourceDnsARecord() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.ARecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeA { + return fmt.Errorf("this resource only supports 'A' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -41,11 +47,12 @@ func resourceDnsARecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "records": { @@ -75,13 +82,13 @@ func resourceDnsARecord() *pluginsdk.Resource { // TODO: switch ConflictsWith for ExactlyOneOf when the Provider SDK's updated }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -90,18 +97,17 @@ func resourceDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewARecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeA, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.A) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS A Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_a_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_a_record", id.ID()) } } @@ -110,18 +116,18 @@ func resourceDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) targetResourceId := d.Get("target_resource_id").(string) recordsRaw := d.Get("records").(*pluginsdk.Set).List() - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, ARecords: expandAzureRmDnsARecords(recordsRaw), - TargetResource: &dns.SubResource{}, + TargetResource: &recordsets.SubResource{}, }, } if targetResourceId != "" { - parameters.RecordSetProperties.TargetResource.ID = utils.String(targetResourceId) + parameters.Properties.TargetResource.Id = utils.String(targetResourceId) } // TODO: this can be removed when the provider SDK is upgraded @@ -129,99 +135,105 @@ func resourceDnsARecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("One of either `records` or `target_resource_id` must be specified") } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.A, parameters, eTag, ifNoneMatch); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { return fmt.Errorf("creating/updating DNS A Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsARecordRead(d, meta) } func resourceDnsARecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ARecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := dnsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.AName, dns.A) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS A record %s: %+v", id.AName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.AName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("fqdn", resp.Fqdn) - d.Set("ttl", resp.TTL) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("fqdn", props.Fqdn) + d.Set("ttl", props.TTL) - if err := d.Set("records", flattenAzureRmDnsARecords(resp.ARecords)); err != nil { - return fmt.Errorf("setting `records`: %+v", err) - } + if err := d.Set("records", flattenAzureRmDnsARecords(props.ARecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) + + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } - targetResourceId := "" - if resp.TargetResource != nil && resp.TargetResource.ID != nil { - targetResourceId = *resp.TargetResource.ID } - d.Set("target_resource_id", targetResourceId) - return tags.FlattenAndSet(d, resp.Metadata) + return nil } func resourceDnsARecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ARecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := dnsClient.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.AName, dns.A, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS A Record %s: %+v", id.AName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func expandAzureRmDnsARecords(input []interface{}) *[]dns.ARecord { - records := make([]dns.ARecord, len(input)) +func expandAzureRmDnsARecords(input []interface{}) *[]recordsets.ARecord { + records := make([]recordsets.ARecord, len(input)) for i, v := range input { ipv4 := v.(string) - records[i] = dns.ARecord{ - Ipv4Address: &ipv4, + records[i] = recordsets.ARecord{ + IPv4Address: &ipv4, } } return &records } -func flattenAzureRmDnsARecords(records *[]dns.ARecord) []string { +func flattenAzureRmDnsARecords(records *[]recordsets.ARecord) []string { if records == nil { return []string{} } results := make([]string, 0) for _, record := range *records { - if record.Ipv4Address == nil { + if record.IPv4Address == nil { continue } - results = append(results, *record.Ipv4Address) + results = append(results, *record.IPv4Address) } return results diff --git a/internal/services/dns/dns_a_record_resource_test.go b/internal/services/dns/dns_a_record_resource_test.go index 56ac43d8ab7a..f6a7bafa402c 100644 --- a/internal/services/dns/dns_a_record_resource_test.go +++ b/internal/services/dns/dns_a_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -171,17 +170,17 @@ func TestAccAzureRMDnsARecord_AliasToRecords(t *testing.T) { } func (TestAccDnsARecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ARecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.AName, dns.A) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS A record %s (resource group: %s): %v", id.AName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (TestAccDnsARecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_aaaa_record_data_source.go b/internal/services/dns/dns_aaaa_record_data_source.go new file mode 100644 index 000000000000..ea1bc66b4c80 --- /dev/null +++ b/internal/services/dns/dns_aaaa_record_data_source.go @@ -0,0 +1,107 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/set" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsAAAARecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsAAAARecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: set.HashIPv6Address, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "target_resource_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsAAAARecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeAAAA, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmDnsAaaaRecords(props.AAAARecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_aaaa_record_data_source_test.go b/internal/services/dns/dns_aaaa_record_data_source_test.go new file mode 100644 index 000000000000..5707601fe17f --- /dev/null +++ b/internal/services/dns/dns_aaaa_record_data_source_test.go @@ -0,0 +1,44 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsAAAARecordDataSource struct{} + +func TestAccDataSourceDnsAAAARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_aaaa_record", "test") + r := DnsAAAARecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + check.That(data.ResourceName).Key("target_resource_id").HasValue(""), + ), + }, + }) +} + +func (DnsAAAARecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_aaaa_record" "test" { + name = azurerm_dns_aaaa_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsAAAARecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_aaaa_record_resource.go b/internal/services/dns/dns_aaaa_record_resource.go index c8932efd587a..1d99936fb825 100644 --- a/internal/services/dns/dns_aaaa_record_resource.go +++ b/internal/services/dns/dns_aaaa_record_resource.go @@ -2,15 +2,15 @@ package dns import ( "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/set" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -33,8 +33,14 @@ func resourceDnsAAAARecord() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.AaaaRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeAAAA { + return fmt.Errorf("this resource only supports 'AAAA' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -44,11 +50,12 @@ func resourceDnsAAAARecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "records": { @@ -72,7 +79,7 @@ func resourceDnsAAAARecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), "target_resource_id": { Type: pluginsdk.TypeString, @@ -85,7 +92,7 @@ func resourceDnsAAAARecord() *pluginsdk.Resource { } func resourceDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -94,18 +101,17 @@ func resourceDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewAaaaRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeAAAA, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.AAAA) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS AAAA Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_aaaa_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_aaaa_record", id.ID()) } } @@ -114,18 +120,18 @@ func resourceDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface recordsRaw := d.Get("records").(*pluginsdk.Set).List() targetResourceId := d.Get("target_resource_id").(string) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - AaaaRecords: expandAzureRmDnsAaaaRecords(recordsRaw), - TargetResource: &dns.SubResource{}, + AAAARecords: expandAzureRmDnsAaaaRecords(recordsRaw), + TargetResource: &recordsets.SubResource{}, }, } if targetResourceId != "" { - parameters.RecordSetProperties.TargetResource.ID = utils.String(targetResourceId) + parameters.Properties.TargetResource.Id = utils.String(targetResourceId) } // TODO: this can be removed when the provider SDK is upgraded @@ -133,99 +139,103 @@ func resourceDnsAaaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface return fmt.Errorf("One of either `records` or `target_resource_id` must be specified") } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.AAAA, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS AAAA Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) - + d.SetId(id.ID()) return resourceDnsAaaaRecordRead(d, meta) } func resourceDnsAaaaRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AaaaRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := dnsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.AAAAName, dns.AAAA) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS AAAA record %s: %v", id.AAAAName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.AAAAName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("fqdn", resp.Fqdn) - d.Set("ttl", resp.TTL) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("fqdn", props.Fqdn) + d.Set("ttl", props.TTL) - if err := d.Set("records", flattenAzureRmDnsAaaaRecords(resp.AaaaRecords)); err != nil { - return fmt.Errorf("setting `records`: %+v", err) - } + if err := d.Set("records", flattenAzureRmDnsAaaaRecords(props.AAAARecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) - targetResourceId := "" - if resp.TargetResource != nil && resp.TargetResource.ID != nil { - targetResourceId = *resp.TargetResource.ID + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - d.Set("target_resource_id", targetResourceId) - return tags.FlattenAndSet(d, resp.Metadata) + return nil } func resourceDnsAaaaRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AaaaRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := dnsClient.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.AAAAName, dns.AAAA, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS AAAA Record %s: %+v", id.AAAAName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", id.RelativeRecordSetName, err) } return nil } -func expandAzureRmDnsAaaaRecords(input []interface{}) *[]dns.AaaaRecord { - records := make([]dns.AaaaRecord, len(input)) +func expandAzureRmDnsAaaaRecords(input []interface{}) *[]recordsets.AaaaRecord { + records := make([]recordsets.AaaaRecord, len(input)) for i, v := range input { ipv6 := NormalizeIPv6Address(v) - records[i] = dns.AaaaRecord{ - Ipv6Address: &ipv6, + records[i] = recordsets.AaaaRecord{ + IPv6Address: &ipv6, } } return &records } -func flattenAzureRmDnsAaaaRecords(records *[]dns.AaaaRecord) []string { +func flattenAzureRmDnsAaaaRecords(records *[]recordsets.AaaaRecord) []string { if records == nil { return []string{} } results := make([]string, 0) for _, record := range *records { - if record.Ipv6Address == nil { + if record.IPv6Address == nil { continue } - results = append(results, NormalizeIPv6Address(*record.Ipv6Address)) + results = append(results, NormalizeIPv6Address(*record.IPv6Address)) } return results } diff --git a/internal/services/dns/dns_aaaa_record_resource_test.go b/internal/services/dns/dns_aaaa_record_resource_test.go index 30b4e4abfe5a..5185450d72fa 100644 --- a/internal/services/dns/dns_aaaa_record_resource_test.go +++ b/internal/services/dns/dns_aaaa_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -194,17 +193,17 @@ func TestAccAzureRMDnsAAAARecord_uncompressed(t *testing.T) { } func (DnsAAAARecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.AaaaRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.AAAAName, dns.AAAA) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS AAAA record %s (resource group: %s): %v", id.AAAAName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsAAAARecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_caa_record_data_source.go b/internal/services/dns/dns_caa_record_data_source.go new file mode 100644 index 000000000000..a8eea460c0e9 --- /dev/null +++ b/internal/services/dns/dns_caa_record_data_source.go @@ -0,0 +1,112 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsCaaRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsCaaRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "flags": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "tag": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "value": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourceDnsCaaRecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsCaaRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeCAA, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsCaaRecords(props.CaaRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_caa_record_data_source_test.go b/internal/services/dns/dns_caa_record_data_source_test.go new file mode 100644 index 000000000000..4d1574cf9961 --- /dev/null +++ b/internal/services/dns/dns_caa_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsCaaRecordDataSource struct{} + +func TestAccDataSourceDnsCaaRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_caa_record", "test") + r := DnsCaaRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("4"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsCaaRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_caa_record" "test" { + name = azurerm_dns_caa_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsCaaRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_caa_record_resource.go b/internal/services/dns/dns_caa_record_resource.go index f11e333de4cc..4482b9807760 100644 --- a/internal/services/dns/dns_caa_record_resource.go +++ b/internal/services/dns/dns_caa_record_resource.go @@ -3,19 +3,17 @@ package dns import ( "bytes" "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceDnsCaaRecord() *pluginsdk.Resource { @@ -33,8 +31,14 @@ func resourceDnsCaaRecord() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.CaaRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeCAA { + return fmt.Errorf("this resource only supports 'CAA' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -44,11 +48,12 @@ func resourceDnsCaaRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "record": { @@ -90,13 +95,13 @@ func resourceDnsCaaRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsCaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -105,103 +110,122 @@ func resourceDnsCaaRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewCaaRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeCAA, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.CAA) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing DNS CAA Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_caa_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_caa_record", id.ID()) } } ttl := int64(d.Get("ttl").(int)) t := d.Get("tags").(map[string]interface{}) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, CaaRecords: expandAzureRmDnsCaaRecords(d), }, } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.CAA, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS CAA Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsCaaRecordRead(d, meta) } func resourceDnsCaaRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.CaaRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.DnszoneName, id.CAAName, dns.CAA) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS CAA record %s: %v", id.CAAName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.CAAName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) - if err := d.Set("record", flattenAzureRmDnsCaaRecords(resp.CaaRecords)); err != nil { - return err + if err := d.Set("record", flattenAzureRmDnsCaaRecords(props.CaaRecords)); err != nil { + return err + } + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - return tags.FlattenAndSet(d, resp.Metadata) + + return nil } func resourceDnsCaaRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.CaaRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := client.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.CAAName, dns.CAA, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS CAA Record %s: %+v", id.CAAName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func flattenAzureRmDnsCaaRecords(records *[]dns.CaaRecord) []map[string]interface{} { +func flattenAzureRmDnsCaaRecords(records *[]recordsets.CaaRecord) []map[string]interface{} { results := make([]map[string]interface{}, 0) if records != nil { for _, record := range *records { + flags := int64(0) + if record.Flags != nil { + flags = *record.Flags + } + + tag := "" + if record.Tag != nil { + tag = *record.Tag + } + + value := "" + if record.Value != nil { + value = *record.Value + } + results = append(results, map[string]interface{}{ - "flags": *record.Flags, - "tag": *record.Tag, - "value": *record.Value, + "flags": flags, + "tag": tag, + "value": value, }) } } @@ -209,23 +233,22 @@ func flattenAzureRmDnsCaaRecords(records *[]dns.CaaRecord) []map[string]interfac return results } -func expandAzureRmDnsCaaRecords(d *pluginsdk.ResourceData) *[]dns.CaaRecord { +func expandAzureRmDnsCaaRecords(d *pluginsdk.ResourceData) *[]recordsets.CaaRecord { recordStrings := d.Get("record").(*pluginsdk.Set).List() - records := make([]dns.CaaRecord, len(recordStrings)) + records := make([]recordsets.CaaRecord, 0) - for i, v := range recordStrings { + for _, v := range recordStrings { record := v.(map[string]interface{}) - flags := int32(record["flags"].(int)) + + flags := int64(record["flags"].(int)) tag := record["tag"].(string) value := record["value"].(string) - caaRecord := dns.CaaRecord{ + records = append(records, recordsets.CaaRecord{ Flags: &flags, Tag: &tag, Value: &value, - } - - records[i] = caaRecord + }) } return &records diff --git a/internal/services/dns/dns_caa_record_resource_test.go b/internal/services/dns/dns_caa_record_resource_test.go index d4733d22f3f5..e570d88b55ec 100644 --- a/internal/services/dns/dns_caa_record_resource_test.go +++ b/internal/services/dns/dns_caa_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -96,17 +95,17 @@ func TestAccDnsCaaRecord_withTags(t *testing.T) { } func (DnsCaaRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.CaaRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.CAAName, dns.CAA) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS CAA record %s (resource group: %s): %v", id.CAAName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsCaaRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_cname_record_data_source.go b/internal/services/dns/dns_cname_record_data_source.go new file mode 100644 index 000000000000..eb66db443b40 --- /dev/null +++ b/internal/services/dns/dns_cname_record_data_source.go @@ -0,0 +1,107 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsCNameRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsCNameRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "target_resource_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsCNameRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeCNAME, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + cname := "" + if props.CNAMERecord != nil && props.CNAMERecord.Cname != nil { + cname = *props.CNAMERecord.Cname + } + d.Set("record", cname) + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_cname_record_data_source_test.go b/internal/services/dns/dns_cname_record_data_source_test.go new file mode 100644 index 000000000000..aba24f4ed012 --- /dev/null +++ b/internal/services/dns/dns_cname_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsCNameRecordDataSource struct{} + +func TestAccDataSourceDnsCNameRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_cname_record", "test") + r := DnsCNameRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record").HasValue("contoso.com"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsCNameRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_cname_record" "test" { + name = azurerm_dns_cname_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsCNameRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_cname_record_resource.go b/internal/services/dns/dns_cname_record_resource.go index 7b9c40ac7d6d..69c823cf19ee 100644 --- a/internal/services/dns/dns_cname_record_resource.go +++ b/internal/services/dns/dns_cname_record_resource.go @@ -2,15 +2,15 @@ package dns import ( "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -31,8 +31,14 @@ func resourceDnsCNameRecord() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.CnameRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeCNAME { + return fmt.Errorf("this resource only supports 'CNAME' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -42,11 +48,12 @@ func resourceDnsCNameRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "record": { @@ -72,13 +79,13 @@ func resourceDnsCNameRecord() *pluginsdk.Resource { ConflictsWith: []string{"record"}, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -87,18 +94,17 @@ func resourceDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta interfac resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewCnameRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeCNAME, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.CNAME) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS CNAME Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_cname_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_cname_record", id.ID()) } } @@ -107,22 +113,22 @@ func resourceDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta interfac t := d.Get("tags").(map[string]interface{}) targetResourceId := d.Get("target_resource_id").(string) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - CnameRecord: &dns.CnameRecord{}, - TargetResource: &dns.SubResource{}, + CNAMERecord: &recordsets.CnameRecord{}, + TargetResource: &recordsets.SubResource{}, }, } if record != "" { - parameters.RecordSetProperties.CnameRecord.Cname = utils.String(record) + parameters.Properties.CNAMERecord.Cname = utils.String(record) } if targetResourceId != "" { - parameters.RecordSetProperties.TargetResource.ID = utils.String(targetResourceId) + parameters.Properties.TargetResource.Id = utils.String(targetResourceId) } // TODO: this can be removed when the provider SDK is upgraded @@ -130,73 +136,76 @@ func resourceDnsCNameRecordCreateUpdate(d *pluginsdk.ResourceData, meta interfac return fmt.Errorf("One of either `record` or `target_resource_id` must be specified") } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.CNAME, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating CNAME Record %q (DNS Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsCNameRecordRead(d, meta) } func resourceDnsCNameRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.CnameRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := dnsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.CNAMEName, dns.CNAME) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("retrieving CNAME Record %s (DNS Zone %q / Resource Group %q): %+v", id.CNAMEName, id.DnszoneName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.CNAMEName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("fqdn", resp.Fqdn) - d.Set("ttl", resp.TTL) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("fqdn", props.Fqdn) + d.Set("ttl", props.TTL) - if props := resp.RecordSetProperties; props != nil { - cname := "" - if props.CnameRecord != nil && props.CnameRecord.Cname != nil { - cname = *props.CnameRecord.Cname - } - d.Set("record", cname) + cname := "" + if props.CNAMERecord != nil && props.CNAMERecord.Cname != nil { + cname = *props.CNAMERecord.Cname + } + d.Set("record", cname) + + targetResourceId := "" + if props.TargetResource != nil && props.TargetResource.Id != nil { + targetResourceId = *props.TargetResource.Id + } + d.Set("target_resource_id", targetResourceId) - targetResourceId := "" - if props.TargetResource != nil && props.TargetResource.ID != nil { - targetResourceId = *props.TargetResource.ID + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } } - d.Set("target_resource_id", targetResourceId) } - return tags.FlattenAndSet(d, resp.Metadata) + return nil } func resourceDnsCNameRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.CnameRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := dnsClient.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.CNAMEName, dns.CNAME, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting CNAME Record %q (DNS Zone %q / Resource Group %q): %+v", id.CNAMEName, id.DnszoneName, id.ResourceGroup, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/dns/dns_cname_record_resource_test.go b/internal/services/dns/dns_cname_record_resource_test.go index df4834378f41..352d278df48c 100644 --- a/internal/services/dns/dns_cname_record_resource_test.go +++ b/internal/services/dns/dns_cname_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -183,17 +182,17 @@ func TestAccAzureRMDnsCNameRecord_AliasToRecord(t *testing.T) { } func (DnsCNameRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.CnameRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.CNAMEName, dns.CNAME) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS CNAME record %s (resource group: %s): %v", id.CNAMEName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsCNameRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_mx_record_data_source.go b/internal/services/dns/dns_mx_record_data_source.go new file mode 100644 index 000000000000..7905f5d600cb --- /dev/null +++ b/internal/services/dns/dns_mx_record_data_source.go @@ -0,0 +1,108 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsMxRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsMxRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "preference": { + // TODO: this should become an Int + Type: pluginsdk.TypeString, + Computed: true, + }, + + "exchange": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourceDnsMxRecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsMxRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeMX, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsMxRecords(props.MXRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_mx_record_data_source_test.go b/internal/services/dns/dns_mx_record_data_source_test.go new file mode 100644 index 000000000000..d9e2747a3cc5 --- /dev/null +++ b/internal/services/dns/dns_mx_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsMxRecordDataSource struct{} + +func TestAccDataSourceDnsMxRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_mx_record", "test") + r := DnsMxRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsMxRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_mx_record" "test" { + name = azurerm_dns_mx_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsMxRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_mx_record_resource.go b/internal/services/dns/dns_mx_record_resource.go index a74cd20754a5..277e0e13a219 100644 --- a/internal/services/dns/dns_mx_record_resource.go +++ b/internal/services/dns/dns_mx_record_resource.go @@ -3,19 +3,17 @@ package dns import ( "bytes" "fmt" - "net/http" "strconv" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceDnsMxRecord() *pluginsdk.Resource { @@ -33,8 +31,14 @@ func resourceDnsMxRecord() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MxRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeMX { + return fmt.Errorf("this resource only supports 'MX' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -45,11 +49,12 @@ func resourceDnsMxRecord() *pluginsdk.Resource { Default: "@", }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "record": { @@ -82,13 +87,13 @@ func resourceDnsMxRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsMxRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -97,87 +102,92 @@ func resourceDnsMxRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{} resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewMxRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeMX, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.MX) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS MX Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_mx_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_mx_record", id.ID()) } } ttl := int64(d.Get("ttl").(int)) t := d.Get("tags").(map[string]interface{}) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - MxRecords: expandAzureRmDnsMxRecords(d), + MXRecords: expandAzureRmDnsMxRecords(d), }, } - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.MX, parameters, "", ""); err != nil { - return fmt.Errorf("creating/updating DNS MX Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) - + d.SetId(id.ID()) return resourceDnsMxRecordRead(d, meta) } func resourceDnsMxRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MxRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.DnszoneName, id.MXName, dns.MX) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS MX record %s: %v", id.MXName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.MXName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) - if err := d.Set("record", flattenAzureRmDnsMxRecords(resp.MxRecords)); err != nil { - return err + if err := d.Set("record", flattenAzureRmDnsMxRecords(props.MXRecords)); err != nil { + return err + } + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - return tags.FlattenAndSet(d, resp.Metadata) + + return nil } func resourceDnsMxRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MxRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := client.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.MXName, dns.MX, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS MX Record %s: %+v", id.MXName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil @@ -186,16 +196,25 @@ func resourceDnsMxRecordDelete(d *pluginsdk.ResourceData, meta interface{}) erro // flatten creates an array of map where preference is a string to suit // the expectations of the ResourceData schema, so that this data can be // managed by Terradata state. -func flattenAzureRmDnsMxRecords(records *[]dns.MxRecord) []map[string]interface{} { +func flattenAzureRmDnsMxRecords(records *[]recordsets.MxRecord) []map[string]interface{} { results := make([]map[string]interface{}, 0) if records != nil { for _, record := range *records { - preferenceI32 := *record.Preference - preference := strconv.Itoa(int(preferenceI32)) + // TODO: convert this to use an int64 + preference := "" + if record.Preference != nil { + preference = strconv.Itoa(int(*record.Preference)) + } + + exchange := "" + if record.Exchange != nil { + exchange = *record.Exchange + } + results = append(results, map[string]interface{}{ "preference": preference, - "exchange": *record.Exchange, + "exchange": exchange, }) } } @@ -206,19 +225,18 @@ func flattenAzureRmDnsMxRecords(records *[]dns.MxRecord) []map[string]interface{ // expand creates an array of dns.MxRecord, that is, the array needed // by azure-sdk-for-go to manipulate azure resources, hence Preference // is an int32 -func expandAzureRmDnsMxRecords(d *pluginsdk.ResourceData) *[]dns.MxRecord { +func expandAzureRmDnsMxRecords(d *pluginsdk.ResourceData) *[]recordsets.MxRecord { recordStrings := d.Get("record").(*pluginsdk.Set).List() - records := make([]dns.MxRecord, len(recordStrings)) + records := make([]recordsets.MxRecord, len(recordStrings)) for i, v := range recordStrings { mxrecord := v.(map[string]interface{}) preference := mxrecord["preference"].(string) i64, _ := strconv.ParseInt(preference, 10, 32) - i32 := int32(i64) exchange := mxrecord["exchange"].(string) - records[i] = dns.MxRecord{ - Preference: &i32, + records[i] = recordsets.MxRecord{ + Preference: &i64, Exchange: &exchange, } } diff --git a/internal/services/dns/dns_mx_record_resource_test.go b/internal/services/dns/dns_mx_record_resource_test.go index 1faacae7c566..3488d59c47f7 100644 --- a/internal/services/dns/dns_mx_record_resource_test.go +++ b/internal/services/dns/dns_mx_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -112,17 +111,17 @@ func TestAccAzureRMDnsMxRecord_withTags(t *testing.T) { } func (DnsMxRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MxRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.MXName, dns.MX) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS MX record %s (resource group: %s): %v", id.MXName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsMxRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_ns_record_data_source.go b/internal/services/dns/dns_ns_record_data_source.go new file mode 100644 index 000000000000..e2a2cfc6fe85 --- /dev/null +++ b/internal/services/dns/dns_ns_record_data_source.go @@ -0,0 +1,96 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsNsRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsNsRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsNsRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeNS, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmDnsNsRecords(props.NSRecords)); err != nil { + return fmt.Errorf("settings `records`: %+v", err) + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_ns_record_data_source_test.go b/internal/services/dns/dns_ns_record_data_source_test.go new file mode 100644 index 000000000000..2c19ae637224 --- /dev/null +++ b/internal/services/dns/dns_ns_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsNsRecordDataSource struct{} + +func TestAccDataSourceDnsNsRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_ns_record", "test") + r := DnsNsRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsNsRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_ns_record" "test" { + name = azurerm_dns_ns_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsNsRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_ns_record_resource.go b/internal/services/dns/dns_ns_record_resource.go index afe07c4ba3b3..d68c4415ed84 100644 --- a/internal/services/dns/dns_ns_record_resource.go +++ b/internal/services/dns/dns_ns_record_resource.go @@ -2,15 +2,14 @@ package dns import ( "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -30,8 +29,14 @@ func resourceDnsNsRecord() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.NsRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeNS { + return fmt.Errorf("this resource only supports 'NS' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ @@ -41,7 +46,7 @@ func resourceDnsNsRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, @@ -67,13 +72,13 @@ func resourceDnsNsRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsNsRecordCreate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -82,17 +87,16 @@ func resourceDnsNsRecordCreate(d *pluginsdk.ResourceData, meta interface{}) erro resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewNsRecordID(subscriptionId, resGroup, zoneName, name) - - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.NS) + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeNS, name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS NS Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_ns_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_ns_record", id.ID()) } ttl := int64(d.Get("ttl").(int)) @@ -101,123 +105,125 @@ func resourceDnsNsRecordCreate(d *pluginsdk.ResourceData, meta interface{}) erro recordsRaw := d.Get("records").([]interface{}) records := expandAzureRmDnsNsRecords(recordsRaw) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - NsRecords: records, + NSRecords: records, }, } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.NS, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating DNS NS Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) } - d.SetId(resourceId.ID()) - + d.SetId(id.ID()) return resourceDnsNsRecordRead(d, meta) } func resourceDnsNsRecordUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.NsRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - existing, err := client.Get(ctx, id.ResourceGroup, id.DnszoneName, id.NSName, dns.NS) + existing, err := client.Get(ctx, *id) if err != nil { - return fmt.Errorf("retrieving NS %q (DNS Zone %q / Resource Group %q): %+v", id.NSName, id.DnszoneName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - - if existing.RecordSetProperties == nil { - return fmt.Errorf("retrieving NS %q (DNS Zone %q / Resource Group %q): `properties` was nil", id.NSName, id.DnszoneName, id.ResourceGroup) + if existing.Model == nil { + return fmt.Errorf("retrieving %s: `model` was nil", *id) + } + if existing.Model.Properties == nil { + return fmt.Errorf("retrieving %s: `properties` was nil", *id) } if d.HasChange("records") { recordsRaw := d.Get("records").([]interface{}) records := expandAzureRmDnsNsRecords(recordsRaw) - existing.RecordSetProperties.NsRecords = records + existing.Model.Properties.NSRecords = records } if d.HasChange("tags") { t := d.Get("tags").(map[string]interface{}) - existing.RecordSetProperties.Metadata = tags.Expand(t) + existing.Model.Properties.Metadata = tags.Expand(t) } if d.HasChange("ttl") { - existing.RecordSetProperties.TTL = utils.Int64(int64(d.Get("ttl").(int))) + existing.Model.Properties.TTL = utils.Int64(int64(d.Get("ttl").(int))) } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.DnszoneName, id.NSName, dns.NS, existing, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("updating DNS NS Record %q (Zone %q / Resource Group %q): %s", id.NSName, id.DnszoneName, id.ResourceGroup, err) + if _, err := client.Update(ctx, *id, *existing.Model, recordsets.DefaultUpdateOperationOptions()); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } return resourceDnsNsRecordRead(d, meta) } func resourceDnsNsRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.NsRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := dnsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.NSName, dns.NS) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS NS record %s: %+v", id.NSName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.NSName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) - if props := resp.RecordSetProperties; props != nil { - if err := d.Set("records", flattenAzureRmDnsNsRecords(props.NsRecords)); err != nil { - return fmt.Errorf("settings `records`: %+v", err) + if err := d.Set("records", flattenAzureRmDnsNsRecords(props.NSRecords)); err != nil { + return fmt.Errorf("settings `records`: %+v", err) + } + + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } } } - return tags.FlattenAndSet(d, resp.Metadata) + return nil } func resourceDnsNsRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - dnsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.NsRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := dnsClient.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.NSName, dns.NS, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS NS Record %s: %+v", id.NSName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func flattenAzureRmDnsNsRecords(records *[]dns.NsRecord) []interface{} { +func flattenAzureRmDnsNsRecords(records *[]recordsets.NsRecord) []interface{} { if records == nil { return []interface{}{} } @@ -234,16 +240,14 @@ func flattenAzureRmDnsNsRecords(records *[]dns.NsRecord) []interface{} { return results } -func expandAzureRmDnsNsRecords(input []interface{}) *[]dns.NsRecord { - records := make([]dns.NsRecord, len(input)) - for i, v := range input { +func expandAzureRmDnsNsRecords(input []interface{}) *[]recordsets.NsRecord { + records := make([]recordsets.NsRecord, 0) + for _, v := range input { record := v.(string) - nsRecord := dns.NsRecord{ + records = append(records, recordsets.NsRecord{ Nsdname: &record, - } - - records[i] = nsRecord + }) } return &records } diff --git a/internal/services/dns/dns_ns_record_resource_test.go b/internal/services/dns/dns_ns_record_resource_test.go index 4fb91dc6d7c2..a07dd24a9f27 100644 --- a/internal/services/dns/dns_ns_record_resource_test.go +++ b/internal/services/dns/dns_ns_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -96,17 +95,17 @@ func TestAccDnsNsRecord_withTags(t *testing.T) { } func (DnsNsRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.NsRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.NSName, dns.NS) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS NS record %s (resource group: %s): %v", id.NSName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsNsRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_ptr_record_data_source.go b/internal/services/dns/dns_ptr_record_data_source.go new file mode 100644 index 000000000000..b323c1a19069 --- /dev/null +++ b/internal/services/dns/dns_ptr_record_data_source.go @@ -0,0 +1,95 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsPtrRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsPtrRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsPtrRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypePTR, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmDnsPtrRecords(props.PTRRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_ptr_record_data_source_test.go b/internal/services/dns/dns_ptr_record_data_source_test.go new file mode 100644 index 000000000000..289069ad6dac --- /dev/null +++ b/internal/services/dns/dns_ptr_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsPtrRecordDataSource struct{} + +func TestAccDataSourceDnsPtrRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_ptr_record", "test") + r := DnsPtrRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsPtrRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_ptr_record" "test" { + name = azurerm_dns_ptr_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsPtrRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_ptr_record_resource.go b/internal/services/dns/dns_ptr_record_resource.go index de7e5c38e829..8b69bf27b4e4 100644 --- a/internal/services/dns/dns_ptr_record_resource.go +++ b/internal/services/dns/dns_ptr_record_resource.go @@ -2,18 +2,16 @@ package dns import ( "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceDnsPtrRecord() *pluginsdk.Resource { @@ -31,8 +29,14 @@ func resourceDnsPtrRecord() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.PtrRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypePTR { + return fmt.Errorf("this resource only supports 'PTR' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ "name": { @@ -41,11 +45,12 @@ func resourceDnsPtrRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "records": { @@ -65,13 +70,13 @@ func resourceDnsPtrRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsPtrRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -80,104 +85,106 @@ func resourceDnsPtrRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewPtrRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypePTR, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.PTR) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS PTR Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_ptr_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_ptr_record", id.ID()) } } ttl := int64(d.Get("ttl").(int)) t := d.Get("tags").(map[string]interface{}) - parameters := dns.RecordSet{ - RecordSetProperties: &dns.RecordSetProperties{ + parameters := recordsets.RecordSet{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - PtrRecords: expandAzureRmDnsPtrRecords(d), + PTRRecords: expandAzureRmDnsPtrRecords(d), }, } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.PTR, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS PTR Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) - + d.SetId(id.ID()) return resourceDnsPtrRecordRead(d, meta) } func resourceDnsPtrRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client) - dnsClient := client.Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.PtrRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := dnsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.PTRName, dns.PTR) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS PTR record %s: %+v", id.PTRName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.PTRName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - if err := d.Set("records", flattenAzureRmDnsPtrRecords(resp.PtrRecords)); err != nil { - return err + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmDnsPtrRecords(props.PTRRecords)); err != nil { + return err + } + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - return tags.FlattenAndSet(d, resp.Metadata) + + return nil } func resourceDnsPtrRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client) - dnsClient := client.Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.PtrRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := dnsClient.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.PTRName, dns.PTR, "") - if err != nil { - if resp.StatusCode == http.StatusNotFound { - return nil - } - - return fmt.Errorf("deleting DNS PTR Record %s: %+v", id.PTRName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func flattenAzureRmDnsPtrRecords(records *[]dns.PtrRecord) []string { +func flattenAzureRmDnsPtrRecords(records *[]recordsets.PtrRecord) []string { results := make([]string, 0) if records != nil { for _, record := range *records { + if record.Ptrdname == nil { + continue + } + results = append(results, *record.Ptrdname) } } @@ -185,15 +192,15 @@ func flattenAzureRmDnsPtrRecords(records *[]dns.PtrRecord) []string { return results } -func expandAzureRmDnsPtrRecords(d *pluginsdk.ResourceData) *[]dns.PtrRecord { +func expandAzureRmDnsPtrRecords(d *pluginsdk.ResourceData) *[]recordsets.PtrRecord { recordStrings := d.Get("records").(*pluginsdk.Set).List() - records := make([]dns.PtrRecord, len(recordStrings)) + records := make([]recordsets.PtrRecord, 0) - for i, v := range recordStrings { + for _, v := range recordStrings { fqdn := v.(string) - records[i] = dns.PtrRecord{ + records = append(records, recordsets.PtrRecord{ Ptrdname: &fqdn, - } + }) } return &records diff --git a/internal/services/dns/dns_ptr_record_resource_test.go b/internal/services/dns/dns_ptr_record_resource_test.go index 37e964e91c96..433002f635ed 100644 --- a/internal/services/dns/dns_ptr_record_resource_test.go +++ b/internal/services/dns/dns_ptr_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -98,17 +97,17 @@ func TestAccDnsPtrRecord_withTags(t *testing.T) { } func (DnsPtrRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.PtrRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.PTRName, dns.PTR) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS PTR record %s (resource group: %s): %v", id.PTRName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsPtrRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_soa_record_data_source.go b/internal/services/dns/dns_soa_record_data_source.go new file mode 100644 index 000000000000..119408e67a5d --- /dev/null +++ b/internal/services/dns/dns_soa_record_data_source.go @@ -0,0 +1,129 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsSoaRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsSoaRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "@", + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "email": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "host_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "expire_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "minimum_ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "refresh_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "retry_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "serial_number": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsSoaRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeSOA, "@") + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if soaRecord := props.SOARecord; soaRecord != nil { + d.Set("email", soaRecord.Email) + d.Set("host_name", soaRecord.Host) + d.Set("expire_time", soaRecord.ExpireTime) + d.Set("minimum_ttl", soaRecord.MinimumTTL) + d.Set("refresh_time", soaRecord.RefreshTime) + d.Set("retry_time", soaRecord.RetryTime) + d.Set("serial_number", soaRecord.SerialNumber) + } + + return tags.FlattenAndSet(d, props.Metadata) + } + + } + + return nil +} diff --git a/internal/services/dns/dns_soa_record_data_source_test.go b/internal/services/dns/dns_soa_record_data_source_test.go new file mode 100644 index 000000000000..21272538a98d --- /dev/null +++ b/internal/services/dns/dns_soa_record_data_source_test.go @@ -0,0 +1,72 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsSoaRecordDataSource struct{} + +func TestAccDataSourceDnsSoaRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_soa_record", "test") + r := DnsSoaRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basicWithDataSource(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("name").HasValue("@"), + check.That(data.ResourceName).Key("email").HasValue("testemail.com"), + check.That(data.ResourceName).Key("host_name").HasValue("testhost.contoso.com"), + check.That(data.ResourceName).Key("expire_time").HasValue("2419200"), + check.That(data.ResourceName).Key("minimum_ttl").HasValue("300"), + check.That(data.ResourceName).Key("refresh_time").HasValue("3600"), + check.That(data.ResourceName).Key("retry_time").HasValue("300"), + check.That(data.ResourceName).Key("serial_number").HasValue("1"), + check.That(data.ResourceName).Key("ttl").HasValue("3600"), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsSoaRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name + + soa_record { + email = "testemail.com" + host_name = "testhost.contoso.com" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (d DnsSoaRecordDataSource) basicWithDataSource(data acceptance.TestData) string { + config := d.basic(data) + return fmt.Sprintf(` +%s + +data "azurerm_dns_soa_record" "test" { + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, config) +} diff --git a/internal/services/dns/dns_srv_record_data_source.go b/internal/services/dns/dns_srv_record_data_source.go new file mode 100644 index 000000000000..504f8597c610 --- /dev/null +++ b/internal/services/dns/dns_srv_record_data_source.go @@ -0,0 +1,116 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsSrvRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsSrvRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "priority": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "weight": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "port": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "target": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourceDnsSrvRecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsSrvRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeSRV, d.Get("name").(string)) + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsSrvRecords(props.SRVRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/dns/dns_srv_record_data_source_test.go b/internal/services/dns/dns_srv_record_data_source_test.go new file mode 100644 index 000000000000..0383638279b9 --- /dev/null +++ b/internal/services/dns/dns_srv_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsSrvRecordDataSource struct{} + +func TestAccDataSourceDnsSrvRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_srv_record", "test") + r := DnsSrvRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsSrvRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_srv_record" "test" { + name = azurerm_dns_srv_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsSrvRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_srv_record_resource.go b/internal/services/dns/dns_srv_record_resource.go index 5616eaeb8047..025fd126fd52 100644 --- a/internal/services/dns/dns_srv_record_resource.go +++ b/internal/services/dns/dns_srv_record_resource.go @@ -3,18 +3,16 @@ package dns import ( "bytes" "fmt" - "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceDnsSrvRecord() *pluginsdk.Resource { @@ -31,8 +29,14 @@ func resourceDnsSrvRecord() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.SrvRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeSRV { + return fmt.Errorf("this resource only supports 'SRV' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ "name": { @@ -41,11 +45,12 @@ func resourceDnsSrvRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "record": { @@ -87,13 +92,13 @@ func resourceDnsSrvRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsSrvRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -102,103 +107,129 @@ func resourceDnsSrvRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewSrvRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeSRV, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.SRV) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS SRV Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_srv_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_srv_record", id.ID()) } } ttl := int64(d.Get("ttl").(int)) t := d.Get("tags").(map[string]interface{}) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - SrvRecords: expandAzureRmDnsSrvRecords(d), + SRVRecords: expandAzureRmDnsSrvRecords(d), }, } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.SRV, parameters, eTag, ifNoneMatch); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { return fmt.Errorf("creating/updating DNS SRV Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsSrvRecordRead(d, meta) } func resourceDnsSrvRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SrvRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.DnszoneName, id.SRVName, dns.SRV) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS SRV record %s: %v", id.SRVName, err) + + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.SRVName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - if err := d.Set("record", flattenAzureRmDnsSrvRecords(resp.SrvRecords)); err != nil { - return err + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsSrvRecords(props.SRVRecords)); err != nil { + return err + } + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - return tags.FlattenAndSet(d, resp.Metadata) + + return nil } func resourceDnsSrvRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.SrvRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := client.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.SRVName, dns.SRV, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS SRV Record %s: %+v", id.SRVName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func flattenAzureRmDnsSrvRecords(records *[]dns.SrvRecord) []map[string]interface{} { +func flattenAzureRmDnsSrvRecords(records *[]recordsets.SrvRecord) []map[string]interface{} { results := make([]map[string]interface{}, 0) if records != nil { for _, record := range *records { + port := int64(0) + if record.Port != nil { + port = *record.Port + } + + priority := int64(0) + if record.Priority != nil { + priority = *record.Priority + } + + target := "" + if record.Target != nil { + target = *record.Target + } + + weight := int64(0) + if record.Weight != nil { + weight = *record.Weight + } + results = append(results, map[string]interface{}{ - "priority": *record.Priority, - "weight": *record.Weight, - "port": *record.Port, - "target": *record.Target, + "port": port, + "priority": priority, + "target": target, + "weight": weight, }) } } @@ -206,25 +237,23 @@ func flattenAzureRmDnsSrvRecords(records *[]dns.SrvRecord) []map[string]interfac return results } -func expandAzureRmDnsSrvRecords(d *pluginsdk.ResourceData) *[]dns.SrvRecord { +func expandAzureRmDnsSrvRecords(d *pluginsdk.ResourceData) *[]recordsets.SrvRecord { recordStrings := d.Get("record").(*pluginsdk.Set).List() - records := make([]dns.SrvRecord, len(recordStrings)) + records := make([]recordsets.SrvRecord, 0) - for i, v := range recordStrings { + for _, v := range recordStrings { record := v.(map[string]interface{}) - priority := int32(record["priority"].(int)) - weight := int32(record["weight"].(int)) - port := int32(record["port"].(int)) + priority := int64(record["priority"].(int)) + weight := int64(record["weight"].(int)) + port := int64(record["port"].(int)) target := record["target"].(string) - srvRecord := dns.SrvRecord{ + records = append(records, recordsets.SrvRecord{ Priority: &priority, Weight: &weight, Port: &port, Target: &target, - } - - records[i] = srvRecord + }) } return &records diff --git a/internal/services/dns/dns_srv_record_resource_test.go b/internal/services/dns/dns_srv_record_resource_test.go index bbf0f360dc1e..dcda2b2c4e0f 100644 --- a/internal/services/dns/dns_srv_record_resource_test.go +++ b/internal/services/dns/dns_srv_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -96,17 +95,17 @@ func TestAccDnsSrvRecord_withTags(t *testing.T) { } func (DnsSrvRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.SrvRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.SRVName, dns.SRV) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS SRV record %s (resource group: %s): %v", id.SRVName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsSrvRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_txt_record_data_source.go b/internal/services/dns/dns_txt_record_data_source.go new file mode 100644 index 000000000000..1168e6e66af5 --- /dev/null +++ b/internal/services/dns/dns_txt_record_data_source.go @@ -0,0 +1,102 @@ +package dns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourceDnsTxtRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceDnsTxtRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "value": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourceDnsTxtRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).Dns.RecordSets + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeTXT, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsTxtRecords(props.TXTRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + + } + } + + return nil +} diff --git a/internal/services/dns/dns_txt_record_data_source_test.go b/internal/services/dns/dns_txt_record_data_source_test.go new file mode 100644 index 000000000000..34937ee4f2ab --- /dev/null +++ b/internal/services/dns/dns_txt_record_data_source_test.go @@ -0,0 +1,43 @@ +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsTxtRecordDataSource struct{} + +func TestAccDataSourceDnsTxtRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_txt_record", "test") + r := DnsTxtRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsTxtRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_txt_record" "test" { + name = azurerm_dns_txt_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsTxtRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_txt_record_resource.go b/internal/services/dns/dns_txt_record_resource.go index 3d53d2cf318c..24633b8627cc 100644 --- a/internal/services/dns/dns_txt_record_resource.go +++ b/internal/services/dns/dns_txt_record_resource.go @@ -2,20 +2,18 @@ package dns import ( "fmt" - "net/http" "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceDnsTxtRecord() *pluginsdk.Resource { @@ -32,8 +30,14 @@ func resourceDnsTxtRecord() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.TxtRecordID(id) - return err + parsed, err := recordsets.ParseRecordTypeID(id) + if err != nil { + return err + } + if parsed.RecordType != recordsets.RecordTypeTXT { + return fmt.Errorf("this resource only supports 'TXT' records") + } + return nil }), Schema: map[string]*pluginsdk.Schema{ "name": { @@ -42,11 +46,12 @@ func resourceDnsTxtRecord() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "zone_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, }, "record": { @@ -73,13 +78,13 @@ func resourceDnsTxtRecord() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsTxtRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) subscriptionId := meta.(*clients.Client).Account.SubscriptionId defer cancel() @@ -88,115 +93,120 @@ func resourceDnsTxtRecordCreateUpdate(d *pluginsdk.ResourceData, meta interface{ resGroup := d.Get("resource_group_name").(string) zoneName := d.Get("zone_name").(string) - resourceId := parse.NewTxtRecordID(subscriptionId, resGroup, zoneName, name) - + id := recordsets.NewRecordTypeID(subscriptionId, resGroup, zoneName, recordsets.RecordTypeTXT, name) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, zoneName, name, dns.TXT) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS TXT Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_txt_record", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_txt_record", id.ID()) } } ttl := int64(d.Get("ttl").(int)) t := d.Get("tags").(map[string]interface{}) - parameters := dns.RecordSet{ + parameters := recordsets.RecordSet{ Name: &name, - RecordSetProperties: &dns.RecordSetProperties{ + Properties: &recordsets.RecordSetProperties{ Metadata: tags.Expand(t), TTL: &ttl, - TxtRecords: expandAzureRmDnsTxtRecords(d), + TXTRecords: expandAzureRmDnsTxtRecords(d), }, } - eTag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, zoneName, name, dns.TXT, parameters, eTag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS TXT Record %q (Zone %q / Resource Group %q): %s", name, zoneName, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsTxtRecordRead(d, meta) } func resourceDnsTxtRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.TxtRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeIDInsensitively(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.DnszoneName, id.TXTName, dns.TXT) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS TXT record %s: %+v", id.TXTName, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.TXTName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("zone_name", id.DnszoneName) - d.Set("ttl", resp.TTL) - d.Set("fqdn", resp.Fqdn) + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.ZoneName) - if err := d.Set("record", flattenAzureRmDnsTxtRecords(resp.TxtRecords)); err != nil { - return err + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.TTL) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmDnsTxtRecords(props.TXTRecords)); err != nil { + return err + } + if err := tags.FlattenAndSet(d, props.Metadata); err != nil { + return err + } + } } - return tags.FlattenAndSet(d, resp.Metadata) + + return nil } func resourceDnsTxtRecordDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.TxtRecordID(d.Id()) + id, err := recordsets.ParseRecordTypeID(d.Id()) if err != nil { return err } - resp, err := client.Delete(ctx, id.ResourceGroup, id.DnszoneName, id.TXTName, dns.TXT, "") - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("deleting DNS TXT Record %s: %+v", id.TXTName, err) + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func flattenAzureRmDnsTxtRecords(records *[]dns.TxtRecord) []map[string]interface{} { +func flattenAzureRmDnsTxtRecords(records *[]recordsets.TxtRecord) []map[string]interface{} { results := make([]map[string]interface{}, 0) if records != nil { for _, record := range *records { - txtRecord := make(map[string]interface{}) - + value := "" if v := record.Value; v != nil { - value := strings.Join(*v, "") - txtRecord["value"] = value + value = strings.Join(*v, "") } - results = append(results, txtRecord) + results = append(results, map[string]interface{}{ + "value": value, + }) } } return results } -func expandAzureRmDnsTxtRecords(d *pluginsdk.ResourceData) *[]dns.TxtRecord { +func expandAzureRmDnsTxtRecords(d *pluginsdk.ResourceData) *[]recordsets.TxtRecord { recordStrings := d.Get("record").(*pluginsdk.Set).List() - records := make([]dns.TxtRecord, len(recordStrings)) + records := make([]recordsets.TxtRecord, len(recordStrings)) segmentLen := 254 for i, v := range recordStrings { @@ -210,11 +220,9 @@ func expandAzureRmDnsTxtRecords(d *pluginsdk.ResourceData) *[]dns.TxtRecord { } value = append(value, v) - txtRecord := dns.TxtRecord{ + records[i] = recordsets.TxtRecord{ Value: &value, } - - records[i] = txtRecord } return &records diff --git a/internal/services/dns/dns_txt_record_resource_test.go b/internal/services/dns/dns_txt_record_resource_test.go index 48bac60156fd..a1ddfef1fbc7 100644 --- a/internal/services/dns/dns_txt_record_resource_test.go +++ b/internal/services/dns/dns_txt_record_resource_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -96,17 +95,17 @@ func TestAccDnsTxtRecord_withTags(t *testing.T) { } func (DnsTxtRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.TxtRecordID(state.ID) + id, err := recordsets.ParseRecordTypeID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.RecordSetsClient.Get(ctx, id.ResourceGroup, id.DnszoneName, id.TXTName, dns.TXT) + resp, err := clients.Dns.RecordSets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS TXT record %s (resource group: %s): %v", id.TXTName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.RecordSetProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsTxtRecordResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/dns_zone_data_source.go b/internal/services/dns/dns_zone_data_source.go index b931520edc88..bdd483ad11d5 100644 --- a/internal/services/dns/dns_zone_data_source.go +++ b/internal/services/dns/dns_zone_data_source.go @@ -5,13 +5,14 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/zones" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceDnsZone() *pluginsdk.Resource { @@ -29,6 +30,7 @@ func dataSourceDnsZone() *pluginsdk.Resource { }, "resource_group_name": { + // TODO: we need a CommonSchema type for this which doesn't have ForceNew Type: pluginsdk.TypeString, Optional: true, Computed: true, @@ -51,54 +53,53 @@ func dataSourceDnsZone() *pluginsdk.Resource { Set: pluginsdk.HashString, }, - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } func dataSourceDnsZoneRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.ZonesClient - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + client := meta.(*clients.Client).Dns.Zones subscriptionId := meta.(*clients.Client).Account.SubscriptionId - + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - - var ( - resp dns.Zone - err error - ) - if resourceGroup != "" { - resp, err = client.Get(ctx, resourceGroup, name) + id := zones.NewDnsZoneID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + var zone *zones.Zone + if id.ResourceGroupName != "" { + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: DNS Zone %q (Resource Group %q) was not found", name, resourceGroup) + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) } - return fmt.Errorf("reading DNS Zone %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } + + zone = resp.Model } else { - var zone *dns.Zone - zone, resourceGroup, err = findZone(client, ctx, name) + result, resourceGroupName, err := findZone(ctx, client, id.SubscriptionId, id.ZoneName) if err != nil { return err } - if zone == nil { - return fmt.Errorf("Error: DNS Zone %q was not found", name) + if resourceGroupName == nil { + return fmt.Errorf("unable to locate the Resource Group for DNS Zone %q in Subscription %q", id.ResourceGroupName, subscriptionId) } - resp = *zone + zone = result + id.ResourceGroupName = *resourceGroupName } - resourceId := parse.NewDnsZoneID(subscriptionId, resourceGroup, name) - d.SetId(resourceId.ID()) + if zone == nil { + return fmt.Errorf("retrieving %s: `model` was nil", id) + } + + d.SetId(id.ID()) - d.Set("name", name) - d.Set("resource_group_name", resourceGroup) + d.Set("name", id.ZoneName) + d.Set("resource_group_name", id.ResourceGroupName) - if props := resp.ZoneProperties; props != nil { + if props := zone.Properties; props != nil { d.Set("number_of_record_sets", props.NumberOfRecordSets) d.Set("max_number_of_record_sets", props.MaxNumberOfRecordSets) @@ -111,36 +112,37 @@ func dataSourceDnsZoneRead(d *pluginsdk.ResourceData, meta interface{}) error { } } - return tags.FlattenAndSet(d, resp.Tags) + if err := tags.FlattenAndSet(d, zone.Tags); err != nil { + return err + } + + return nil } -func findZone(client *dns.ZonesClient, ctx context.Context, name string) (*dns.Zone, string, error) { - zonesIterator, err := client.ListComplete(ctx, nil) +func findZone(ctx context.Context, client *zones.ZonesClient, subscriptionId, name string) (*zones.Zone, *string, error) { + subscriptionResourceId := commonids.NewSubscriptionID(subscriptionId) + zonesIterator, err := client.ListComplete(ctx, subscriptionResourceId, zones.DefaultListOperationOptions()) if err != nil { - return nil, "", fmt.Errorf("listing DNS Zones: %+v", err) + return nil, nil, fmt.Errorf("listing DNS Zones: %+v", err) } - var found *dns.Zone - for zonesIterator.NotDone() { - zone := zonesIterator.Value() + var found zones.Zone + for _, zone := range zonesIterator.Items { if zone.Name != nil && *zone.Name == name { - if found != nil { - return nil, "", fmt.Errorf("found multiple DNS zones with name %q, please specify the resource group", name) + if found.Id != nil { + return nil, nil, fmt.Errorf("found multiple DNS zones with name %q, please specify the resource group", name) } - found = &zone - } - if err := zonesIterator.NextWithContext(ctx); err != nil { - return nil, "", fmt.Errorf("listing DNS Zones: %+v", err) + found = zone } } - if found == nil || found.ID == nil { - return nil, "", fmt.Errorf("could not find DNS zone with name: %q", name) + if found.Id == nil { + return nil, nil, fmt.Errorf("could not find DNS zone with name: %q", name) } - id, err := parse.DnsZoneID(*found.ID) + id, err := zones.ParseDnsZoneIDInsensitively(*found.Id) if err != nil { - return nil, "", fmt.Errorf("DNS zone id not valid: %+v", err) + return nil, nil, fmt.Errorf("parsing %q as a DNS Zone ID: %+v", *found.Id, err) } - return found, id.ResourceGroup, nil + return &found, &id.ResourceGroupName, nil } diff --git a/internal/services/dns/dns_zone_resource.go b/internal/services/dns/dns_zone_resource.go index 729fe806dc91..eccba9569d7a 100644 --- a/internal/services/dns/dns_zone_resource.go +++ b/internal/services/dns/dns_zone_resource.go @@ -5,14 +5,16 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/recordsets" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/zones" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/migration" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -39,7 +41,7 @@ func resourceDnsZone() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.DnsZoneID(id) + _, err := zones.ParseDnsZoneID(id) return err }), Schema: map[string]*pluginsdk.Schema{ @@ -49,7 +51,7 @@ func resourceDnsZone() *pluginsdk.Resource { ForceNew: true, }, - "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + "resource_group_name": commonschema.ResourceGroupName(), "number_of_record_sets": { Type: pluginsdk.TypeInt, @@ -130,7 +132,7 @@ func resourceDnsZone() *pluginsdk.Resource { ValidateFunc: validation.IntBetween(0, 2147483647), }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), "fqdn": { Type: pluginsdk.TypeString, @@ -140,145 +142,142 @@ func resourceDnsZone() *pluginsdk.Resource { }, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } func resourceDnsZoneCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.ZonesClient - recordSetsClient := meta.(*clients.Client).Dns.RecordSetsClient + client := meta.(*clients.Client).Dns.Zones + recordSetsClient := meta.(*clients.Client).Dns.RecordSets subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resGroup := d.Get("resource_group_name").(string) - - resourceId := parse.NewDnsZoneID(subscriptionId, resGroup, name) - + id := zones.NewDnsZoneID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, resGroup, name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing DNS Zone %q (Resource Group %q): %s", name, resGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { - return tf.ImportAsExistsError("azurerm_dns_zone", resourceId.ID()) + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError("azurerm_dns_zone", id.ID()) } } - location := "global" t := d.Get("tags").(map[string]interface{}) - parameters := dns.Zone{ - Location: &location, + parameters := zones.Zone{ + Location: location.Normalize("global"), Tags: tags.Expand(t), } - etag := "" - ifNoneMatch := "" // set to empty to allow updates to records after creation - if _, err := client.CreateOrUpdate(ctx, resGroup, name, parameters, etag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS Zone %q (Resource Group %q): %s", name, resGroup, err) + if _, err := client.CreateOrUpdate(ctx, id, parameters, zones.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } if v, ok := d.GetOk("soa_record"); ok { soaRecord := v.([]interface{})[0].(map[string]interface{}) - rsParameters := dns.RecordSet{ - RecordSetProperties: &dns.RecordSetProperties{ + rsParameters := recordsets.RecordSet{ + Properties: &recordsets.RecordSetProperties{ TTL: utils.Int64(int64(soaRecord["ttl"].(int))), Metadata: tags.Expand(soaRecord["tags"].(map[string]interface{})), - SoaRecord: expandArmDNSZoneSOARecord(soaRecord), + SOARecord: expandArmDNSZoneSOARecord(soaRecord), }, } - if len(name+strings.TrimSuffix(*rsParameters.RecordSetProperties.SoaRecord.Email, ".")) > 253 { + if len(id.ZoneName+strings.TrimSuffix(*rsParameters.Properties.SOARecord.Email, ".")) > 253 { return fmt.Errorf("`email` which is concatenated with DNS Zone `name` cannot exceed 253 characters excluding a trailing period") } - if _, err := recordSetsClient.CreateOrUpdate(ctx, resGroup, name, "@", dns.SOA, rsParameters, etag, ifNoneMatch); err != nil { - return fmt.Errorf("creating/updating DNS SOA Record @ (Zone %q / Resource Group %q): %s", name, resGroup, err) + soaRecordId := recordsets.NewRecordTypeID(id.SubscriptionId, id.ResourceGroupName, id.ZoneName, recordsets.RecordTypeSOA, "@") + if _, err := recordSetsClient.CreateOrUpdate(ctx, soaRecordId, rsParameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating/updating %s: %+v", soaRecordId, err) } } - d.SetId(resourceId.ID()) + d.SetId(id.ID()) return resourceDnsZoneRead(d, meta) } func resourceDnsZoneRead(d *pluginsdk.ResourceData, meta interface{}) error { - zonesClient := meta.(*clients.Client).Dns.ZonesClient - recordSetsClient := meta.(*clients.Client).Dns.RecordSetsClient + zonesClient := meta.(*clients.Client).Dns.Zones + recordSetsClient := meta.(*clients.Client).Dns.RecordSets ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DnsZoneID(d.Id()) + id, err := zones.ParseDnsZoneIDInsensitively(d.Id()) if err != nil { return err } - resp, err := zonesClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := zonesClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("reading DNS Zone %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - - d.Set("number_of_record_sets", resp.NumberOfRecordSets) - d.Set("max_number_of_record_sets", resp.MaxNumberOfRecordSets) - - nameServers := make([]string, 0) - if s := resp.NameServers; s != nil { - nameServers = *s - } - if err := d.Set("name_servers", nameServers); err != nil { - return err - } - - rsResp, err := recordSetsClient.Get(ctx, id.ResourceGroup, id.Name, "@", dns.SOA) + soaRecord := recordsets.NewRecordTypeID(id.SubscriptionId, id.ResourceGroupName, id.ZoneName, recordsets.RecordTypeSOA, "@") + soaRecordResp, err := recordSetsClient.Get(ctx, soaRecord) if err != nil { - return fmt.Errorf("reading DNS SOA record @: %v", err) + return fmt.Errorf("retrieving %s: %+v", id, err) } - if err := d.Set("soa_record", flattenArmDNSZoneSOARecord(&rsResp)); err != nil { + if err := d.Set("soa_record", flattenArmDNSZoneSOARecord(soaRecordResp.Model)); err != nil { return fmt.Errorf("setting `soa_record`: %+v", err) } - return tags.FlattenAndSet(d, resp.Tags) + d.Set("name", id.ZoneName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("number_of_record_sets", props.NumberOfRecordSets) + d.Set("max_number_of_record_sets", props.MaxNumberOfRecordSets) + + nameServers := make([]string, 0) + if s := props.NameServers; s != nil { + nameServers = *s + } + if err := d.Set("name_servers", nameServers); err != nil { + return err + } + } + + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } + } + + return nil } func resourceDnsZoneDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Dns.ZonesClient + client := meta.(*clients.Client).Dns.Zones ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DnsZoneID(d.Id()) + id, err := zones.ParseDnsZoneID(d.Id()) if err != nil { return err } - etag := "" - future, err := client.Delete(ctx, id.ResourceGroup, id.Name, etag) - if err != nil { - return fmt.Errorf("deleting DNS Zone %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) - } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the deletion of DNS Zone %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if err := client.DeleteThenPoll(ctx, *id, zones.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil } -func expandArmDNSZoneSOARecord(input map[string]interface{}) *dns.SoaRecord { - return &dns.SoaRecord{ +func expandArmDNSZoneSOARecord(input map[string]interface{}) *recordsets.SoaRecord { + return &recordsets.SoaRecord{ Email: utils.String(input["email"].(string)), Host: utils.String(input["host_name"].(string)), ExpireTime: utils.Int64(int64(input["expire_time"].(int))), @@ -289,75 +288,76 @@ func expandArmDNSZoneSOARecord(input map[string]interface{}) *dns.SoaRecord { } } -func flattenArmDNSZoneSOARecord(input *dns.RecordSet) []interface{} { - if input == nil { - return make([]interface{}, 0) - } - - ttl := 0 - if input.TTL != nil { - ttl = int(*input.TTL) - } - - metaData := make(map[string]interface{}) - if input.Metadata != nil { - metaData = tags.Flatten(input.Metadata) - } - - fqdn := "" - if input.Fqdn != nil { - fqdn = *input.Fqdn - } - - email := "" - hostName := "" - expireTime := 0 - minimumTTL := 0 - refreshTime := 0 - retryTime := 0 - serialNumber := 0 - if input.SoaRecord != nil { - if input.SoaRecord.Email != nil { - email = *input.SoaRecord.Email - } - - if input.SoaRecord.Host != nil { - hostName = *input.SoaRecord.Host - } - - if input.SoaRecord.ExpireTime != nil { - expireTime = int(*input.SoaRecord.ExpireTime) - } +func flattenArmDNSZoneSOARecord(input *recordsets.RecordSet) []interface{} { + output := make([]interface{}, 0) + if input != nil { + if props := input.Properties; props != nil { + ttl := 0 + if props.TTL != nil { + ttl = int(*props.TTL) + } - if input.SoaRecord.MinimumTTL != nil { - minimumTTL = int(*input.SoaRecord.MinimumTTL) - } + metaData := make(map[string]interface{}) + if props.Metadata != nil { + metaData = tags.Flatten(props.Metadata) + } - if input.SoaRecord.RefreshTime != nil { - refreshTime = int(*input.SoaRecord.RefreshTime) - } + fqdn := "" + if props.Fqdn != nil { + fqdn = *props.Fqdn + } - if input.SoaRecord.RetryTime != nil { - retryTime = int(*input.SoaRecord.RetryTime) - } + email := "" + hostName := "" + expireTime := 0 + minimumTTL := 0 + refreshTime := 0 + retryTime := 0 + serialNumber := 0 + if record := props.SOARecord; record != nil { + if record.Email != nil { + email = *record.Email + } + + if record.Host != nil { + hostName = *record.Host + } + + if record.ExpireTime != nil { + expireTime = int(*record.ExpireTime) + } + + if record.MinimumTTL != nil { + minimumTTL = int(*record.MinimumTTL) + } + + if record.RefreshTime != nil { + refreshTime = int(*record.RefreshTime) + } + + if record.RetryTime != nil { + retryTime = int(*record.RetryTime) + } + + if record.SerialNumber != nil { + serialNumber = int(*record.SerialNumber) + } + } - if input.SoaRecord.SerialNumber != nil { - serialNumber = int(*input.SoaRecord.SerialNumber) + output = append(output, map[string]interface{}{ + "email": email, + "host_name": hostName, + "expire_time": expireTime, + "minimum_ttl": minimumTTL, + "refresh_time": refreshTime, + "retry_time": retryTime, + "serial_number": serialNumber, + "ttl": ttl, + "tags": metaData, + "fqdn": fqdn, + }) } } - return []interface{}{ - map[string]interface{}{ - "email": email, - "host_name": hostName, - "expire_time": expireTime, - "minimum_ttl": minimumTTL, - "refresh_time": refreshTime, - "retry_time": retryTime, - "serial_number": serialNumber, - "ttl": ttl, - "tags": metaData, - "fqdn": fqdn, - }, - } + return output } diff --git a/internal/services/dns/dns_zone_resource_test.go b/internal/services/dns/dns_zone_resource_test.go index db0dc4d6baa7..bd1f167892fd 100644 --- a/internal/services/dns/dns_zone_resource_test.go +++ b/internal/services/dns/dns_zone_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/zones" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -101,17 +101,17 @@ func TestAccDnsZone_withSOARecord(t *testing.T) { } func (DnsZoneResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.DnsZoneID(state.ID) + id, err := zones.ParseDnsZoneID(state.ID) if err != nil { return nil, err } - resp, err := clients.Dns.ZonesClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.Dns.Zones.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving DNS zone %s (resource group: %s): %v", id.Name, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", id, err) } - return utils.Bool(resp.ZoneProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (DnsZoneResource) basic(data acceptance.TestData) string { diff --git a/internal/services/dns/migration/dns_zone_V0_to_V1.go b/internal/services/dns/migration/dns_zone_V0_to_V1.go index 9222e7d66d47..d67a5af9c083 100644 --- a/internal/services/dns/migration/dns_zone_V0_to_V1.go +++ b/internal/services/dns/migration/dns_zone_V0_to_V1.go @@ -5,8 +5,8 @@ import ( "fmt" "log" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2018-05-01/zones" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -129,20 +129,20 @@ func (DnsZoneV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { groupsClient := meta.(*clients.Client).Resource.GroupsClient oldId := rawState["id"].(string) - id, err := parse.DnsZoneID(oldId) + id, err := zones.ParseDnsZoneID(oldId) if err != nil { return rawState, err } - resGroup, err := groupsClient.Get(ctx, id.ResourceGroup) + resGroup, err := groupsClient.Get(ctx, id.ResourceGroupName) if err != nil { return rawState, err } if resGroup.Name == nil { - return rawState, fmt.Errorf("`name` was nil for Resource Group %q", id.ResourceGroup) + return rawState, fmt.Errorf("`name` was nil for Resource Group %q", id.ResourceGroupName) } resourceGroup := *resGroup.Name name := rawState["name"].(string) - newId := parse.NewDnsZoneID(id.SubscriptionId, resourceGroup, name).ID() + newId := zones.NewDnsZoneID(id.SubscriptionId, resourceGroup, name).ID() log.Printf("Updating `id` from %q to %q", oldId, newId) rawState["id"] = newId return rawState, nil diff --git a/internal/services/dns/parse/a_record.go b/internal/services/dns/parse/a_record.go deleted file mode 100644 index cc22cfdc3cde..000000000000 --- a/internal/services/dns/parse/a_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ARecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - AName string -} - -func NewARecordID(subscriptionId, resourceGroup, dnszoneName, aName string) ARecordId { - return ARecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - AName: aName, - } -} - -func (id ARecordId) String() string { - segments := []string{ - fmt.Sprintf("A Name %q", id.AName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "A Record", segmentsStr) -} - -func (id ARecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/A/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.AName) -} - -// ARecordID parses a ARecord ID into an ARecordId struct -func ARecordID(input string) (*ARecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ARecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.AName, err = id.PopSegment("A"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/a_record_test.go b/internal/services/dns/parse/a_record_test.go deleted file mode 100644 index d7d2894fb685..000000000000 --- a/internal/services/dns/parse/a_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ARecordId{} - -func TestARecordIDFormatter(t *testing.T) { - actual := NewARecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "eh1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/eh1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestARecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ARecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing AName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for AName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/eh1", - Expected: &ARecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - AName: "eh1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/A/EH1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ARecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.AName != v.Expected.AName { - t.Fatalf("Expected %q but got %q for AName", v.Expected.AName, actual.AName) - } - } -} diff --git a/internal/services/dns/parse/aaaa_record.go b/internal/services/dns/parse/aaaa_record.go deleted file mode 100644 index 4c05e28108ca..000000000000 --- a/internal/services/dns/parse/aaaa_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type AaaaRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - AAAAName string -} - -func NewAaaaRecordID(subscriptionId, resourceGroup, dnszoneName, aAAAName string) AaaaRecordId { - return AaaaRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - AAAAName: aAAAName, - } -} - -func (id AaaaRecordId) String() string { - segments := []string{ - fmt.Sprintf("A A A A Name %q", id.AAAAName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Aaaa Record", segmentsStr) -} - -func (id AaaaRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/AAAA/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.AAAAName) -} - -// AaaaRecordID parses a AaaaRecord ID into an AaaaRecordId struct -func AaaaRecordID(input string) (*AaaaRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := AaaaRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.AAAAName, err = id.PopSegment("AAAA"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/aaaa_record_test.go b/internal/services/dns/parse/aaaa_record_test.go deleted file mode 100644 index 0f630220e9cc..000000000000 --- a/internal/services/dns/parse/aaaa_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = AaaaRecordId{} - -func TestAaaaRecordIDFormatter(t *testing.T) { - actual := NewAaaaRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "eheh1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/eheh1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestAaaaRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *AaaaRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing AAAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for AAAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/eheh1", - Expected: &AaaaRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - AAAAName: "eheh1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/AAAA/EHEH1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := AaaaRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.AAAAName != v.Expected.AAAAName { - t.Fatalf("Expected %q but got %q for AAAAName", v.Expected.AAAAName, actual.AAAAName) - } - } -} diff --git a/internal/services/dns/parse/caa_record.go b/internal/services/dns/parse/caa_record.go deleted file mode 100644 index 156be086f698..000000000000 --- a/internal/services/dns/parse/caa_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type CaaRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - CAAName string -} - -func NewCaaRecordID(subscriptionId, resourceGroup, dnszoneName, cAAName string) CaaRecordId { - return CaaRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - CAAName: cAAName, - } -} - -func (id CaaRecordId) String() string { - segments := []string{ - fmt.Sprintf("C A A Name %q", id.CAAName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Caa Record", segmentsStr) -} - -func (id CaaRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/CAA/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.CAAName) -} - -// CaaRecordID parses a CaaRecord ID into an CaaRecordId struct -func CaaRecordID(input string) (*CaaRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := CaaRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.CAAName, err = id.PopSegment("CAA"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/caa_record_test.go b/internal/services/dns/parse/caa_record_test.go deleted file mode 100644 index 80da422f8ec9..000000000000 --- a/internal/services/dns/parse/caa_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = CaaRecordId{} - -func TestCaaRecordIDFormatter(t *testing.T) { - actual := NewCaaRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "caa1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/caa1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestCaaRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *CaaRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing CAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for CAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/caa1", - Expected: &CaaRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - CAAName: "caa1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/CAA/CAA1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := CaaRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.CAAName != v.Expected.CAAName { - t.Fatalf("Expected %q but got %q for CAAName", v.Expected.CAAName, actual.CAAName) - } - } -} diff --git a/internal/services/dns/parse/cname_record.go b/internal/services/dns/parse/cname_record.go deleted file mode 100644 index 1cdf96189e6c..000000000000 --- a/internal/services/dns/parse/cname_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type CnameRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - CNAMEName string -} - -func NewCnameRecordID(subscriptionId, resourceGroup, dnszoneName, cNAMEName string) CnameRecordId { - return CnameRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - CNAMEName: cNAMEName, - } -} - -func (id CnameRecordId) String() string { - segments := []string{ - fmt.Sprintf("C N A M E Name %q", id.CNAMEName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Cname Record", segmentsStr) -} - -func (id CnameRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/CNAME/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.CNAMEName) -} - -// CnameRecordID parses a CnameRecord ID into an CnameRecordId struct -func CnameRecordID(input string) (*CnameRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := CnameRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.CNAMEName, err = id.PopSegment("CNAME"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/cname_record_test.go b/internal/services/dns/parse/cname_record_test.go deleted file mode 100644 index ce81768189b3..000000000000 --- a/internal/services/dns/parse/cname_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = CnameRecordId{} - -func TestCnameRecordIDFormatter(t *testing.T) { - actual := NewCnameRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "name1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/name1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestCnameRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *CnameRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing CNAMEName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for CNAMEName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/name1", - Expected: &CnameRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - CNAMEName: "name1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/CNAME/NAME1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := CnameRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.CNAMEName != v.Expected.CNAMEName { - t.Fatalf("Expected %q but got %q for CNAMEName", v.Expected.CNAMEName, actual.CNAMEName) - } - } -} diff --git a/internal/services/dns/parse/dns_zone.go b/internal/services/dns/parse/dns_zone.go deleted file mode 100644 index 8f4564b18e99..000000000000 --- a/internal/services/dns/parse/dns_zone.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type DnsZoneId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewDnsZoneID(subscriptionId, resourceGroup, name string) DnsZoneId { - return DnsZoneId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id DnsZoneId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Dns Zone", segmentsStr) -} - -func (id DnsZoneId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// DnsZoneID parses a DnsZone ID into an DnsZoneId struct -func DnsZoneID(input string) (*DnsZoneId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := DnsZoneId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/dns_zone_test.go b/internal/services/dns/parse/dns_zone_test.go deleted file mode 100644 index a60db762ba38..000000000000 --- a/internal/services/dns/parse/dns_zone_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = DnsZoneId{} - -func TestDnsZoneIDFormatter(t *testing.T) { - actual := NewDnsZoneID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestDnsZoneID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *DnsZoneId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1", - Expected: &DnsZoneId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "zone1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := DnsZoneID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/dns/parse/mx_record.go b/internal/services/dns/parse/mx_record.go deleted file mode 100644 index b1c6aa955c50..000000000000 --- a/internal/services/dns/parse/mx_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MxRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - MXName string -} - -func NewMxRecordID(subscriptionId, resourceGroup, dnszoneName, mXName string) MxRecordId { - return MxRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - MXName: mXName, - } -} - -func (id MxRecordId) String() string { - segments := []string{ - fmt.Sprintf("M X Name %q", id.MXName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Mx Record", segmentsStr) -} - -func (id MxRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/MX/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.MXName) -} - -// MxRecordID parses a MxRecord ID into an MxRecordId struct -func MxRecordID(input string) (*MxRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MxRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.MXName, err = id.PopSegment("MX"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/mx_record_test.go b/internal/services/dns/parse/mx_record_test.go deleted file mode 100644 index cf4fe1630841..000000000000 --- a/internal/services/dns/parse/mx_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MxRecordId{} - -func TestMxRecordIDFormatter(t *testing.T) { - actual := NewMxRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "mx1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/mx1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMxRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MxRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing MXName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for MXName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/mx1", - Expected: &MxRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - MXName: "mx1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/MX/MX1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MxRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.MXName != v.Expected.MXName { - t.Fatalf("Expected %q but got %q for MXName", v.Expected.MXName, actual.MXName) - } - } -} diff --git a/internal/services/dns/parse/ns_record.go b/internal/services/dns/parse/ns_record.go deleted file mode 100644 index dd8399f70f59..000000000000 --- a/internal/services/dns/parse/ns_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type NsRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - NSName string -} - -func NewNsRecordID(subscriptionId, resourceGroup, dnszoneName, nSName string) NsRecordId { - return NsRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - NSName: nSName, - } -} - -func (id NsRecordId) String() string { - segments := []string{ - fmt.Sprintf("N S Name %q", id.NSName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Ns Record", segmentsStr) -} - -func (id NsRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/NS/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.NSName) -} - -// NsRecordID parses a NsRecord ID into an NsRecordId struct -func NsRecordID(input string) (*NsRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := NsRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.NSName, err = id.PopSegment("NS"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/ns_record_test.go b/internal/services/dns/parse/ns_record_test.go deleted file mode 100644 index 694bf6359516..000000000000 --- a/internal/services/dns/parse/ns_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = NsRecordId{} - -func TestNsRecordIDFormatter(t *testing.T) { - actual := NewNsRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "ns1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/ns1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestNsRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *NsRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing NSName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for NSName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/ns1", - Expected: &NsRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - NSName: "ns1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/NS/NS1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := NsRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.NSName != v.Expected.NSName { - t.Fatalf("Expected %q but got %q for NSName", v.Expected.NSName, actual.NSName) - } - } -} diff --git a/internal/services/dns/parse/ptr_record.go b/internal/services/dns/parse/ptr_record.go deleted file mode 100644 index e0b4965f0bd3..000000000000 --- a/internal/services/dns/parse/ptr_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type PtrRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - PTRName string -} - -func NewPtrRecordID(subscriptionId, resourceGroup, dnszoneName, pTRName string) PtrRecordId { - return PtrRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - PTRName: pTRName, - } -} - -func (id PtrRecordId) String() string { - segments := []string{ - fmt.Sprintf("P T R Name %q", id.PTRName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Ptr Record", segmentsStr) -} - -func (id PtrRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/PTR/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.PTRName) -} - -// PtrRecordID parses a PtrRecord ID into an PtrRecordId struct -func PtrRecordID(input string) (*PtrRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := PtrRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.PTRName, err = id.PopSegment("PTR"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/ptr_record_test.go b/internal/services/dns/parse/ptr_record_test.go deleted file mode 100644 index f5f0a3fd7ec3..000000000000 --- a/internal/services/dns/parse/ptr_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = PtrRecordId{} - -func TestPtrRecordIDFormatter(t *testing.T) { - actual := NewPtrRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "ptr1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/ptr1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestPtrRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *PtrRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing PTRName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for PTRName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/ptr1", - Expected: &PtrRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - PTRName: "ptr1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/PTR/PTR1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := PtrRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.PTRName != v.Expected.PTRName { - t.Fatalf("Expected %q but got %q for PTRName", v.Expected.PTRName, actual.PTRName) - } - } -} diff --git a/internal/services/dns/parse/soa_record.go b/internal/services/dns/parse/soa_record.go new file mode 100644 index 000000000000..350c5604a64a --- /dev/null +++ b/internal/services/dns/parse/soa_record.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type SoaRecordId struct { + SubscriptionId string + ResourceGroup string + DnszoneName string + SOAName string +} + +func NewSoaRecordID(subscriptionId, resourceGroup, dnszoneName, sOAName string) SoaRecordId { + return SoaRecordId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + DnszoneName: dnszoneName, + SOAName: sOAName, + } +} + +func (id SoaRecordId) String() string { + segments := []string{ + fmt.Sprintf("SOA Name %q", id.SOAName), + fmt.Sprintf("Dnszone Name %q", id.DnszoneName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "SOA Record", segmentsStr) +} + +func (id SoaRecordId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/SOA/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.SOAName) +} + +// SoaRecordID parses a SOARecord ID into an SoaRecordId struct +func SoaRecordID(input string) (*SoaRecordId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := SoaRecordId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { + return nil, err + } + if resourceId.SOAName, err = id.PopSegment("SOA"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/dns/parse/srv_record.go b/internal/services/dns/parse/srv_record.go deleted file mode 100644 index 2006f14bbd63..000000000000 --- a/internal/services/dns/parse/srv_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type SrvRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - SRVName string -} - -func NewSrvRecordID(subscriptionId, resourceGroup, dnszoneName, sRVName string) SrvRecordId { - return SrvRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - SRVName: sRVName, - } -} - -func (id SrvRecordId) String() string { - segments := []string{ - fmt.Sprintf("S R V Name %q", id.SRVName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Srv Record", segmentsStr) -} - -func (id SrvRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/SRV/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.SRVName) -} - -// SrvRecordID parses a SrvRecord ID into an SrvRecordId struct -func SrvRecordID(input string) (*SrvRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := SrvRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.SRVName, err = id.PopSegment("SRV"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/srv_record_test.go b/internal/services/dns/parse/srv_record_test.go deleted file mode 100644 index 14d3f25dab55..000000000000 --- a/internal/services/dns/parse/srv_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = SrvRecordId{} - -func TestSrvRecordIDFormatter(t *testing.T) { - actual := NewSrvRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "srv1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/srv1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestSrvRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *SrvRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing SRVName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for SRVName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/srv1", - Expected: &SrvRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - SRVName: "srv1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/SRV/SRV1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := SrvRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.SRVName != v.Expected.SRVName { - t.Fatalf("Expected %q but got %q for SRVName", v.Expected.SRVName, actual.SRVName) - } - } -} diff --git a/internal/services/dns/parse/txt_record.go b/internal/services/dns/parse/txt_record.go deleted file mode 100644 index 57b0bae23501..000000000000 --- a/internal/services/dns/parse/txt_record.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type TxtRecordId struct { - SubscriptionId string - ResourceGroup string - DnszoneName string - TXTName string -} - -func NewTxtRecordID(subscriptionId, resourceGroup, dnszoneName, tXTName string) TxtRecordId { - return TxtRecordId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - DnszoneName: dnszoneName, - TXTName: tXTName, - } -} - -func (id TxtRecordId) String() string { - segments := []string{ - fmt.Sprintf("T X T Name %q", id.TXTName), - fmt.Sprintf("Dnszone Name %q", id.DnszoneName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Txt Record", segmentsStr) -} - -func (id TxtRecordId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/dnszones/%s/TXT/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.DnszoneName, id.TXTName) -} - -// TxtRecordID parses a TxtRecord ID into an TxtRecordId struct -func TxtRecordID(input string) (*TxtRecordId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := TxtRecordId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.DnszoneName, err = id.PopSegment("dnszones"); err != nil { - return nil, err - } - if resourceId.TXTName, err = id.PopSegment("TXT"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/dns/parse/txt_record_test.go b/internal/services/dns/parse/txt_record_test.go deleted file mode 100644 index 37b0d53ec2bc..000000000000 --- a/internal/services/dns/parse/txt_record_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = TxtRecordId{} - -func TestTxtRecordIDFormatter(t *testing.T) { - actual := NewTxtRecordID("12345678-1234-9876-4563-123456789012", "resGroup1", "zone1", "txt1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/txt1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestTxtRecordID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *TxtRecordId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Error: true, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Error: true, - }, - - { - // missing TXTName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Error: true, - }, - - { - // missing value for TXTName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/txt1", - Expected: &TxtRecordId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - DnszoneName: "zone1", - TXTName: "txt1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/TXT/TXT1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := TxtRecordID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.DnszoneName != v.Expected.DnszoneName { - t.Fatalf("Expected %q but got %q for DnszoneName", v.Expected.DnszoneName, actual.DnszoneName) - } - if actual.TXTName != v.Expected.TXTName { - t.Fatalf("Expected %q but got %q for TXTName", v.Expected.TXTName, actual.TXTName) - } - } -} diff --git a/internal/services/dns/registration.go b/internal/services/dns/registration.go index 4316c2bb110b..34bd173367d4 100644 --- a/internal/services/dns/registration.go +++ b/internal/services/dns/registration.go @@ -28,7 +28,17 @@ func (r Registration) WebsiteCategories() []string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_dns_zone": dataSourceDnsZone(), + "azurerm_dns_a_record": dataSourceDnsARecord(), + "azurerm_dns_aaaa_record": dataSourceDnsAAAARecord(), + "azurerm_dns_caa_record": dataSourceDnsCaaRecord(), + "azurerm_dns_cname_record": dataSourceDnsCNameRecord(), + "azurerm_dns_mx_record": dataSourceDnsMxRecord(), + "azurerm_dns_ns_record": dataSourceDnsNsRecord(), + "azurerm_dns_ptr_record": dataSourceDnsPtrRecord(), + "azurerm_dns_soa_record": dataSourceDnsSoaRecord(), + "azurerm_dns_srv_record": dataSourceDnsSrvRecord(), + "azurerm_dns_txt_record": dataSourceDnsTxtRecord(), + "azurerm_dns_zone": dataSourceDnsZone(), } } diff --git a/internal/services/dns/resourceids.go b/internal/services/dns/resourceids.go deleted file mode 100644 index 4ad419bec34f..000000000000 --- a/internal/services/dns/resourceids.go +++ /dev/null @@ -1,12 +0,0 @@ -package dns - -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DnsZone -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ARecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/eh1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AaaaRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/eheh1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=CaaRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/caa1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=CnameRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/name1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MxRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/mx1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NsRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/ns1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=PtrRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/ptr1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SrvRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/srv1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=TxtRecord -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/txt1 diff --git a/internal/services/dns/validate/a_record_id.go b/internal/services/dns/validate/a_record_id.go deleted file mode 100644 index 6d70f11164a0..000000000000 --- a/internal/services/dns/validate/a_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func ARecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ARecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/a_record_id_test.go b/internal/services/dns/validate/a_record_id_test.go deleted file mode 100644 index f6ccc517f5d5..000000000000 --- a/internal/services/dns/validate/a_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestARecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing AName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for AName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/A/eh1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/A/EH1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ARecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/aaaa_record_id.go b/internal/services/dns/validate/aaaa_record_id.go deleted file mode 100644 index dd75910b3a70..000000000000 --- a/internal/services/dns/validate/aaaa_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func AaaaRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.AaaaRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/aaaa_record_id_test.go b/internal/services/dns/validate/aaaa_record_id_test.go deleted file mode 100644 index 89378cfceaf9..000000000000 --- a/internal/services/dns/validate/aaaa_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestAaaaRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing AAAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for AAAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/AAAA/eheh1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/AAAA/EHEH1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := AaaaRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/caa_record_id.go b/internal/services/dns/validate/caa_record_id.go deleted file mode 100644 index 4fd6b17485f7..000000000000 --- a/internal/services/dns/validate/caa_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func CaaRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.CaaRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/caa_record_id_test.go b/internal/services/dns/validate/caa_record_id_test.go deleted file mode 100644 index aa05cd29ef88..000000000000 --- a/internal/services/dns/validate/caa_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestCaaRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing CAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for CAAName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CAA/caa1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/CAA/CAA1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := CaaRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/cname_record_id.go b/internal/services/dns/validate/cname_record_id.go deleted file mode 100644 index a0a20845f322..000000000000 --- a/internal/services/dns/validate/cname_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func CnameRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.CnameRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/cname_record_id_test.go b/internal/services/dns/validate/cname_record_id_test.go deleted file mode 100644 index 23c4075e7ccf..000000000000 --- a/internal/services/dns/validate/cname_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestCnameRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing CNAMEName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for CNAMEName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/CNAME/name1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/CNAME/NAME1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := CnameRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/dns_zone_id.go b/internal/services/dns/validate/dns_zone_id.go deleted file mode 100644 index 32e9b7d30b17..000000000000 --- a/internal/services/dns/validate/dns_zone_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func DnsZoneID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.DnsZoneID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/dns_zone_id_test.go b/internal/services/dns/validate/dns_zone_id_test.go deleted file mode 100644 index e98912a40947..000000000000 --- a/internal/services/dns/validate/dns_zone_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestDnsZoneID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := DnsZoneID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/mx_record_id.go b/internal/services/dns/validate/mx_record_id.go deleted file mode 100644 index 5bea8660e56e..000000000000 --- a/internal/services/dns/validate/mx_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func MxRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MxRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/mx_record_id_test.go b/internal/services/dns/validate/mx_record_id_test.go deleted file mode 100644 index ec999a7d517a..000000000000 --- a/internal/services/dns/validate/mx_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMxRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing MXName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for MXName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/MX/mx1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/MX/MX1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MxRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/ns_record_id.go b/internal/services/dns/validate/ns_record_id.go deleted file mode 100644 index 6b808841c897..000000000000 --- a/internal/services/dns/validate/ns_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func NsRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.NsRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/ns_record_id_test.go b/internal/services/dns/validate/ns_record_id_test.go deleted file mode 100644 index c9710d386833..000000000000 --- a/internal/services/dns/validate/ns_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestNsRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing NSName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for NSName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/NS/ns1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/NS/NS1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := NsRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/ptr_record_id.go b/internal/services/dns/validate/ptr_record_id.go deleted file mode 100644 index 444679b22c17..000000000000 --- a/internal/services/dns/validate/ptr_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func PtrRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.PtrRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/ptr_record_id_test.go b/internal/services/dns/validate/ptr_record_id_test.go deleted file mode 100644 index 7e526241c55c..000000000000 --- a/internal/services/dns/validate/ptr_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestPtrRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing PTRName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for PTRName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/PTR/ptr1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/PTR/PTR1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := PtrRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/srv_record_id.go b/internal/services/dns/validate/srv_record_id.go deleted file mode 100644 index 988b99abfc23..000000000000 --- a/internal/services/dns/validate/srv_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func SrvRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.SrvRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/srv_record_id_test.go b/internal/services/dns/validate/srv_record_id_test.go deleted file mode 100644 index d48d19c45af8..000000000000 --- a/internal/services/dns/validate/srv_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestSrvRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing SRVName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for SRVName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/SRV/srv1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/SRV/SRV1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := SrvRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/dns/validate/txt_record_id.go b/internal/services/dns/validate/txt_record_id.go deleted file mode 100644 index d4d2b6abad51..000000000000 --- a/internal/services/dns/validate/txt_record_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/parse" -) - -func TxtRecordID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.TxtRecordID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/dns/validate/txt_record_id_test.go b/internal/services/dns/validate/txt_record_id_test.go deleted file mode 100644 index eb23b2b72f55..000000000000 --- a/internal/services/dns/validate/txt_record_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestTxtRecordID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", - Valid: false, - }, - - { - // missing value for DnszoneName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/", - Valid: false, - }, - - { - // missing TXTName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/", - Valid: false, - }, - - { - // missing value for TXTName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/dnszones/zone1/TXT/txt1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/DNSZONES/ZONE1/TXT/TXT1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := TxtRecordID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/domainservices/active_directory_domain_service_data_source.go b/internal/services/domainservices/active_directory_domain_service_data_source.go index 0785b42199ab..d31b0bb164a4 100644 --- a/internal/services/domainservices/active_directory_domain_service_data_source.go +++ b/internal/services/domainservices/active_directory_domain_service_data_source.go @@ -4,15 +4,15 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceActiveDirectoryDomainService() *pluginsdk.Resource { @@ -129,6 +129,16 @@ func dataSourceActiveDirectoryDomainService() *pluginsdk.Resource { Computed: true, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ + "kerberos_armoring_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "kerberos_rc4_encryption_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + "ntlm_v1_enabled": { Type: pluginsdk.TypeBool, Computed: true, @@ -167,7 +177,7 @@ func dataSourceActiveDirectoryDomainService() *pluginsdk.Resource { Computed: true, }, - "tags": tags.SchemaDataSource(), + "tags": commonschema.Tags(), "tenant_id": { Type: pluginsdk.TypeString, @@ -220,32 +230,43 @@ func dataSourceActiveDirectoryDomainServiceReplicaSetSchema() map[string]*plugin func dataSourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).DomainServices.DomainServicesClient + subscrptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - resp, err := client.Get(ctx, resourceGroup, name) + idsdk := domainservices.NewDomainServiceID(subscrptionId, resourceGroup, name) + + resp, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return nil } return err } - if resp.ID == nil { + model := resp.Model + if model == nil { + return fmt.Errorf("reading Domain Service: model was returned nil") + } + + if model.Id == nil { return fmt.Errorf("reading Domain Service: ID was returned nil") } - d.SetId(*resp.ID) + + d.SetId(idsdk.ID()) d.Set("name", name) d.Set("resource_group_name", resourceGroup) + d.Set("location", location.NormalizeNilable(model.Location)) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } - d.Set("location", location.NormalizeNilable(resp.Location)) - - if props := resp.DomainServiceProperties; props != nil { - d.Set("deployment_id", props.DeploymentID) + if props := model.Properties; props != nil { + d.Set("deployment_id", props.DeploymentId) domainConfigType := "" if v := props.DomainConfigurationType; v != nil { @@ -256,14 +277,14 @@ func dataSourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta d.Set("domain_name", props.DomainName) d.Set("filtered_sync_enabled", false) - if props.FilteredSync == aad.FilteredSyncEnabled { + if props.FilteredSync != nil && *props.FilteredSync == domainservices.FilteredSyncEnabled { d.Set("filtered_sync_enabled", true) } - d.Set("resource_id", resp.ID) + d.Set("resource_id", model.Id) d.Set("sku", props.Sku) d.Set("sync_owner", props.SyncOwner) - d.Set("tenant_id", props.TenantID) + d.Set("tenant_id", props.TenantId) d.Set("version", props.Version) if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil { @@ -284,5 +305,5 @@ func dataSourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/domainservices/active_directory_domain_service_replica_set_resource.go b/internal/services/domainservices/active_directory_domain_service_replica_set_resource.go index 9059a63e5fbe..9d5a88fe02f5 100644 --- a/internal/services/domainservices/active_directory_domain_service_replica_set_resource.go +++ b/internal/services/domainservices/active_directory_domain_service_replica_set_resource.go @@ -5,8 +5,9 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -88,82 +89,90 @@ func resourceActiveDirectoryDomainServiceReplicaSetCreate(d *pluginsdk.ResourceD return fmt.Errorf("parsing ID for Domain Service Replica Set") } + idsdk := domainservices.NewDomainServiceID(domainServiceId.SubscriptionId, domainServiceId.ResourceGroup, domainServiceId.Name) + locks.ByName(domainServiceId.Name, DomainServiceResourceName) defer locks.UnlockByName(domainServiceId.Name, DomainServiceResourceName) - domainService, err := client.Get(ctx, domainServiceId.ResourceGroup, domainServiceId.Name) + domainService, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(domainService.Response) { + if response.WasNotFound(domainService.HttpResponse) { return fmt.Errorf("could not find %s: %s", domainServiceId, err) } return fmt.Errorf("reading %s: %s", domainServiceId, err) } - if domainService.DomainServiceProperties.ReplicaSets == nil || len(*domainService.DomainServiceProperties.ReplicaSets) == 0 { - return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set: %s", domainServiceId, err) + model := domainService.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", domainServiceId) + } + + if model.Properties == nil || model.Properties.ReplicaSets == nil || len(*model.Properties.ReplicaSets) == 0 { + return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set", domainServiceId) } subnetId := d.Get("subnet_id").(string) - replicaSets := *domainService.DomainServiceProperties.ReplicaSets + replicaSets := *model.Properties.ReplicaSets for _, r := range replicaSets { - if r.ReplicaSetID == nil { + if r.ReplicaSetId == nil { return fmt.Errorf("reading %s: a replica set was returned with a missing ReplicaSetID", domainServiceId) } - if r.SubnetID == nil { + if r.SubnetId == nil { return fmt.Errorf("reading %s: a replica set was returned with a missing SubnetID", domainServiceId) } // We assume that two replica sets cannot coexist in the same subnet - if strings.EqualFold(subnetId, *r.SubnetID) { + if strings.EqualFold(subnetId, *r.SubnetId) { // Generate an ID here since we only know it once we know the ReplicaSetID - id := parse.NewDomainServiceReplicaSetID(domainServiceId.SubscriptionId, domainServiceId.ResourceGroup, domainServiceId.Name, *r.ReplicaSetID) + id := parse.NewDomainServiceReplicaSetID(domainServiceId.SubscriptionId, domainServiceId.ResourceGroup, domainServiceId.Name, *r.ReplicaSetId) return tf.ImportAsExistsError("azurerm_active_directory_domain_service_replica_set", id.ID()) } } loc := location.Normalize(d.Get("location").(string)) - replicaSets = append(replicaSets, aad.ReplicaSet{ + replicaSets = append(replicaSets, domainservices.ReplicaSet{ Location: utils.String(loc), - SubnetID: utils.String(subnetId), + SubnetId: utils.String(subnetId), }) - domainService.DomainServiceProperties.ReplicaSets = &replicaSets + model.Properties.ReplicaSets = &replicaSets - future, err := client.CreateOrUpdate(ctx, domainServiceId.ResourceGroup, domainServiceId.Name, domainService) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, idsdk, *model); err != nil { return fmt.Errorf("creating/updating Replica Sets for %s: %+v", domainServiceId, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for Replica Sets for %s: %+v", domainServiceId, err) - } // We need to retrieve the domain service again to find out the new replica set ID - domainService, err = client.Get(ctx, domainServiceId.ResourceGroup, domainServiceId.Name) + domainService, err = client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(domainService.Response) { + if response.WasNotFound(domainService.HttpResponse) { return fmt.Errorf("could not find %s: %s", domainServiceId, err) } return fmt.Errorf("reading %s: %s", domainServiceId, err) } - if domainService.DomainServiceProperties.ReplicaSets == nil || len(*domainService.DomainServiceProperties.ReplicaSets) == 0 { - return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set: %s", domainServiceId, err) + model = domainService.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", domainServiceId) + } + + if model.Properties == nil || model.Properties.ReplicaSets == nil || len(*model.Properties.ReplicaSets) == 0 { + return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set", domainServiceId) } var id parse.DomainServiceReplicaSetId // Assuming that two replica sets cannot coexist in the same subnet, we identify our new replica set by its SubnetID - for _, r := range *domainService.DomainServiceProperties.ReplicaSets { - if r.ReplicaSetID == nil { + for _, r := range *model.Properties.ReplicaSets { + if r.ReplicaSetId == nil { return fmt.Errorf("reading %s: a replica set was returned with a missing ReplicaSetID", domainServiceId) } - if r.SubnetID == nil { + if r.SubnetId == nil { return fmt.Errorf("reading %s: a replica set was returned with a missing SubnetID", domainServiceId) } - if strings.EqualFold(subnetId, *r.SubnetID) { + if strings.EqualFold(subnetId, *r.SubnetId) { // We found it! - id = parse.NewDomainServiceReplicaSetID(domainServiceId.SubscriptionId, domainServiceId.ResourceGroup, domainServiceId.Name, *r.ReplicaSetID) + id = parse.NewDomainServiceReplicaSetID(domainServiceId.SubscriptionId, domainServiceId.ResourceGroup, domainServiceId.Name, *r.ReplicaSetId) } } @@ -201,41 +210,48 @@ func resourceActiveDirectoryDomainServiceReplicaSetRead(d *pluginsdk.ResourceDat return err } - domainService, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) + + domainService, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(domainService.Response) { + if response.WasNotFound(domainService.HttpResponse) { d.SetId("") return nil } return err } - if domainService.DomainServiceProperties.ReplicaSets == nil || len(*domainService.DomainServiceProperties.ReplicaSets) == 0 { - return fmt.Errorf("reading %s: domain service returned with missing replica set information, expected at least 1 replica set: %s", id, err) + model := domainService.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", id) + } + + if model.Properties == nil || model.Properties.ReplicaSets == nil || len(*model.Properties.ReplicaSets) == 0 { + return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set", id) } var ( - domainControllerIpAddresses []string - externalAccessIpAddress string + domainControllerIPAddresses []string + externalAccessIPAddress string loc string serviceStatus string subnetId string ) - replicaSets := *domainService.DomainServiceProperties.ReplicaSets + replicaSets := *model.Properties.ReplicaSets for _, r := range replicaSets { - if r.ReplicaSetID == nil { + if r.ReplicaSetId == nil { return fmt.Errorf("reading %s: a replica set was returned with a missing ReplicaSetID", id) } // ReplicaSetName in the ID struct is really the replica set ID - if *r.ReplicaSetID == id.ReplicaSetName { + if *r.ReplicaSetId == id.ReplicaSetName { if r.DomainControllerIPAddress != nil { - domainControllerIpAddresses = *r.DomainControllerIPAddress + domainControllerIPAddresses = *r.DomainControllerIPAddress } if r.ExternalAccessIPAddress != nil { - externalAccessIpAddress = *r.ExternalAccessIPAddress + externalAccessIPAddress = *r.ExternalAccessIPAddress } if r.Location != nil { loc = location.NormalizeNilable(r.Location) @@ -243,14 +259,14 @@ func resourceActiveDirectoryDomainServiceReplicaSetRead(d *pluginsdk.ResourceDat if r.ServiceStatus != nil { serviceStatus = *r.ServiceStatus } - if r.SubnetID != nil { - subnetId = *r.SubnetID + if r.SubnetId != nil { + subnetId = *r.SubnetId } } } - d.Set("domain_controller_ip_addresses", domainControllerIpAddresses) - d.Set("external_access_ip_address", externalAccessIpAddress) + d.Set("domain_controller_ip_addresses", domainControllerIPAddresses) + d.Set("external_access_ip_address", externalAccessIPAddress) d.Set("location", loc) d.Set("service_status", serviceStatus) d.Set("subnet_id", subnetId) @@ -268,27 +284,34 @@ func resourceActiveDirectoryDomainServiceReplicaSetDelete(d *pluginsdk.ResourceD return err } - domainService, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) + + domainService, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(domainService.Response) { + if response.WasNotFound(domainService.HttpResponse) { return fmt.Errorf("deleting %s: domain service was not found: %s", id, err) } return err } - if domainService.DomainServiceProperties.ReplicaSets == nil || len(*domainService.DomainServiceProperties.ReplicaSets) == 0 { - return fmt.Errorf("deleting %s: domain service returned with missing replica set information, expected at least 1 replica set: %s", id, err) + model := domainService.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", id) } - replicaSets := *domainService.DomainServiceProperties.ReplicaSets + if model.Properties == nil || model.Properties.ReplicaSets == nil || len(*model.Properties.ReplicaSets) == 0 { + return fmt.Errorf("reading %s: returned with missing replica set information, expected at least 1 replica set", id) + } - newReplicaSets := make([]aad.ReplicaSet, 0) + replicaSets := *model.Properties.ReplicaSets + + newReplicaSets := make([]domainservices.ReplicaSet, 0) for _, r := range replicaSets { - if r.ReplicaSetID == nil { + if r.ReplicaSetId == nil { return fmt.Errorf("deleting %s: a replica set was returned with a missing ReplicaSetID", id) } - if *r.ReplicaSetID == id.ReplicaSetName { + if *r.ReplicaSetId == id.ReplicaSetName { continue } @@ -299,15 +322,11 @@ func resourceActiveDirectoryDomainServiceReplicaSetDelete(d *pluginsdk.ResourceD return fmt.Errorf("deleting %s: could not determine which replica set to remove", id) } - domainService.DomainServiceProperties.ReplicaSets = &newReplicaSets + model.Properties.ReplicaSets = &newReplicaSets - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.DomainServiceName, domainService) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, idsdk, *model); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", id, err) - } // Wait for all replica sets to become available with two domain controllers each before proceeding // Generate a partial DomainServiceId since we don't need to know the initial replica set ID here diff --git a/internal/services/domainservices/active_directory_domain_service_resource.go b/internal/services/domainservices/active_directory_domain_service_resource.go index 102a41f641b1..82e7b0571215 100644 --- a/internal/services/domainservices/active_directory_domain_service_resource.go +++ b/internal/services/domainservices/active_directory_domain_service_resource.go @@ -7,18 +7,17 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/domainservices/parse" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -54,9 +53,9 @@ func resourceActiveDirectoryDomainService() *pluginsdk.Resource { ValidateFunc: validation.StringIsNotEmpty, // TODO: proper validation }, - "location": azure.SchemaLocation(), + "location": commonschema.Location(), - "resource_group_name": azure.SchemaResourceGroupName(), + "resource_group_name": commonschema.ResourceGroupName(), "domain_name": { Type: pluginsdk.TypeString, @@ -211,6 +210,18 @@ func resourceActiveDirectoryDomainService() *pluginsdk.Resource { MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ + "kerberos_armoring_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "kerberos_rc4_encryption_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "ntlm_v1_enabled": { Type: pluginsdk.TypeBool, Optional: true, @@ -254,7 +265,7 @@ func resourceActiveDirectoryDomainService() *pluginsdk.Resource { }, false), }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), "deployment_id": { Type: pluginsdk.TypeString, @@ -286,6 +297,7 @@ func resourceActiveDirectoryDomainService() *pluginsdk.Resource { func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).DomainServices.DomainServicesClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -300,18 +312,24 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, // know the ID of the first replica set. var id *parse.DomainServiceId + idsdk := domainservices.NewDomainServiceID(subscriptionId, resourceGroup, name) + if d.IsNewResource() { - existing, err := client.Get(ctx, resourceGroup, name) + existing, err := client.Get(ctx, idsdk) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %s", resourceErrorName, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { // Parse the replica sets and assume the first one returned to be the initial replica set // This is a best effort and the user can choose any replica set if they structure their config accordingly - props := existing.DomainServiceProperties + model := existing.Model + if model == nil { + return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing model", resourceErrorName) + } + props := model.Properties if props == nil { return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing properties", resourceErrorName) } @@ -320,7 +338,7 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing replica set details", resourceErrorName) } initialReplicaSetId := replicaSets[0].(map[string]interface{})["id"].(string) - id := parse.NewDomainServiceID(client.SubscriptionID, resourceGroup, name, initialReplicaSetId) + id := parse.NewDomainServiceID(subscriptionId, resourceGroup, name, initialReplicaSetId) return tf.ImportAsExistsError(DomainServiceResourceName, id.ID()) } @@ -336,16 +354,16 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, } loc := location.Normalize(d.Get("location").(string)) - filteredSync := aad.FilteredSyncDisabled + filteredSync := domainservices.FilteredSyncDisabled if d.Get("filtered_sync_enabled").(bool) { - filteredSync = aad.FilteredSyncDisabled + filteredSync = domainservices.FilteredSyncDisabled } - domainService := aad.DomainService{ - DomainServiceProperties: &aad.DomainServiceProperties{ + domainService := domainservices.DomainService{ + Properties: &domainservices.DomainServiceProperties{ DomainName: utils.String(d.Get("domain_name").(string)), DomainSecuritySettings: expandDomainServiceSecurity(d.Get("security").([]interface{})), - FilteredSync: filteredSync, + FilteredSync: &filteredSync, LdapsSettings: expandDomainServiceLdaps(d.Get("secure_ldap").([]interface{})), NotificationSettings: expandDomainServiceNotifications(d.Get("notifications").([]interface{})), Sku: utils.String(d.Get("sku").(string)), @@ -355,36 +373,36 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, } if v := d.Get("domain_configuration_type").(string); v != "" { - domainService.DomainServiceProperties.DomainConfigurationType = &v + domainService.Properties.DomainConfigurationType = &v } if d.IsNewResource() { // On resource creation, specify the initial replica set. // No provision is made for changing the initial replica set, it should remain intact for the resource to function properly - replicaSets := []aad.ReplicaSet{ + replicaSets := []domainservices.ReplicaSet{ { Location: utils.String(loc), - SubnetID: utils.String(d.Get("initial_replica_set.0.subnet_id").(string)), + SubnetId: utils.String(d.Get("initial_replica_set.0.subnet_id").(string)), }, } - domainService.DomainServiceProperties.ReplicaSets = &replicaSets + domainService.Properties.ReplicaSets = &replicaSets } - future, err := client.CreateOrUpdate(ctx, resourceGroup, name, domainService) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, idsdk, domainService); err != nil { return fmt.Errorf("creating/updating %s: %+v", resourceErrorName, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for %s: %+v", resourceErrorName, err) - } // Retrieve the domain service to discover the unique ID for the initial replica set, which should not subsequently change if d.IsNewResource() { - resp, err := client.Get(ctx, resourceGroup, name) + resp, err := client.Get(ctx, idsdk) if err != nil { return fmt.Errorf("retrieving %s after creating: %+v", resourceErrorName, err) } - props := resp.DomainServiceProperties + model := resp.Model + if model == nil { + return fmt.Errorf("%s returned with no model", resourceErrorName) + } + props := model.Properties if props == nil { return fmt.Errorf("%s returned with no properties", resourceErrorName) } @@ -399,7 +417,7 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *pluginsdk.ResourceData, // Once we know the initial replica set ID, we can build a resource ID initialReplicaSetId := replicaSets[0].(map[string]interface{})["id"].(string) - newId := parse.NewDomainServiceID(client.SubscriptionID, resourceGroup, name, initialReplicaSetId) + newId := parse.NewDomainServiceID(subscriptionId, resourceGroup, name, initialReplicaSetId) id = &newId d.SetId(id.ID()) @@ -441,9 +459,11 @@ func resourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta in return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.Name) + + resp, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } @@ -452,59 +472,64 @@ func resourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta in d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - d.Set("resource_id", resp.ID) - d.Set("location", location.NormalizeNilable(resp.Location)) + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(model.Location)) + d.Set("resource_id", model.Id) + if err := tags.FlattenAndSet(d, model.Tags); err != nil { + return err + } - if props := resp.DomainServiceProperties; props != nil { - d.Set("deployment_id", props.DeploymentID) - d.Set("domain_name", props.DomainName) - d.Set("sync_owner", props.SyncOwner) - d.Set("tenant_id", props.TenantID) - d.Set("version", props.Version) - d.Set("domain_configuration_type", props.DomainConfigurationType) + if props := model.Properties; props != nil { + d.Set("deployment_id", props.DeploymentId) + d.Set("domain_name", props.DomainName) + d.Set("sync_owner", props.SyncOwner) + d.Set("tenant_id", props.TenantId) + d.Set("version", props.Version) + d.Set("domain_configuration_type", props.DomainConfigurationType) - d.Set("filtered_sync_enabled", false) - if props.FilteredSync == aad.FilteredSyncEnabled { - d.Set("filtered_sync_enabled", true) - } + d.Set("filtered_sync_enabled", false) + if props.FilteredSync != nil && *props.FilteredSync == domainservices.FilteredSyncEnabled { + d.Set("filtered_sync_enabled", true) + } - d.Set("sku", props.Sku) + d.Set("sku", props.Sku) - if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil { - return fmt.Errorf("setting `notifications`: %+v", err) - } + if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil { + return fmt.Errorf("setting `notifications`: %+v", err) + } - var initialReplicaSet interface{} - replicaSets := flattenDomainServiceReplicaSets(props.ReplicaSets) + var initialReplicaSet interface{} + replicaSets := flattenDomainServiceReplicaSets(props.ReplicaSets) - // Determine the initial replica set. This is why we need to include InitialReplicaSetId in the resource ID, - // without it we would not be able to reliably support importing. - for _, replicaSetRaw := range replicaSets { - replicaSet := replicaSetRaw.(map[string]interface{}) - if replicaSet["id"].(string) == id.InitialReplicaSetIdName { - initialReplicaSet = replicaSetRaw - break + // Determine the initial replica set. This is why we need to include InitialReplicaSetId in the resource ID, + // without it we would not be able to reliably support importing. + for _, replicaSetRaw := range replicaSets { + replicaSet := replicaSetRaw.(map[string]interface{}) + if replicaSet["id"].(string) == id.InitialReplicaSetIdName { + initialReplicaSet = replicaSetRaw + break + } + } + if initialReplicaSet == nil { + // It's safest to error out here, since we don't want to wipe the initial replica set from state if it was deleted manually + return fmt.Errorf("reading %s: could not determine initial replica set from API response", id) + } + if err := d.Set("initial_replica_set", []interface{}{initialReplicaSet}); err != nil { + return fmt.Errorf("setting `initial_replica_set`: %+v", err) } - } - if initialReplicaSet == nil { - // It's safest to error out here, since we don't want to wipe the initial replica set from state if it was deleted manually - return fmt.Errorf("reading %s: could not determine initial replica set from API response", id) - } - if err := d.Set("initial_replica_set", []interface{}{initialReplicaSet}); err != nil { - return fmt.Errorf("setting `initial_replica_set`: %+v", err) - } - if err := d.Set("secure_ldap", flattenDomainServiceLdaps(d, props.LdapsSettings, false)); err != nil { - return fmt.Errorf("setting `secure_ldap`: %+v", err) - } + if err := d.Set("secure_ldap", flattenDomainServiceLdaps(d, props.LdapsSettings, false)); err != nil { + return fmt.Errorf("setting `secure_ldap`: %+v", err) + } - if err := d.Set("security", flattenDomainServiceSecurity(props.DomainSecuritySettings)); err != nil { - return fmt.Errorf("setting `security`: %+v", err) + if err := d.Set("security", flattenDomainServiceSecurity(props.DomainSecuritySettings)); err != nil { + return fmt.Errorf("setting `security`: %+v", err) + } } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourceActiveDirectoryDomainServiceDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -517,32 +542,28 @@ func resourceActiveDirectoryDomainServiceDelete(d *pluginsdk.ResourceData, meta return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.Name) - if err != nil { - return fmt.Errorf("deleting %s: %+v", id, err) - } + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.Name) - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - if !response.WasNotFound(future.Response()) { - return fmt.Errorf("waiting for deletion of %s: %+v", id, err) - } + if err := client.DeleteThenPoll(ctx, idsdk); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) } return nil } -func domainServiceControllerRefreshFunc(ctx context.Context, client *aad.DomainServicesClient, id parse.DomainServiceId, deleting bool) pluginsdk.StateRefreshFunc { +func domainServiceControllerRefreshFunc(ctx context.Context, client *domainservices.DomainServicesClient, id parse.DomainServiceId, deleting bool) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { log.Printf("[DEBUG] Waiting for domain controllers to deploy...") - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, idsdk) if err != nil { return nil, "error", err } - if resp.DomainServiceProperties == nil || resp.DomainServiceProperties.ReplicaSets == nil || len(*resp.DomainServiceProperties.ReplicaSets) == 0 { + if model := resp.Model; model == nil || model.Properties == nil || model.Properties.ReplicaSets == nil || len(*model.Properties.ReplicaSets) == 0 { return nil, "error", fmt.Errorf("API error: `replicaSets` was not returned") } // Loop through all replica sets and ensure they are running and each have two available domain controllers - for _, repl := range *resp.DomainServiceProperties.ReplicaSets { + for _, repl := range *resp.Model.Properties.ReplicaSets { if repl.ServiceStatus == nil { return resp, "pending", nil } @@ -565,29 +586,30 @@ func domainServiceControllerRefreshFunc(ctx context.Context, client *aad.DomainS } } -func expandDomainServiceLdaps(input []interface{}) (ldaps *aad.LdapsSettings) { - ldaps = &aad.LdapsSettings{ - Ldaps: aad.LdapsDisabled, +func expandDomainServiceLdaps(input []interface{}) (ldaps *domainservices.LdapsSettings) { + state := domainservices.LdapsDisabled + ldaps = &domainservices.LdapsSettings{ + Ldaps: &state, } if len(input) > 0 { v := input[0].(map[string]interface{}) if v["enabled"].(bool) { - ldaps.Ldaps = aad.LdapsEnabled + *ldaps.Ldaps = domainservices.LdapsEnabled } ldaps.PfxCertificate = utils.String(v["pfx_certificate"].(string)) ldaps.PfxCertificatePassword = utils.String(v["pfx_certificate_password"].(string)) + access := domainservices.ExternalAccessDisabled if v["external_access_enabled"].(bool) { - ldaps.ExternalAccess = aad.Enabled - } else { - ldaps.ExternalAccess = aad.Disabled + access = domainservices.ExternalAccessEnabled } + ldaps.ExternalAccess = &access } return } -func expandDomainServiceNotifications(input []interface{}) *aad.NotificationSettings { +func expandDomainServiceNotifications(input []interface{}) *domainservices.NotificationSettings { if len(input) == 0 { return nil } @@ -601,61 +623,71 @@ func expandDomainServiceNotifications(input []interface{}) *aad.NotificationSett } } - notifyDcAdmins := aad.NotifyDcAdminsDisabled + notifyDcAdmins := domainservices.NotifyDcAdminsDisabled if n, ok := v["notify_dc_admins"]; ok && n.(bool) { - notifyDcAdmins = aad.NotifyDcAdminsEnabled + notifyDcAdmins = domainservices.NotifyDcAdminsEnabled } - notifyGlobalAdmins := aad.NotifyGlobalAdminsDisabled + notifyGlobalAdmins := domainservices.NotifyGlobalAdminsDisabled if n, ok := v["notify_global_admins"]; ok && n.(bool) { - notifyGlobalAdmins = aad.NotifyGlobalAdminsEnabled + notifyGlobalAdmins = domainservices.NotifyGlobalAdminsEnabled } - return &aad.NotificationSettings{ + return &domainservices.NotificationSettings{ AdditionalRecipients: &additionalRecipients, - NotifyDcAdmins: notifyDcAdmins, - NotifyGlobalAdmins: notifyGlobalAdmins, + NotifyDcAdmins: ¬ifyDcAdmins, + NotifyGlobalAdmins: ¬ifyGlobalAdmins, } } -func expandDomainServiceSecurity(input []interface{}) *aad.DomainSecuritySettings { +func expandDomainServiceSecurity(input []interface{}) *domainservices.DomainSecuritySettings { if len(input) == 0 { return nil } v := input[0].(map[string]interface{}) - ntlmV1 := aad.NtlmV1Disabled - syncKerberosPasswords := aad.SyncKerberosPasswordsDisabled - syncNtlmPasswords := aad.SyncNtlmPasswordsDisabled - syncOnPremPasswords := aad.SyncOnPremPasswordsDisabled - tlsV1 := aad.TLSV1Disabled + kerberosRc4Encryption := domainservices.KerberosRc4EncryptionDisabled + kerberosArmoring := domainservices.KerberosArmoringDisabled + ntlmV1 := domainservices.NtlmV1Disabled + syncKerberosPasswords := domainservices.SyncKerberosPasswordsDisabled + syncNtlmPasswords := domainservices.SyncNtlmPasswordsDisabled + syncOnPremPasswords := domainservices.SyncOnPremPasswordsDisabled + tlsV1 := domainservices.TlsV1Disabled + if v["kerberos_armoring_enabled"].(bool) { + kerberosArmoring = domainservices.KerberosArmoringEnabled + } + if v["kerberos_rc4_encryption_enabled"].(bool) { + kerberosRc4Encryption = domainservices.KerberosRc4EncryptionEnabled + } if v["ntlm_v1_enabled"].(bool) { - ntlmV1 = aad.NtlmV1Enabled + ntlmV1 = domainservices.NtlmV1Enabled } if v["sync_kerberos_passwords"].(bool) { - syncKerberosPasswords = aad.SyncKerberosPasswordsEnabled + syncKerberosPasswords = domainservices.SyncKerberosPasswordsEnabled } if v["sync_ntlm_passwords"].(bool) { - syncNtlmPasswords = aad.SyncNtlmPasswordsEnabled + syncNtlmPasswords = domainservices.SyncNtlmPasswordsEnabled } if v["sync_on_prem_passwords"].(bool) { - syncOnPremPasswords = aad.SyncOnPremPasswordsEnabled + syncOnPremPasswords = domainservices.SyncOnPremPasswordsEnabled } if v["tls_v1_enabled"].(bool) { - tlsV1 = aad.TLSV1Enabled + tlsV1 = domainservices.TlsV1Enabled } - return &aad.DomainSecuritySettings{ - NtlmV1: ntlmV1, - SyncKerberosPasswords: syncKerberosPasswords, - SyncNtlmPasswords: syncNtlmPasswords, - SyncOnPremPasswords: syncOnPremPasswords, - TLSV1: tlsV1, + return &domainservices.DomainSecuritySettings{ + KerberosArmoring: &kerberosArmoring, + KerberosRc4Encryption: &kerberosRc4Encryption, + NtlmV1: &ntlmV1, + SyncKerberosPasswords: &syncKerberosPasswords, + SyncNtlmPasswords: &syncNtlmPasswords, + SyncOnPremPasswords: &syncOnPremPasswords, + TlsV1: &tlsV1, } } -func flattenDomainServiceLdaps(d *pluginsdk.ResourceData, input *aad.LdapsSettings, dataSource bool) []interface{} { +func flattenDomainServiceLdaps(d *pluginsdk.ResourceData, input *domainservices.LdapsSettings, dataSource bool) []interface{} { result := map[string]interface{}{ "enabled": false, "external_access_enabled": false, @@ -677,14 +709,14 @@ func flattenDomainServiceLdaps(d *pluginsdk.ResourceData, input *aad.LdapsSettin } if input != nil { - if input.ExternalAccess == aad.Enabled { + if input.ExternalAccess != nil && *input.ExternalAccess == domainservices.ExternalAccessEnabled { result["external_access_enabled"] = true } - if input.Ldaps == aad.LdapsEnabled { + if input.Ldaps != nil && *input.Ldaps == domainservices.LdapsEnabled { result["enabled"] = true } if v := input.CertificateNotAfter; v != nil { - result["certificate_expiry"] = v.Format(time.RFC3339) + result["certificate_expiry"] = *v } if v := input.CertificateThumbprint; v != nil { result["certificate_thumbprint"] = *v @@ -697,7 +729,7 @@ func flattenDomainServiceLdaps(d *pluginsdk.ResourceData, input *aad.LdapsSettin return []interface{}{result} } -func flattenDomainServiceNotifications(input *aad.NotificationSettings) []interface{} { +func flattenDomainServiceNotifications(input *domainservices.NotificationSettings) []interface{} { if input == nil { return make([]interface{}, 0) } @@ -710,17 +742,17 @@ func flattenDomainServiceNotifications(input *aad.NotificationSettings) []interf if input.AdditionalRecipients != nil { result["additional_recipients"] = *input.AdditionalRecipients } - if input.NotifyDcAdmins == aad.NotifyDcAdminsEnabled { + if input.NotifyDcAdmins != nil && *input.NotifyDcAdmins == domainservices.NotifyDcAdminsEnabled { result["notify_dc_admins"] = true } - if input.NotifyGlobalAdmins == aad.NotifyGlobalAdminsEnabled { + if input.NotifyGlobalAdmins != nil && *input.NotifyGlobalAdmins == domainservices.NotifyGlobalAdminsEnabled { result["notify_global_admins"] = true } return []interface{}{result} } -func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{}) { +func flattenDomainServiceReplicaSets(input *[]domainservices.ReplicaSet) (ret []interface{}) { if input == nil { return } @@ -740,14 +772,14 @@ func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{} if in.ExternalAccessIPAddress != nil { repl["external_access_ip_address"] = *in.ExternalAccessIPAddress } - if in.ReplicaSetID != nil { - repl["id"] = *in.ReplicaSetID + if in.ReplicaSetId != nil { + repl["id"] = *in.ReplicaSetId } if in.ServiceStatus != nil { repl["service_status"] = *in.ServiceStatus } - if in.SubnetID != nil { - repl["subnet_id"] = *in.SubnetID + if in.SubnetId != nil { + repl["subnet_id"] = *in.SubnetId } ret = append(ret, repl) } @@ -755,31 +787,39 @@ func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{} return } -func flattenDomainServiceSecurity(input *aad.DomainSecuritySettings) []interface{} { +func flattenDomainServiceSecurity(input *domainservices.DomainSecuritySettings) []interface{} { if input == nil { return make([]interface{}, 0) } result := map[string]bool{ - "ntlm_v1_enabled": false, - "sync_kerberos_passwords": false, - "sync_ntlm_passwords": false, - "sync_on_prem_passwords": false, - "tls_v1_enabled": false, + "kerberos_armoring_enabled": false, + "kerberos_rc4_encryption_enabled": false, + "ntlm_v1_enabled": false, + "sync_kerberos_passwords": false, + "sync_ntlm_passwords": false, + "sync_on_prem_passwords": false, + "tls_v1_enabled": false, + } + if input.KerberosArmoring != nil && *input.KerberosArmoring == domainservices.KerberosArmoringEnabled { + result["kerberos_armoring_enabled"] = true + } + if input.KerberosRc4Encryption != nil && *input.KerberosRc4Encryption == domainservices.KerberosRc4EncryptionEnabled { + result["kerberos_rc4_encryption_enabled"] = true } - if input.NtlmV1 == aad.NtlmV1Enabled { + if input.NtlmV1 != nil && *input.NtlmV1 == domainservices.NtlmV1Enabled { result["ntlm_v1_enabled"] = true } - if input.SyncKerberosPasswords == aad.SyncKerberosPasswordsEnabled { + if input.SyncKerberosPasswords != nil && *input.SyncKerberosPasswords == domainservices.SyncKerberosPasswordsEnabled { result["sync_kerberos_passwords"] = true } - if input.SyncNtlmPasswords == aad.SyncNtlmPasswordsEnabled { + if input.SyncNtlmPasswords != nil && *input.SyncNtlmPasswords == domainservices.SyncNtlmPasswordsEnabled { result["sync_ntlm_passwords"] = true } - if input.SyncOnPremPasswords == aad.SyncOnPremPasswordsEnabled { + if input.SyncOnPremPasswords != nil && *input.SyncOnPremPasswords == domainservices.SyncOnPremPasswordsEnabled { result["sync_on_prem_passwords"] = true } - if input.TLSV1 == aad.TLSV1Enabled { + if input.TlsV1 != nil && *input.TlsV1 == domainservices.TlsV1Enabled { result["tls_v1_enabled"] = true } diff --git a/internal/services/domainservices/active_directory_domain_service_test.go b/internal/services/domainservices/active_directory_domain_service_test.go index 87e0e8e03976..2ff4bfc0a711 100644 --- a/internal/services/domainservices/active_directory_domain_service_test.go +++ b/internal/services/domainservices/active_directory_domain_service_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" @@ -124,6 +125,8 @@ func TestAccActiveDirectoryDomainService_updateWithDatasource(t *testing.T) { check.That(dataSourceData.ResourceName).Key("secure_ldap.0.external_access_enabled").HasValue("true"), check.That(dataSourceData.ResourceName).Key("secure_ldap.0.public_certificate").Exists(), check.That(dataSourceData.ResourceName).Key("security.#").HasValue("1"), + check.That(dataSourceData.ResourceName).Key("security.0.kerberos_armoring_enabled").HasValue("true"), + check.That(dataSourceData.ResourceName).Key("security.0.kerberos_rc4_encryption_enabled").HasValue("true"), check.That(dataSourceData.ResourceName).Key("security.0.ntlm_v1_enabled").HasValue("true"), check.That(dataSourceData.ResourceName).Key("security.0.sync_kerberos_passwords").HasValue("true"), check.That(dataSourceData.ResourceName).Key("security.0.sync_ntlm_passwords").HasValue("true"), @@ -161,12 +164,14 @@ func (ActiveDirectoryDomainServiceResource) Exists(ctx context.Context, client * return nil, err } - resp, err := client.DomainServices.DomainServicesClient.Get(ctx, id.ResourceGroup, id.Name) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.Name) + + resp, err := client.DomainServices.DomainServicesClient.Get(ctx, idsdk) if err != nil { return nil, fmt.Errorf("reading DomainService: %+v", err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil && resp.Model.Id != nil), nil } func (ActiveDirectoryDomainServiceReplicaSetResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -175,17 +180,29 @@ func (ActiveDirectoryDomainServiceReplicaSetResource) Exists(ctx context.Context return nil, err } - resp, err := client.DomainServices.DomainServicesClient.Get(ctx, id.ResourceGroup, id.DomainServiceName) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) + + resp, err := client.DomainServices.DomainServicesClient.Get(ctx, idsdk) if err != nil { return nil, fmt.Errorf("reading DomainService: %+v", err) } - if resp.ReplicaSets == nil || len(*resp.ReplicaSets) == 0 { + model := resp.Model + if model == nil { + return nil, fmt.Errorf("DomainService response returned with nil model") + } + + props := model.Properties + if props == nil { + return nil, fmt.Errorf("DomainService response returned with nil properties") + } + + if props.ReplicaSets == nil || len(*props.ReplicaSets) == 0 { return nil, fmt.Errorf("DomainService response returned with nil or empty replicaSets") } - for _, replica := range *resp.ReplicaSets { - if replica.ReplicaSetID != nil && *replica.ReplicaSetID == id.ReplicaSetName { + for _, replica := range *props.ReplicaSets { + if replica.ReplicaSetId != nil && *replica.ReplicaSetId == id.ReplicaSetName { return utils.Bool(true), nil } } @@ -339,11 +356,13 @@ resource "azurerm_active_directory_domain_service" "test" { } security { - ntlm_v1_enabled = true - sync_kerberos_passwords = true - sync_ntlm_passwords = true - sync_on_prem_passwords = true - tls_v1_enabled = true + kerberos_armoring_enabled = true + kerberos_rc4_encryption_enabled = true + ntlm_v1_enabled = true + sync_kerberos_passwords = true + sync_ntlm_passwords = true + sync_on_prem_passwords = true + tls_v1_enabled = true } tags = { diff --git a/internal/services/domainservices/active_directory_domain_service_trust_resource.go b/internal/services/domainservices/active_directory_domain_service_trust_resource.go index 7578996f5588..f3082a2dc692 100644 --- a/internal/services/domainservices/active_directory_domain_service_trust_resource.go +++ b/internal/services/domainservices/active_directory_domain_service_trust_resource.go @@ -6,7 +6,8 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/domainservices/parse" @@ -98,16 +99,23 @@ func (r DomainServiceTrustResource) Create() sdk.ResourceFunc { } id := parse.NewDomainServiceTrustID(dsid.SubscriptionId, dsid.ResourceGroup, dsid.Name, plan.Name) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) locks.ByName(id.DomainServiceName, DomainServiceResourceName) defer locks.UnlockByName(id.DomainServiceName, DomainServiceResourceName) - existing, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + existing, err := client.Get(ctx, idsdk) if err != nil { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } - existingTrusts := []aad.ForestTrust{} - if props := existing.DomainServiceProperties; props != nil { + + model := existing.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", idsdk) + } + + existingTrusts := []domainservices.ForestTrust{} + if props := model.Properties; props != nil { if fsettings := props.ResourceForestSettings; fsettings != nil { if settings := fsettings.Settings; settings != nil { existingTrusts = *settings @@ -120,28 +128,24 @@ func (r DomainServiceTrustResource) Create() sdk.ResourceFunc { } } - existingTrusts = append(existingTrusts, aad.ForestTrust{ + existingTrusts = append(existingTrusts, domainservices.ForestTrust{ TrustedDomainFqdn: utils.String(plan.TrustedDomainFqdn), TrustDirection: utils.String("Inbound"), FriendlyName: utils.String(id.TrustName), - RemoteDNSIps: utils.String(strings.Join(plan.TrustedDomainDnsIPs, ",")), + RemoteDnsIPs: utils.String(strings.Join(plan.TrustedDomainDnsIPs, ",")), TrustPassword: utils.String(plan.Password), }) - params := aad.DomainService{ - DomainServiceProperties: &aad.DomainServiceProperties{ - ResourceForestSettings: &aad.ResourceForestSettings{ + params := domainservices.DomainService{ + Properties: &domainservices.DomainServiceProperties{ + ResourceForestSettings: &domainservices.ResourceForestSettings{ Settings: &existingTrusts, }, }, } - future, err := client.Update(ctx, id.ResourceGroup, id.DomainServiceName, params) - if err != nil { + if err := client.UpdateThenPoll(ctx, idsdk, params); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of %s: %+v", id, err) - } metadata.SetID(id) return nil @@ -160,22 +164,27 @@ func (r DomainServiceTrustResource) Read() sdk.ResourceFunc { return err } - resourceErrorName := fmt.Sprintf("Domain Service (Name: %q, Resource Group: %q)", id.DomainServiceName, id.ResourceGroup) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) - existing, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + existing, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(existing.Response) { + if response.WasNotFound(existing.HttpResponse) { return metadata.MarkAsGone(id) } - return fmt.Errorf("retrieving %s: %+v", resourceErrorName, err) + return fmt.Errorf("retrieving %s: %+v", idsdk, err) + } + + model := existing.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", idsdk) } - props := existing.DomainServiceProperties + props := model.Properties if props == nil { - return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing properties", resourceErrorName) + return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing properties", idsdk) } - existingTrusts := []aad.ForestTrust{} + existingTrusts := []domainservices.ForestTrust{} if props != nil { if fsettings := props.ResourceForestSettings; fsettings != nil { if settings := fsettings.Settings; settings != nil { @@ -183,7 +192,7 @@ func (r DomainServiceTrustResource) Read() sdk.ResourceFunc { } } } - var trust *aad.ForestTrust + var trust *domainservices.ForestTrust for _, setting := range existingTrusts { existingTrust := setting if setting.FriendlyName != nil && *setting.FriendlyName == id.TrustName { @@ -197,17 +206,17 @@ func (r DomainServiceTrustResource) Read() sdk.ResourceFunc { // Retrieve the initial replica set id to construct the domain service id. replicaSets := flattenDomainServiceReplicaSets(props.ReplicaSets) if len(replicaSets) == 0 { - return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing replica set details", resourceErrorName) + return fmt.Errorf("checking for presence of existing %s: API response contained nil or missing replica set details", idsdk) } initialReplicaSetId := replicaSets[0].(map[string]interface{})["id"].(string) - dsid := parse.NewDomainServiceID(client.SubscriptionID, id.ResourceGroup, id.DomainServiceName, initialReplicaSetId) + dsid := parse.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName, initialReplicaSetId) var state DomainServiceTrustModel if err := metadata.Decode(&state); err != nil { return err } - model := DomainServiceTrustModel{ + data := DomainServiceTrustModel{ DomainServiceId: dsid.ID(), Name: id.TrustName, // Setting the password from state as it is not returned by API. @@ -215,14 +224,14 @@ func (r DomainServiceTrustResource) Read() sdk.ResourceFunc { } if trust.TrustedDomainFqdn != nil { - model.TrustedDomainFqdn = *trust.TrustedDomainFqdn + data.TrustedDomainFqdn = *trust.TrustedDomainFqdn } - if trust.RemoteDNSIps != nil { - model.TrustedDomainDnsIPs = strings.Split(*trust.RemoteDNSIps, ",") + if trust.RemoteDnsIPs != nil { + data.TrustedDomainDnsIPs = strings.Split(*trust.RemoteDnsIPs, ",") } - return metadata.Encode(&model) + return metadata.Encode(&data) }, } } @@ -238,20 +247,25 @@ func (r DomainServiceTrustResource) Delete() sdk.ResourceFunc { return err } - resourceErrorName := fmt.Sprintf("Domain Service (Name: %q, Resource Group: %q)", id.DomainServiceName, id.ResourceGroup) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) locks.ByName(id.DomainServiceName, DomainServiceResourceName) defer locks.UnlockByName(id.DomainServiceName, DomainServiceResourceName) - existing, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + existing, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(existing.Response) { + if response.WasNotFound(existing.HttpResponse) { return metadata.MarkAsGone(id) } - return fmt.Errorf("retrieving %s: %+v", resourceErrorName, err) + return fmt.Errorf("retrieving %s: %+v", idsdk, err) + } + + model := existing.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", idsdk) } - existingTrusts := []aad.ForestTrust{} - if props := existing.DomainServiceProperties; props != nil { + existingTrusts := []domainservices.ForestTrust{} + if props := model.Properties; props != nil { if fsettings := props.ResourceForestSettings; fsettings != nil { if settings := fsettings.Settings; settings != nil { existingTrusts = *settings @@ -259,7 +273,7 @@ func (r DomainServiceTrustResource) Delete() sdk.ResourceFunc { } } var found bool - newTrusts := []aad.ForestTrust{} + newTrusts := []domainservices.ForestTrust{} for _, trust := range existingTrusts { if trust.FriendlyName != nil && *trust.FriendlyName == id.TrustName { found = true @@ -272,21 +286,17 @@ func (r DomainServiceTrustResource) Delete() sdk.ResourceFunc { return metadata.MarkAsGone(id) } - params := aad.DomainService{ - DomainServiceProperties: &aad.DomainServiceProperties{ - ResourceForestSettings: &aad.ResourceForestSettings{ + params := domainservices.DomainService{ + Properties: &domainservices.DomainServiceProperties{ + ResourceForestSettings: &domainservices.ResourceForestSettings{ Settings: &newTrusts, }, }, } - future, err := client.Update(ctx, id.ResourceGroup, id.DomainServiceName, params) - if err != nil { + if err := client.UpdateThenPoll(ctx, idsdk, params); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for removal of %s: %+v", id, err) - } return nil }, @@ -304,17 +314,23 @@ func (r DomainServiceTrustResource) Update() sdk.ResourceFunc { return err } - resourceErrorName := fmt.Sprintf("Domain Service (Name: %q, Resource Group: %q)", id.DomainServiceName, id.ResourceGroup) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) locks.ByName(id.DomainServiceName, DomainServiceResourceName) defer locks.UnlockByName(id.DomainServiceName, DomainServiceResourceName) - existing, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + existing, err := client.Get(ctx, idsdk) if err != nil { - return fmt.Errorf("retrieving %s: %+v", resourceErrorName, err) + return fmt.Errorf("retrieving %s: %+v", idsdk, err) + } + + model := existing.Model + if model == nil { + return fmt.Errorf("reading %s: returned with null model", idsdk) } - existingTrusts := []aad.ForestTrust{} - if props := existing.DomainServiceProperties; props != nil { + + existingTrusts := []domainservices.ForestTrust{} + if props := model.Properties; props != nil { if fsettings := props.ResourceForestSettings; fsettings != nil { if settings := fsettings.Settings; settings != nil { existingTrusts = *settings @@ -328,7 +344,7 @@ func (r DomainServiceTrustResource) Update() sdk.ResourceFunc { } var found bool - newTrusts := []aad.ForestTrust{} + newTrusts := []domainservices.ForestTrust{} for _, trust := range existingTrusts { if trust.FriendlyName != nil && *trust.FriendlyName == id.TrustName { found = true @@ -336,7 +352,7 @@ func (r DomainServiceTrustResource) Update() sdk.ResourceFunc { trust.TrustedDomainFqdn = utils.String(plan.TrustedDomainFqdn) } if metadata.ResourceData.HasChange("trusted_domain_dns_ips") { - trust.RemoteDNSIps = utils.String(strings.Join(plan.TrustedDomainDnsIPs, ",")) + trust.RemoteDnsIPs = utils.String(strings.Join(plan.TrustedDomainDnsIPs, ",")) } trust.TrustPassword = utils.String(plan.Password) } @@ -346,21 +362,17 @@ func (r DomainServiceTrustResource) Update() sdk.ResourceFunc { return fmt.Errorf("%s not exists: %+v", id, err) } - params := aad.DomainService{ - DomainServiceProperties: &aad.DomainServiceProperties{ - ResourceForestSettings: &aad.ResourceForestSettings{ + params := domainservices.DomainService{ + Properties: &domainservices.DomainServiceProperties{ + ResourceForestSettings: &domainservices.ResourceForestSettings{ Settings: &newTrusts, }, }, } - future, err := client.Update(ctx, id.ResourceGroup, id.DomainServiceName, params) - if err != nil { + if err := client.UpdateThenPoll(ctx, idsdk, params); err != nil { return fmt.Errorf("updating %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of %s: %+v", id, err) - } return nil }, } diff --git a/internal/services/domainservices/active_directory_domain_service_trust_resource_test.go b/internal/services/domainservices/active_directory_domain_service_trust_resource_test.go index bd18f105253e..629d2571ff2d 100644 --- a/internal/services/domainservices/active_directory_domain_service_trust_resource_test.go +++ b/internal/services/domainservices/active_directory_domain_service_trust_resource_test.go @@ -6,7 +6,8 @@ import ( "os" "testing" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" @@ -116,15 +117,23 @@ func (r DomainServiceTrustResource) Exists(ctx context.Context, clients *clients return nil, err } - resp, err := client.Get(ctx, id.ResourceGroup, id.DomainServiceName) + idsdk := domainservices.NewDomainServiceID(id.SubscriptionId, id.ResourceGroup, id.DomainServiceName) + + resp, err := client.Get(ctx, idsdk) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil } return nil, err } - existingTrusts := []aad.ForestTrust{} - if props := resp.DomainServiceProperties; props != nil { + + model := resp.Model + if model == nil { + return nil, fmt.Errorf("reading %s: returned with null model", idsdk) + } + + existingTrusts := []domainservices.ForestTrust{} + if props := model.Properties; props != nil { if fsettings := props.ResourceForestSettings; fsettings != nil { if settings := fsettings.Settings; settings != nil { existingTrusts = *settings diff --git a/internal/services/domainservices/client/client.go b/internal/services/domainservices/client/client.go index e3f16a08a108..e83f747a2938 100644 --- a/internal/services/domainservices/client/client.go +++ b/internal/services/domainservices/client/client.go @@ -1,16 +1,16 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-sdk/resource-manager/aad/2021-05-01/domainservices" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - DomainServicesClient *aad.DomainServicesClient + DomainServicesClient *domainservices.DomainServicesClient } func NewClient(o *common.ClientOptions) *Client { - domainServicesClient := aad.NewDomainServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + domainServicesClient := domainservices.NewDomainServicesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&domainServicesClient.Client, o.ResourceManagerAuthorizer) return &Client{ diff --git a/internal/services/eventhub/client/client.go b/internal/services/eventhub/client/client.go index cb49f8333294..dba63b7829a5 100644 --- a/internal/services/eventhub/client/client.go +++ b/internal/services/eventhub/client/client.go @@ -1,15 +1,16 @@ package client import ( - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationruleseventhubs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/checknameavailabilitydisasterrecoveryconfigs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/consumergroups" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/disasterrecoveryconfigs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/eventhubsclusters" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/networkrulesets" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationruleseventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/checknameavailabilitydisasterrecoveryconfigs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/consumergroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/disasterrecoveryconfigs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubsclusters" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/networkrulesets" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/schemaregistry" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) @@ -23,6 +24,7 @@ type Client struct { NamespacesClient *namespaces.NamespacesClient NamespaceAuthorizationRulesClient *authorizationrulesnamespaces.AuthorizationRulesNamespacesClient NetworkRuleSetsClient *networkrulesets.NetworkRuleSetsClient + SchemaRegistryClient *schemaregistry.SchemaRegistryClient } func NewClient(o *common.ClientOptions) *Client { @@ -53,6 +55,9 @@ func NewClient(o *common.ClientOptions) *Client { networkRuleSetsClient := networkrulesets.NewNetworkRuleSetsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&networkRuleSetsClient.Client, o.ResourceManagerAuthorizer) + schemaRegistryClient := schemaregistry.NewSchemaRegistryClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&schemaRegistryClient.Client, o.ResourceManagerAuthorizer) + return &Client{ ClusterClient: &clustersClient, ConsumerGroupClient: &consumerGroupsClient, @@ -63,5 +68,6 @@ func NewClient(o *common.ClientOptions) *Client { NamespacesClient: &namespacesClient, NamespaceAuthorizationRulesClient: &namespaceAuthorizationRulesClient, NetworkRuleSetsClient: &networkRuleSetsClient, + SchemaRegistryClient: &schemaRegistryClient, } } diff --git a/internal/services/eventhub/eventhub_authorization_rule_data_source.go b/internal/services/eventhub/eventhub_authorization_rule_data_source.go index 64e12bbccc89..64682d9972af 100644 --- a/internal/services/eventhub/eventhub_authorization_rule_data_source.go +++ b/internal/services/eventhub/eventhub_authorization_rule_data_source.go @@ -6,8 +6,8 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationruleseventhubs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationruleseventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" diff --git a/internal/services/eventhub/eventhub_authorization_rule_resource.go b/internal/services/eventhub/eventhub_authorization_rule_resource.go index 70bda16dbdb7..01794f6da18a 100644 --- a/internal/services/eventhub/eventhub_authorization_rule_resource.go +++ b/internal/services/eventhub/eventhub_authorization_rule_resource.go @@ -6,8 +6,8 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationruleseventhubs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationruleseventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_authorization_rule_resource_test.go b/internal/services/eventhub/eventhub_authorization_rule_resource_test.go index 1691d5debce6..b1bfb14d87e8 100644 --- a/internal/services/eventhub/eventhub_authorization_rule_resource_test.go +++ b/internal/services/eventhub/eventhub_authorization_rule_resource_test.go @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_cluster_data_source.go b/internal/services/eventhub/eventhub_cluster_data_source.go index 77a820e43a73..92e6ea95d60e 100644 --- a/internal/services/eventhub/eventhub_cluster_data_source.go +++ b/internal/services/eventhub/eventhub_cluster_data_source.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/eventhubsclusters" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubsclusters" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" diff --git a/internal/services/eventhub/eventhub_cluster_resource.go b/internal/services/eventhub/eventhub_cluster_resource.go index 7f0c66aae8b7..3403c4c19d11 100644 --- a/internal/services/eventhub/eventhub_cluster_resource.go +++ b/internal/services/eventhub/eventhub_cluster_resource.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/eventhubsclusters" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubsclusters" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_cluster_resource_test.go b/internal/services/eventhub/eventhub_cluster_resource_test.go index b2ea26b1b249..76d4331a76a5 100644 --- a/internal/services/eventhub/eventhub_cluster_resource_test.go +++ b/internal/services/eventhub/eventhub_cluster_resource_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/eventhubsclusters" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubsclusters" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_consumer_group_data_source.go b/internal/services/eventhub/eventhub_consumer_group_data_source.go index 36cd7000e2b6..815cdb33ca98 100644 --- a/internal/services/eventhub/eventhub_consumer_group_data_source.go +++ b/internal/services/eventhub/eventhub_consumer_group_data_source.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/consumergroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/consumergroups" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" diff --git a/internal/services/eventhub/eventhub_consumer_group_resource.go b/internal/services/eventhub/eventhub_consumer_group_resource.go index dca39f7408cc..af86aab0114d 100644 --- a/internal/services/eventhub/eventhub_consumer_group_resource.go +++ b/internal/services/eventhub/eventhub_consumer_group_resource.go @@ -6,7 +6,7 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/consumergroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/consumergroups" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/migration" diff --git a/internal/services/eventhub/eventhub_consumer_group_resource_test.go b/internal/services/eventhub/eventhub_consumer_group_resource_test.go index bbf4729347fe..a3a8aa1a4494 100644 --- a/internal/services/eventhub/eventhub_consumer_group_resource_test.go +++ b/internal/services/eventhub/eventhub_consumer_group_resource_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/consumergroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/consumergroups" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_data_source.go b/internal/services/eventhub/eventhub_data_source.go index 61929f149044..1dfc20e99d57 100644 --- a/internal/services/eventhub/eventhub_data_source.go +++ b/internal/services/eventhub/eventhub_data_source.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" diff --git a/internal/services/eventhub/eventhub_namespace_authorization_rule_data_source.go b/internal/services/eventhub/eventhub_namespace_authorization_rule_data_source.go index 7ce5dc97a623..31109163d127 100644 --- a/internal/services/eventhub/eventhub_namespace_authorization_rule_data_source.go +++ b/internal/services/eventhub/eventhub_namespace_authorization_rule_data_source.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" diff --git a/internal/services/eventhub/eventhub_namespace_authorization_rule_resource.go b/internal/services/eventhub/eventhub_namespace_authorization_rule_resource.go index e5c7daacaff2..3f701cdbe901 100644 --- a/internal/services/eventhub/eventhub_namespace_authorization_rule_resource.go +++ b/internal/services/eventhub/eventhub_namespace_authorization_rule_resource.go @@ -6,7 +6,7 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_namespace_authorization_rule_resource_test.go b/internal/services/eventhub/eventhub_namespace_authorization_rule_resource_test.go index f9e5de6331b7..142cb93994cd 100644 --- a/internal/services/eventhub/eventhub_namespace_authorization_rule_resource_test.go +++ b/internal/services/eventhub/eventhub_namespace_authorization_rule_resource_test.go @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource.go b/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource.go index 57f22221db0f..6f3425229502 100644 --- a/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource.go +++ b/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource.go @@ -7,7 +7,7 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" diff --git a/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource_test.go b/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource_test.go index cdf986900a3e..ab4d4c6e8473 100644 --- a/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource_test.go +++ b/internal/services/eventhub/eventhub_namespace_customer_managed_key_resource_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_namespace_data_source.go b/internal/services/eventhub/eventhub_namespace_data_source.go index b4691fe125ba..c67c5d809f0b 100644 --- a/internal/services/eventhub/eventhub_namespace_data_source.go +++ b/internal/services/eventhub/eventhub_namespace_data_source.go @@ -9,8 +9,8 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" diff --git a/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource.go b/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource.go index 436db25284c0..aa4489a14a45 100644 --- a/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource.go +++ b/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource.go @@ -8,8 +8,8 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/checknameavailabilitydisasterrecoveryconfigs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/disasterrecoveryconfigs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/checknameavailabilitydisasterrecoveryconfigs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/disasterrecoveryconfigs" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource_test.go b/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource_test.go index 49cd724c4c22..90810659e837 100644 --- a/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource_test.go +++ b/internal/services/eventhub/eventhub_namespace_disaster_recovery_config_resource_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/disasterrecoveryconfigs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/disasterrecoveryconfigs" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_namespace_resource.go b/internal/services/eventhub/eventhub_namespace_resource.go index fdc9ba6d3926..bf9ca2bddc45 100644 --- a/internal/services/eventhub/eventhub_namespace_resource.go +++ b/internal/services/eventhub/eventhub_namespace_resource.go @@ -14,10 +14,10 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/eventhubsclusters" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2018-01-01-preview/networkrulesets" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubsclusters" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/networkrulesets" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -190,6 +190,29 @@ func resourceEventHubNamespace() *pluginsdk.Resource { }, }, + "local_authentication_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "minimum_tls_version": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + string(namespaces.TlsVersionOnePointZero), + string(namespaces.TlsVersionOnePointOne), + string(namespaces.TlsVersionOnePointTwo), + }, false), + }, + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + "default_primary_connection_string_alias": { Type: pluginsdk.TypeString, Computed: true, @@ -228,22 +251,26 @@ func resourceEventHubNamespace() *pluginsdk.Resource { "tags": commonschema.Tags(), }, - CustomizeDiff: pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error { - oldSku, newSku := d.GetChange("sku") - if d.HasChange("sku") { - if strings.EqualFold(newSku.(string), string(namespaces.SkuNamePremium)) || strings.EqualFold(oldSku.(string), string(namespaces.SkuTierPremium)) { - log.Printf("[DEBUG] cannot migrate a namespace from or to Premium SKU") - d.ForceNew("sku") - } - if strings.EqualFold(newSku.(string), string(namespaces.SkuTierPremium)) { - zoneRedundant := d.Get("zone_redundant").(bool) - if !zoneRedundant { - return fmt.Errorf("zone_redundant needs to be set to true when using premium SKU") + + CustomizeDiff: pluginsdk.CustomDiffWithAll( + pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error { + oldSku, newSku := d.GetChange("sku") + if d.HasChange("sku") { + if strings.EqualFold(newSku.(string), string(namespaces.SkuNamePremium)) || strings.EqualFold(oldSku.(string), string(namespaces.SkuTierPremium)) { + log.Printf("[DEBUG] cannot migrate a namespace from or to Premium SKU") + d.ForceNew("sku") + } + if strings.EqualFold(newSku.(string), string(namespaces.SkuTierPremium)) { + zoneRedundant := d.Get("zone_redundant").(bool) + if !zoneRedundant { + return fmt.Errorf("zone_redundant needs to be set to true when using premium SKU") + } } } - } - return nil - }), + return nil + }), + pluginsdk.CustomizeDiffShim(eventhubTLSVersionDiff), + ), } } @@ -278,6 +305,16 @@ func resourceEventHubNamespaceCreate(d *pluginsdk.ResourceData, meta interface{} return fmt.Errorf("expanding `identity`: %+v", err) } + publicNetworkEnabled := namespaces.PublicNetworkAccessEnabled + if !d.Get("public_network_access_enabled").(bool) { + publicNetworkEnabled = namespaces.PublicNetworkAccessDisabled + } + + disableLocalAuth := false + if !d.Get("local_authentication_enabled").(bool) { + disableLocalAuth = true + } + parameters := namespaces.EHNamespace{ Location: &location, Sku: &namespaces.Sku{ @@ -292,6 +329,8 @@ func resourceEventHubNamespaceCreate(d *pluginsdk.ResourceData, meta interface{} Properties: &namespaces.EHNamespaceProperties{ IsAutoInflateEnabled: utils.Bool(autoInflateEnabled), ZoneRedundant: utils.Bool(zoneRedundant), + DisableLocalAuth: utils.Bool(disableLocalAuth), + PublicNetworkAccess: &publicNetworkEnabled, }, Tags: tags.Expand(t), } @@ -300,6 +339,11 @@ func resourceEventHubNamespaceCreate(d *pluginsdk.ResourceData, meta interface{} parameters.Properties.ClusterArmId = utils.String(v) } + if tlsValue := d.Get("minimum_tls_version").(string); tlsValue != "" { + minimumTls := namespaces.TlsVersion(tlsValue) + parameters.Properties.MinimumTlsVersion = &minimumTls + } + if v, ok := d.GetOk("maximum_throughput_units"); ok { parameters.Properties.MaximumThroughputUnits = utils.Int64(int64(v.(int))) } @@ -312,25 +356,19 @@ func resourceEventHubNamespaceCreate(d *pluginsdk.ResourceData, meta interface{} ruleSets, hasRuleSets := d.GetOk("network_rulesets") if hasRuleSets { + // cannot use network rulesets with the basic SKU + if parameters.Sku.Name == namespaces.SkuNameBasic { + return fmt.Errorf("network_rulesets cannot be used when the SKU is basic") + } + rulesets := networkrulesets.NetworkRuleSet{ Properties: expandEventHubNamespaceNetworkRuleset(ruleSets.([]interface{})), } - // cannot use network rulesets with the basic SKU - if parameters.Sku.Name != namespaces.SkuNameBasic { - ruleSetsClient := meta.(*clients.Client).Eventhub.NetworkRuleSetsClient - namespaceId := networkrulesets.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName) - if _, err := ruleSetsClient.NamespacesCreateOrUpdateNetworkRuleSet(ctx, namespaceId, rulesets); err != nil { - return fmt.Errorf("setting network ruleset properties for %s: %+v", id, err) - } - } else if rulesets.Properties != nil { - props := rulesets.Properties - // so if the user has specified the non default rule sets throw a validation error - if *props.DefaultAction != networkrulesets.DefaultActionDeny || - (props.IpRules != nil && len(*props.IpRules) > 0) || - (props.VirtualNetworkRules != nil && len(*props.VirtualNetworkRules) > 0) { - return fmt.Errorf("network_rulesets cannot be used when the SKU is basic") - } + ruleSetsClient := meta.(*clients.Client).Eventhub.NetworkRuleSetsClient + namespaceId := networkrulesets.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName) + if _, err := ruleSetsClient.NamespacesCreateOrUpdateNetworkRuleSet(ctx, namespaceId, rulesets); err != nil { + return fmt.Errorf("setting network ruleset properties for %s: %+v", id, err) } } @@ -353,6 +391,16 @@ func resourceEventHubNamespaceUpdate(d *pluginsdk.ResourceData, meta interface{} autoInflateEnabled := d.Get("auto_inflate_enabled").(bool) zoneRedundant := d.Get("zone_redundant").(bool) + publicNetworkEnabled := namespaces.PublicNetworkAccessEnabled + if !d.Get("public_network_access_enabled").(bool) { + publicNetworkEnabled = namespaces.PublicNetworkAccessDisabled + } + + disableLocalAuth := false + if !d.Get("local_authentication_enabled").(bool) { + disableLocalAuth = true + } + identity, err := identity.ExpandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) @@ -372,6 +420,8 @@ func resourceEventHubNamespaceUpdate(d *pluginsdk.ResourceData, meta interface{} Properties: &namespaces.EHNamespaceProperties{ IsAutoInflateEnabled: utils.Bool(autoInflateEnabled), ZoneRedundant: utils.Bool(zoneRedundant), + DisableLocalAuth: utils.Bool(disableLocalAuth), + PublicNetworkAccess: &publicNetworkEnabled, }, Tags: tags.Expand(t), } @@ -380,6 +430,11 @@ func resourceEventHubNamespaceUpdate(d *pluginsdk.ResourceData, meta interface{} parameters.Properties.ClusterArmId = utils.String(v) } + if tlsValue := d.Get("minimum_tls_version").(string); tlsValue != "" { + minimumTls := namespaces.TlsVersion(tlsValue) + parameters.Properties.MinimumTlsVersion = &minimumTls + } + if v, ok := d.GetOk("maximum_throughput_units"); ok { parameters.Properties.MaximumThroughputUnits = utils.Int64(int64(v.(int))) } @@ -397,32 +452,37 @@ func resourceEventHubNamespaceUpdate(d *pluginsdk.ResourceData, meta interface{} return fmt.Errorf("updating %s: %+v", id, err) } - d.SetId(id.ID()) + if d.HasChange("network_rulesets") { + // cannot use network rulesets with the basic SKU + if parameters.Sku.Name == namespaces.SkuNameBasic { + return fmt.Errorf("network_rulesets cannot be used when the SKU is basic") + } - ruleSets, hasRuleSets := d.GetOk("network_rulesets") - if hasRuleSets { + ruleSets := d.Get("network_rulesets") rulesets := networkrulesets.NetworkRuleSet{ Properties: expandEventHubNamespaceNetworkRuleset(ruleSets.([]interface{})), } - // cannot use network rulesets with the basic SKU - if parameters.Sku.Name != namespaces.SkuNameBasic { - ruleSetsClient := meta.(*clients.Client).Eventhub.NetworkRuleSetsClient - namespaceId := networkrulesets.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName) - if _, err := ruleSetsClient.NamespacesCreateOrUpdateNetworkRuleSet(ctx, namespaceId, rulesets); err != nil { - return fmt.Errorf("setting network ruleset properties for %s: %+v", id, err) - } - } else if rulesets.Properties != nil { - props := rulesets.Properties - // so if the user has specified the non default rule sets throw a validation error - if *props.DefaultAction != networkrulesets.DefaultActionDeny || - (props.IpRules != nil && len(*props.IpRules) > 0) || - (props.VirtualNetworkRules != nil && len(*props.VirtualNetworkRules) > 0) { - return fmt.Errorf("network_rulesets cannot be used when the SKU is basic") - } + ruleSetsClient := meta.(*clients.Client).Eventhub.NetworkRuleSetsClient + namespaceId := networkrulesets.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName) + if _, err := ruleSetsClient.NamespacesCreateOrUpdateNetworkRuleSet(ctx, namespaceId, rulesets); err != nil { + return fmt.Errorf("setting network ruleset properties for %s: %+v", id, err) } } + deadline, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Activating", "ActivatingIdentity", "Updating", "Pending"}, + Target: []string{"Succeeded"}, + Refresh: eventHubNamespaceProvisioningStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + PollInterval: 10 * time.Second, + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be updated: %+v", id, err) + } + return resourceEventHubNamespaceRead(d, meta) } @@ -472,6 +532,22 @@ func resourceEventHubNamespaceRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("maximum_throughput_units", int(*props.MaximumThroughputUnits)) d.Set("zone_redundant", props.ZoneRedundant) d.Set("dedicated_cluster_id", props.ClusterArmId) + + localAuthDisabled := false + if props.DisableLocalAuth != nil { + localAuthDisabled = *props.DisableLocalAuth + } + d.Set("local_authentication_enabled", !localAuthDisabled) + + publicNetworkAccess := true + if props.PublicNetworkAccess != nil && *props.PublicNetworkAccess == namespaces.PublicNetworkAccessDisabled { + publicNetworkAccess = false + } + d.Set("public_network_access_enabled", publicNetworkAccess) + + if props.MinimumTlsVersion != nil { + d.Set("minimum_tls_version", *props.MinimumTlsVersion) + } } if err := tags.FlattenAndSet(d, model.Tags); err != nil { @@ -568,6 +644,26 @@ func eventHubNamespaceStateStatusCodeRefreshFunc(ctx context.Context, client *na } } +func eventHubNamespaceProvisioningStateRefreshFunc(ctx context.Context, client *namespaces.NamespacesClient, id namespaces.NamespaceId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id) + + provisioningState := "Pending" + if err != nil { + if response.WasNotFound(res.HttpResponse) { + return res, provisioningState, nil + } + return nil, "Error", fmt.Errorf("polling for the provisioning state of %s: %+v", id, err) + } + + if res.Model != nil && res.Model.Properties != nil && res.Model.Properties.ProvisioningState != nil { + provisioningState = *res.Model.Properties.ProvisioningState + } + + return res, provisioningState, nil + } +} + func expandEventHubNamespaceNetworkRuleset(input []interface{}) *networkrulesets.NetworkRuleSetProperties { if len(input) == 0 { return nil @@ -606,11 +702,11 @@ func expandEventHubNamespaceNetworkRuleset(input []interface{}) *networkrulesets if v, ok := block["ip_rule"].([]interface{}); ok { if len(v) > 0 { - var rules []networkrulesets.NWRuleSetIpRules + var rules []networkrulesets.NWRuleSetIPRules for _, r := range v { rblock := r.(map[string]interface{}) - rules = append(rules, networkrulesets.NWRuleSetIpRules{ - IpMask: utils.String(rblock["ip_mask"].(string)), + rules = append(rules, networkrulesets.NWRuleSetIPRules{ + IPMask: utils.String(rblock["ip_mask"].(string)), Action: func() *networkrulesets.NetworkRuleIPAction { v := networkrulesets.NetworkRuleIPAction(rblock["action"].(string)) return &v @@ -618,7 +714,7 @@ func expandEventHubNamespaceNetworkRuleset(input []interface{}) *networkrulesets }) } - ruleset.IpRules = &rules + ruleset.IPRules = &rules } } @@ -649,7 +745,7 @@ func flattenEventHubNamespaceNetworkRuleset(ruleset networkrulesets.NamespacesGe } } ipBlocks := make([]interface{}, 0) - if ipRules := ruleset.Model.Properties.IpRules; ipRules != nil { + if ipRules := ruleset.Model.Properties.IPRules; ipRules != nil { for _, ipRule := range *ipRules { block := make(map[string]interface{}) @@ -660,7 +756,7 @@ func flattenEventHubNamespaceNetworkRuleset(ruleset networkrulesets.NamespacesGe block["action"] = action - if v := ipRule.IpMask; v != nil { + if v := ipRule.IPMask; v != nil { block["ip_mask"] = *v } @@ -693,3 +789,11 @@ func resourceVnetRuleHash(v interface{}) int { } return pluginsdk.HashString(buf.String()) } + +func eventhubTLSVersionDiff(ctx context.Context, d *pluginsdk.ResourceDiff, _ interface{}) (err error) { + old, new := d.GetChange("minimum_tls_version") + if old != "" && new == "" { + err = fmt.Errorf("`minimum_tls_version` has been set before, please set a valid value for this property ") + } + return +} diff --git a/internal/services/eventhub/eventhub_namespace_resource_test.go b/internal/services/eventhub/eventhub_namespace_resource_test.go index 7793f6235337..9712741a2f93 100644 --- a/internal/services/eventhub/eventhub_namespace_resource_test.go +++ b/internal/services/eventhub/eventhub_namespace_resource_test.go @@ -6,7 +6,7 @@ import ( "regexp" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -326,6 +326,21 @@ func TestAccEventHubNamespace_BasicWithCapacity(t *testing.T) { }) } +func TestAccEventHubNamespace_BasicWithLocalAuthProperty(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace", "test") + r := EventHubNamespaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.localAuthProperty(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("local_authentication_enabled").HasValue("false"), + ), + }, + }) +} + func TestAccEventHubNamespace_BasicWithCapacityUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace", "test") r := EventHubNamespaceResource{} @@ -428,6 +443,50 @@ func TestAccEventHubNamespace_maximumThroughputUnitsUpdate(t *testing.T) { }) } +func TestAccEventHubNamespace_publicNetworkAccessUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace", "test") + r := EventHubNamespaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("public_network_access_enabled").HasValue("true"), + ), + }, + { + Config: r.publicNetworkAccessUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("public_network_access_enabled").HasValue("false"), + ), + }, + }) +} + +func TestAccEventHubNamespace_minimumTLSUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace", "test") + r := EventHubNamespaceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("minimum_tls_version").HasValue("1.2"), + ), + }, + { + Config: r.minimumTLSUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("minimum_tls_version").HasValue("1.1"), + ), + }, + }) +} + func TestAccEventHubNamespace_autoInfalteDisabledWithAutoInflateUnits(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace", "test") r := EventHubNamespaceResource{} @@ -924,6 +983,69 @@ resource "azurerm_eventhub_namespace" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, capacity) } +func (EventHubNamespaceResource) localAuthProperty(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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 = "Basic" + local_authentication_enabled = false +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (EventHubNamespaceResource) publicNetworkAccessUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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 = "Basic" + public_network_access_enabled = false +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (EventHubNamespaceResource) minimumTLSUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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 = "Basic" + minimum_tls_version = "1.1" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func (EventHubNamespaceResource) maximumThroughputUnitsUpdate(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/eventhub/eventhub_namespace_schema_registry_resource.go b/internal/services/eventhub/eventhub_namespace_schema_registry_resource.go new file mode 100644 index 000000000000..a2e62d4bcc54 --- /dev/null +++ b/internal/services/eventhub/eventhub_namespace_schema_registry_resource.go @@ -0,0 +1,180 @@ +package eventhub + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/schemaregistry" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func resourceEventHubNamespaceSchemaRegistry() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceEventHubNamespaceSchemaRegistryCreateUpdate, + Read: resourceEventHubNamespaceSchemaRegistryRead, + // Update: resourceEventHubNamespaceSchemaRegistryCreateUpdate, + Delete: resourceEventHubNamespaceSchemaRegistryDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := schemaregistry.ParseSchemaGroupID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + // Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ValidateSchemaGroupName(), + }, + + "namespace_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: namespaces.ValidateNamespaceID, + }, + + "schema_compatibility": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(schemaregistry.SchemaCompatibilityNone), + string(schemaregistry.SchemaCompatibilityBackward), + string(schemaregistry.SchemaCompatibilityForward), + }, false), + }, + + "schema_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(schemaregistry.SchemaTypeUnknown), + string(schemaregistry.SchemaTypeAvro), + }, false), + }, + }, + } +} + +func resourceEventHubNamespaceSchemaRegistryCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Eventhub.SchemaRegistryClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + log.Printf("[INFO] preparing arguments for AzureRM EventHub Namespace Schema Registry creation.") + + namespaceId, err := namespaces.ParseNamespaceID(d.Get("namespace_id").(string)) + if err != nil { + return fmt.Errorf("parsing eventhub namespace %s error: %+v", namespaceId.ID(), err) + } + + id := schemaregistry.NewSchemaGroupID(subscriptionId, namespaceId.ResourceGroupName, namespaceId.NamespaceName, d.Get("name").(string)) + if d.IsNewResource() { + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if existing.Model != nil { + return tf.ImportAsExistsError("azurerm_eventhub_namespace_schema_group", id.ID()) + } + } + + schemaCompatibilityType := schemaregistry.SchemaCompatibility(d.Get("schema_compatibility").(string)) + schemaType := schemaregistry.SchemaType(d.Get("schema_type").(string)) + + parameters := schemaregistry.SchemaGroup{ + Properties: &schemaregistry.SchemaGroupProperties{ + SchemaCompatibility: &schemaCompatibilityType, + SchemaType: &schemaType, + }, + } + + if _, err := client.CreateOrUpdate(ctx, id, parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceEventHubNamespaceSchemaRegistryRead(d, meta) +} + +func resourceEventHubNamespaceSchemaRegistryRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Eventhub.SchemaRegistryClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := schemaregistry.ParseSchemaGroupID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + d.SetId("") + return nil + } + return fmt.Errorf("making Read request on %s: %+v", id, err) + } + + d.Set("name", id.SchemaGroupName) + + namespaceId := namespaces.NewNamespaceID(id.SubscriptionId, id.ResourceGroupName, id.NamespaceName) + d.Set("namespace_id", namespaceId.ID()) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if props.SchemaCompatibility != nil { + d.Set("schema_compatibility", string(*props.SchemaCompatibility)) + } + if props.SchemaType != nil { + d.Set("schema_type", string(*props.SchemaType)) + } + } + } + + return nil +} + +func resourceEventHubNamespaceSchemaRegistryDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Eventhub.SchemaRegistryClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := schemaregistry.ParseSchemaGroupID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Delete(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return nil + } + + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil +} diff --git a/internal/services/eventhub/eventhub_namespace_schema_registry_resource_test.go b/internal/services/eventhub/eventhub_namespace_schema_registry_resource_test.go new file mode 100644 index 000000000000..cfb779444b81 --- /dev/null +++ b/internal/services/eventhub/eventhub_namespace_schema_registry_resource_test.go @@ -0,0 +1,71 @@ +package eventhub_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/schemaregistry" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type EventHubNamespaceSchemaRegistryResource struct{} + +func TestAccEventHubNamespaceSchemaRegistry_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventhub_namespace_schema_group", "test") + r := EventHubNamespaceSchemaRegistryResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func (EventHubNamespaceSchemaRegistryResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := schemaregistry.ParseSchemaGroupID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Eventhub.SchemaRegistryClient.Get(ctx, *id) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %v", *id, err) + } + + return utils.Bool(resp.Model != nil), nil +} + +func (EventHubNamespaceSchemaRegistryResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-eventhubSG-%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" +} + +resource "azurerm_eventhub_namespace_schema_group" "test" { + name = "acctestsg-%d" + namespace_id = azurerm_eventhub_namespace.test.id + schema_compatibility = "Forward" + schema_type = "Avro" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/eventhub/eventhub_resource.go b/internal/services/eventhub/eventhub_resource.go index 536be3754628..ffaa7ffb3e4a 100644 --- a/internal/services/eventhub/eventhub_resource.go +++ b/internal/services/eventhub/eventhub_resource.go @@ -6,8 +6,8 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-01-01-preview/namespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/eventhub_resource_test.go b/internal/services/eventhub/eventhub_resource_test.go index 579c92c652ae..7a23fde4d42a 100644 --- a/internal/services/eventhub/eventhub_resource_test.go +++ b/internal/services/eventhub/eventhub_resource_test.go @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/eventhub/migration/consumer_group.go b/internal/services/eventhub/migration/consumer_group.go index 08a09b63ff98..0e3ec8e815e4 100644 --- a/internal/services/eventhub/migration/consumer_group.go +++ b/internal/services/eventhub/migration/consumer_group.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/consumergroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/consumergroups" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) diff --git a/internal/services/eventhub/migration/eventhub_authorization_rule.go b/internal/services/eventhub/migration/eventhub_authorization_rule.go index 94868d53184f..52529b323223 100644 --- a/internal/services/eventhub/migration/eventhub_authorization_rule.go +++ b/internal/services/eventhub/migration/eventhub_authorization_rule.go @@ -4,7 +4,7 @@ import ( "context" "log" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) diff --git a/internal/services/eventhub/registration.go b/internal/services/eventhub/registration.go index 19db6da0b323..0edb4cf9a9f3 100644 --- a/internal/services/eventhub/registration.go +++ b/internal/services/eventhub/registration.go @@ -46,6 +46,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_eventhub_namespace_customer_managed_key": resourceEventHubNamespaceCustomerManagedKey(), "azurerm_eventhub_namespace_disaster_recovery_config": resourceEventHubNamespaceDisasterRecoveryConfig(), "azurerm_eventhub_namespace": resourceEventHubNamespace(), + "azurerm_eventhub_namespace_schema_group": resourceEventHubNamespaceSchemaRegistry(), "azurerm_eventhub": resourceEventHub(), } } diff --git a/internal/services/eventhub/validate/eventhub_names.go b/internal/services/eventhub/validate/eventhub_names.go index ed1d5b01801e..09c0fe01af28 100644 --- a/internal/services/eventhub/validate/eventhub_names.go +++ b/internal/services/eventhub/validate/eventhub_names.go @@ -35,3 +35,10 @@ func ValidateEventHubAuthorizationRuleName() pluginsdk.SchemaValidateFunc { "The authorization rule name can contain only letters, numbers, periods, hyphens and underscores. The name must start and end with a letter or number and be up to 60 characters long.", ) } + +func ValidateSchemaGroupName() pluginsdk.SchemaValidateFunc { + return validation.StringMatch( + regexp.MustCompile("^[a-zA-Z0-9]([-._a-zA-Z0-9]{0,254}[a-zA-Z0-9])?$"), + "The schema group name can contain only letters, numbers, periods (.), hyphens (-),and underscores (_), up to 256 characters, and it must begin and end with a letter or number.", + ) +} diff --git a/internal/services/firewall/firewall_policy_resource.go b/internal/services/firewall/firewall_policy_resource.go index e9086275bc69..b1cd243ef554 100644 --- a/internal/services/firewall/firewall_policy_resource.go +++ b/internal/services/firewall/firewall_policy_resource.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" @@ -17,9 +18,9 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/firewall/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/firewall/validate" - logAnalytiscValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -98,6 +99,12 @@ func resourceFirewallPolicyCreateUpdate(d *pluginsdk.ResourceData, meta interfac } } + if v, ok := d.GetOk("sql_redirect_allowed"); ok { + props.FirewallPolicyPropertiesFormat.SQL = &network.FirewallPolicySQL{ + AllowSQLRedirect: utils.Bool(v.(bool)), + } + } + if v, ok := d.GetOk("private_ip_ranges"); ok { privateIPRanges := utils.ExpandStringSlice(v.([]interface{})) props.FirewallPolicyPropertiesFormat.Snat = &network.FirewallPolicySNAT{ @@ -198,6 +205,12 @@ func resourceFirewallPolicyRead(d *pluginsdk.ResourceData, meta interface{}) err if err := d.Set("insights", flattenFirewallPolicyInsights(prop.Insights)); err != nil { return fmt.Errorf(`setting "insights": %+v`, err) } + + if prop.SQL != nil && prop.SQL.AllowSQLRedirect != nil { + if err := d.Set("sql_redirect_allowed", prop.SQL.AllowSQLRedirect); err != nil { + return fmt.Errorf("setting `sql_redirect_allowed`: %+v", err) + } + } } flattenedIdentity, err := flattenFirewallPolicyIdentity(resp.Identity) @@ -297,10 +310,16 @@ func expandFirewallPolicyIntrusionDetection(input []interface{}) *network.Firewa }) } + var privateRanges []string + for _, v := range raw["private_ranges"].([]interface{}) { + privateRanges = append(privateRanges, v.(string)) + } + return &network.FirewallPolicyIntrusionDetection{ Mode: network.FirewallPolicyIntrusionDetectionStateType(raw["mode"].(string)), Configuration: &network.FirewallPolicyIntrusionDetectionConfiguration{ SignatureOverrides: &signatureOverrides, + PrivateRanges: &privateRanges, BypassTrafficSettings: &trafficBypass, }, } @@ -460,12 +479,12 @@ func flattenFirewallPolicyIntrusionDetection(input *network.FirewallPolicyIntrus description = *bypass.Description } - sourceAddresses := make([]string, 0) + var sourceAddresses []string if bypass.SourceAddresses != nil { sourceAddresses = *bypass.SourceAddresses } - destinationAddresses := make([]string, 0) + var destinationAddresses []string if bypass.DestinationAddresses != nil { destinationAddresses = *bypass.DestinationAddresses } @@ -497,12 +516,17 @@ func flattenFirewallPolicyIntrusionDetection(input *network.FirewallPolicyIntrus }) } } + var privateRanges []string + if privates := input.Configuration.PrivateRanges; privates != nil { + privateRanges = *privates + } return []interface{}{ map[string]interface{}{ "mode": string(input.Mode), "signature_overrides": signatureOverrides, "traffic_bypass": trafficBypass, + "private_ranges": privateRanges, }, } } @@ -727,6 +751,13 @@ func resourceFirewallPolicySchema() map[string]*pluginsdk.Schema { }, }, }, + "private_ranges": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, "traffic_bypass": { Type: pluginsdk.TypeList, Optional: true, @@ -743,12 +774,14 @@ func resourceFirewallPolicySchema() map[string]*pluginsdk.Schema { "protocol": { Type: pluginsdk.TypeString, Required: true, + // protocol to be one of [ICMP ANY TCP UDP] but response may be "Any" + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.FirewallPolicyIntrusionDetectionProtocolICMP), string(network.FirewallPolicyIntrusionDetectionProtocolANY), string(network.FirewallPolicyIntrusionDetectionProtocolTCP), string(network.FirewallPolicyIntrusionDetectionProtocolUDP), - }, false), + }, true), }, "source_addresses": { Type: pluginsdk.TypeSet, @@ -826,7 +859,7 @@ func resourceFirewallPolicySchema() map[string]*pluginsdk.Schema { "default_log_analytics_workspace_id": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: logAnalytiscValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, }, "retention_in_days": { Type: pluginsdk.TypeInt, @@ -841,7 +874,7 @@ func resourceFirewallPolicySchema() map[string]*pluginsdk.Schema { "id": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: logAnalytiscValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, }, "firewall_location": commonschema.LocationWithoutForceNew(), }, @@ -851,6 +884,11 @@ func resourceFirewallPolicySchema() map[string]*pluginsdk.Schema { }, }, + "sql_redirect_allowed": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + "child_policies": { Type: pluginsdk.TypeList, Computed: true, diff --git a/internal/services/firewall/firewall_policy_resource_test.go b/internal/services/firewall/firewall_policy_resource_test.go index 77610b0cec81..5f03e5f6cf8b 100644 --- a/internal/services/firewall/firewall_policy_resource_test.go +++ b/internal/services/firewall/firewall_policy_resource_test.go @@ -58,6 +58,7 @@ func TestAccFirewallPolicy_complete(t *testing.T) { check.That(data.ResourceName).Key("dns.0.servers.0").HasValue("1.1.1.1"), check.That(data.ResourceName).Key("dns.0.servers.1").HasValue("3.3.3.3"), check.That(data.ResourceName).Key("dns.0.servers.2").HasValue("2.2.2.2"), + check.That(data.ResourceName).Key("dns.0.proxy_enabled").HasValue("true"), ), }, data.ImportStep(), @@ -127,13 +128,6 @@ func TestAccFirewallPolicy_updatePremium(t *testing.T) { ), }, data.ImportStep(), - { - Config: r.basic(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - ), - }, - data.ImportStep(), }) } @@ -287,11 +281,14 @@ resource "azurerm_firewall_policy" "test" { state = "Alert" id = "1" } + private_ranges = ["172.111.111.111"] traffic_bypass { - name = "Name bypass traffic settings" - description = "Description bypass traffic settings" - protocol = "ANY" - destination_ports = ["*"] + name = "Name bypass traffic settings" + description = "Description bypass traffic settings" + destination_addresses = [] + source_addresses = [] + protocol = "Any" + destination_ports = ["*"] source_ip_groups = [ azurerm_ip_group.test_source.id, ] @@ -300,6 +297,7 @@ resource "azurerm_firewall_policy" "test" { ] } } + sql_redirect_allowed = true identity { type = "UserAssigned" identity_ids = [ diff --git a/internal/services/firewall/firewall_resource.go b/internal/services/firewall/firewall_resource.go index e65975c52606..843cb7b0a5e8 100644 --- a/internal/services/firewall/firewall_resource.go +++ b/internal/services/firewall/firewall_resource.go @@ -74,7 +74,6 @@ func resourceFirewall() *pluginsdk.Resource { "sku_tier": { Type: pluginsdk.TypeString, Required: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(network.AzureFirewallSkuTierPremium), string(network.AzureFirewallSkuTierStandard), diff --git a/internal/services/firewall/firewall_resource_test.go b/internal/services/firewall/firewall_resource_test.go index a4b9b8a095ac..84b5ebdf6ccc 100644 --- a/internal/services/firewall/firewall_resource_test.go +++ b/internal/services/firewall/firewall_resource_test.go @@ -16,6 +16,9 @@ import ( type FirewallResource struct{} +const premium = "Premium" +const standard = "Standard" + func TestAccFirewall_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_firewall", "test") r := FirewallResource{} @@ -194,6 +197,31 @@ func TestAccFirewall_withZones(t *testing.T) { }) } +func TestAccFirewall_skuTierUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_firewall", "test") + r := FirewallResource{} + skuTier := standard + skuTierUpdate := premium + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withSkuTier(data, skuTier), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku_tier").HasValue("Standard"), + ), + }, + { + Config: r.withSkuTier(data, skuTierUpdate), + Check: acceptance.ComposeTestCheckFunc( + + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku_tier").HasValue("Premium"), + ), + }, + }) +} + func TestAccFirewall_withoutZone(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_firewall", "test") r := FirewallResource{} @@ -764,6 +792,58 @@ resource "azurerm_firewall" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger) } +func (FirewallResource) withSkuTier(data acceptance.TestData, skuTier string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-fw-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "AzureFirewallSubnet" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] +} + +resource "azurerm_public_ip" "test" { + name = "acctestpip%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" + sku = "Standard" + zones = [] +} + +resource "azurerm_firewall" "test" { + name = "acctestfirewall%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "AZFW_VNet" + sku_tier = "%s" + + ip_configuration { + name = "configuration" + subnet_id = azurerm_subnet.test.id + public_ip_address_id = azurerm_public_ip.test.id + } + + zones = [] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, skuTier) +} + func (FirewallResource) withZones(data acceptance.TestData, zones []string) string { zoneString := strings.Join(zones, ",") return fmt.Sprintf(` diff --git a/internal/services/hdinsight/common_hdinsight.go b/internal/services/hdinsight/common_hdinsight.go index be369070aaf2..c8e10689a6d5 100644 --- a/internal/services/hdinsight/common_hdinsight.go +++ b/internal/services/hdinsight/common_hdinsight.go @@ -321,6 +321,17 @@ func createHDInsightEdgeNodes(ctx context.Context, client *hdinsight.Application ApplicationType: utils.String("CustomApplication"), }, } + + if v, ok := input["https_endpoints"]; ok { + httpsEndpoints := expandHDInsightApplicationEdgeNodeHttpsEndpoints(v.([]interface{})) + application.Properties.HTTPSEndpoints = httpsEndpoints + } + + if v, ok := input["uninstall_script_actions"]; ok { + uninstallScriptActions := expandHDInsightApplicationEdgeNodeUninstallScriptActions(v.([]interface{})) + application.Properties.UninstallScriptActions = uninstallScriptActions + } + future, err := client.Create(ctx, resourceGroup, name, name, application) if err != nil { return fmt.Errorf("creating edge nodes for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) diff --git a/internal/services/hdinsight/hdinsight_hadoop_cluster_resource.go b/internal/services/hdinsight/hdinsight_hadoop_cluster_resource.go index bfdaeef76dfb..3df0a02f86ba 100644 --- a/internal/services/hdinsight/hdinsight_hadoop_cluster_resource.go +++ b/internal/services/hdinsight/hdinsight_hadoop_cluster_resource.go @@ -95,6 +95,8 @@ func resourceHDInsightHadoopCluster() *pluginsdk.Resource { }, }, + "disk_encryption": SchemaHDInsightsDiskEncryptionProperties(), + "gateway": SchemaHDInsightsGateway(), "metastores": SchemaHDInsightsExternalMetastores(), @@ -153,9 +155,18 @@ func resourceHDInsightHadoopCluster() *pluginsdk.Resource { Required: true, ValidateFunc: validation.StringIsNotEmpty, }, + "parameters": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, }, }, }, + + "https_endpoints": SchemaHDInsightsHttpsEndpoints(), + + "uninstall_script_actions": SchemaHDInsightsScriptActions(), }, }, }, @@ -264,6 +275,14 @@ func resourceHDInsightHadoopClusterCreate(d *pluginsdk.ResourceData, meta interf Identity: identity, } + if diskEncryptionPropertiesRaw, ok := d.GetOk("disk_encryption"); ok { + diskEncryptionProperties, err := ExpandHDInsightsDiskEncryptionProperties(diskEncryptionPropertiesRaw.([]interface{})) + if err != nil { + return err + } + params.Properties.DiskEncryptionProperties = diskEncryptionProperties + } + if v, ok := d.GetOk("security_profile"); ok { params.Properties.SecurityProfile = ExpandHDInsightSecurityProfile(v.([]interface{})) @@ -428,6 +447,16 @@ func resourceHDInsightHadoopClusterRead(d *pluginsdk.ResourceData, meta interfac flattenedRoles = flattenHDInsightEdgeNode(flattenedRoles, edgeNodeProps) } + if props.DiskEncryptionProperties != nil { + diskEncryptionProps, err := FlattenHDInsightsDiskEncryptionProperties(*props.DiskEncryptionProperties) + if err != nil { + return err + } + if err := d.Set("disk_encryption", diskEncryptionProps); err != nil { + return fmt.Errorf("flattening `disk_encryption`: %+v", err) + } + } + if err := d.Set("roles", flattenedRoles); err != nil { return fmt.Errorf("flattening `roles`: %+v", err) } @@ -485,7 +514,30 @@ func flattenHDInsightEdgeNode(roles []interface{}, props *hdinsight.ApplicationP for _, action := range *installScriptActions { actions["name"] = action.Name actions["uri"] = action.URI + actions["parameters"] = action.Parameters + } + } + + if uninstallScriptActions := props.UninstallScriptActions; uninstallScriptActions != nil && len(*uninstallScriptActions) != 0 { + uninstallActions := make(map[string]interface{}) + for _, uninstallAction := range *uninstallScriptActions { + actions["name"] = uninstallAction.Name + actions["uri"] = uninstallAction.URI + actions["parameters"] = uninstallAction.Parameters + } + edgeNode["uninstall_script_actions"] = []interface{}{uninstallActions} + } + + if HTTPSEndpoints := props.HTTPSEndpoints; HTTPSEndpoints != nil && len(*HTTPSEndpoints) != 0 { + httpsEndpoints := make(map[string]interface{}) + for _, HTTPSEndpoint := range *HTTPSEndpoints { + httpsEndpoints["access_modes"] = HTTPSEndpoint.AccessModes + httpsEndpoints["destination_port"] = HTTPSEndpoint.DestinationPort + httpsEndpoints["disable_gateway_auth"] = HTTPSEndpoint.DisableGatewayAuth + httpsEndpoints["private_ip_address"] = HTTPSEndpoint.PrivateIPAddress + httpsEndpoints["sub_domain_suffix"] = HTTPSEndpoint.SubDomainSuffix } + edgeNode["https_endpoints"] = []interface{}{httpsEndpoints} } edgeNode["install_script_action"] = []interface{}{actions} @@ -524,12 +576,69 @@ func expandHDInsightApplicationEdgeNodeInstallScriptActions(input []interface{}) name := val["name"].(string) uri := val["uri"].(string) + parameters := val["parameters"].(string) action := hdinsight.RuntimeScriptAction{ Name: utils.String(name), URI: utils.String(uri), // The only role available for edge nodes is edgenode - Roles: &[]string{"edgenode"}, + Parameters: utils.String(parameters), + Roles: &[]string{"edgenode"}, + } + + actions = append(actions, action) + } + + return &actions +} + +func expandHDInsightApplicationEdgeNodeHttpsEndpoints(input []interface{}) *[]hdinsight.ApplicationGetHTTPSEndpoint { + endpoints := make([]hdinsight.ApplicationGetHTTPSEndpoint, 0) + if len(input) == 0 || input[0] == nil { + return &endpoints + } + + for _, v := range input { + val := v.(map[string]interface{}) + + accessModes := val["access_modes"].([]string) + destinationPort := val["destination_port"].(int32) + disableGatewayAuth := val["disable_gateway_auth"].(bool) + privateIpAddress := val["private_ip_address"].(string) + subDomainSuffix := val["sub_domain_suffix"].(string) + + endPoint := hdinsight.ApplicationGetHTTPSEndpoint{ + AccessModes: &accessModes, + DestinationPort: utils.Int32(destinationPort), + PrivateIPAddress: utils.String(privateIpAddress), + SubDomainSuffix: utils.String(subDomainSuffix), + DisableGatewayAuth: utils.Bool(disableGatewayAuth), + } + + endpoints = append(endpoints, endPoint) + } + + return &endpoints +} + +func expandHDInsightApplicationEdgeNodeUninstallScriptActions(input []interface{}) *[]hdinsight.RuntimeScriptAction { + actions := make([]hdinsight.RuntimeScriptAction, 0) + if len(input) == 0 || input[0] == nil { + return &actions + } + + for _, v := range input { + val := v.(map[string]interface{}) + + name := val["name"].(string) + uri := val["uri"].(string) + parameters := val["parameters"].(string) + + action := hdinsight.RuntimeScriptAction{ + Name: utils.String(name), + URI: utils.String(uri), + Parameters: utils.String(parameters), + Roles: &[]string{"edgenode"}, } actions = append(actions, action) diff --git a/internal/services/hdinsight/hdinsight_hadoop_cluster_resource_test.go b/internal/services/hdinsight/hdinsight_hadoop_cluster_resource_test.go index fc357f3a5520..34887c8c22e3 100644 --- a/internal/services/hdinsight/hdinsight_hadoop_cluster_resource_test.go +++ b/internal/services/hdinsight/hdinsight_hadoop_cluster_resource_test.go @@ -323,6 +323,26 @@ func TestAccHDInsightHadoopCluster_tls(t *testing.T) { }) } +func TestAccHDInsightHadoopCluster_diskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_hadoop_cluster", "test") + r := HDInsightHadoopClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + func TestAccHDInsightHadoopCluster_allMetastores(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_hadoop_cluster", "test") r := HDInsightHadoopClusterResource{} @@ -539,7 +559,7 @@ func TestAccHDInsightHadoopCluster_updateMonitor(t *testing.T) { }) } -func TestAccAzureRMHDInsightHadoopCluster_autoscale(t *testing.T) { +func TestAccAzureRMHDInsightHadoopCluster_autoscaleWithSchedule(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_hadoop_cluster", "test") r := HDInsightHadoopClusterResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -558,6 +578,28 @@ func TestAccAzureRMHDInsightHadoopCluster_autoscale(t *testing.T) { "roles.0.zookeeper_node.0.password", "roles.0.zookeeper_node.0.vm_size", "storage_account"), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("https_endpoint").Exists(), + check.That(data.ResourceName).Key("ssh_endpoint").Exists(), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + +func TestAccAzureRMHDInsightHadoopCluster_autoscaleWithCapacity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_hadoop_cluster", "test") + r := HDInsightHadoopClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.autoscale_capacity(data), Check: acceptance.ComposeTestCheckFunc( @@ -1642,6 +1684,54 @@ resource "azurerm_hdinsight_hadoop_cluster" "test" { `, r.template(data), data.RandomInteger) } +func (r HDInsightHadoopClusterResource) diskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_hdinsight_hadoop_cluster" "test" { + name = "acctesthdi-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + cluster_version = "4.0" + tier = "Standard" + tls_min_version = "1.2" + component_version { + hadoop = "3.1" + } + gateway { + username = "acctestusrgw" + password = "TerrAform123!" + } + disk_encryption { + encryption_at_host_enabled = true + } + storage_account { + storage_container_id = azurerm_storage_container.test.id + storage_account_key = azurerm_storage_account.test.primary_access_key + is_default = true + } + roles { + head_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + worker_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + target_instance_count = 2 + } + zookeeper_node { + vm_size = "Standard_DS2_V2" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + } +} + +`, r.template(data), data.RandomInteger) +} + func (r HDInsightHadoopClusterResource) allMetastores(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/hdinsight/hdinsight_hbase_cluster_resource.go b/internal/services/hdinsight/hdinsight_hbase_cluster_resource.go index b7a728ab529e..525860966e86 100644 --- a/internal/services/hdinsight/hdinsight_hbase_cluster_resource.go +++ b/internal/services/hdinsight/hdinsight_hbase_cluster_resource.go @@ -89,6 +89,8 @@ func resourceHDInsightHBaseCluster() *pluginsdk.Resource { }, }, + "disk_encryption": SchemaHDInsightsDiskEncryptionProperties(), + "gateway": SchemaHDInsightsGateway(), "metastores": SchemaHDInsightsExternalMetastores(), @@ -230,6 +232,13 @@ func resourceHDInsightHBaseClusterCreate(d *pluginsdk.ResourceData, meta interfa } } + if diskEncryptionPropertiesRaw, ok := d.GetOk("disk_encryption"); ok { + params.Properties.DiskEncryptionProperties, err = ExpandHDInsightsDiskEncryptionProperties(diskEncryptionPropertiesRaw.([]interface{})) + if err != nil { + return err + } + } + future, err := client.Create(ctx, resourceGroup, name, params) if err != nil { return fmt.Errorf("failure creating HDInsight HBase Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -335,6 +344,16 @@ func resourceHDInsightHBaseClusterRead(d *pluginsdk.ResourceData, meta interface } } + if props.DiskEncryptionProperties != nil { + diskEncryptionProps, err := FlattenHDInsightsDiskEncryptionProperties(*props.DiskEncryptionProperties) + if err != nil { + return err + } + if err := d.Set("disk_encryption", diskEncryptionProps); err != nil { + return fmt.Errorf("flattening `disk_encryption`: %+v", err) + } + } + hbaseRoles := hdInsightRoleDefinition{ HeadNodeDef: hdInsightHBaseClusterHeadNodeDefinition, WorkerNodeDef: hdInsightHBaseClusterWorkerNodeDefinition, diff --git a/internal/services/hdinsight/hdinsight_hbase_cluster_resource_test.go b/internal/services/hdinsight/hdinsight_hbase_cluster_resource_test.go index 561acd1428b7..67690918b949 100644 --- a/internal/services/hdinsight/hdinsight_hbase_cluster_resource_test.go +++ b/internal/services/hdinsight/hdinsight_hbase_cluster_resource_test.go @@ -221,6 +221,26 @@ func TestAccHDInsightHBaseCluster_tls(t *testing.T) { }) } +func TestAccHDInsightHBaseCluster_diskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_hbase_cluster", "test") + r := HDInsightHBaseClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + func TestAccHDInsightHBaseCluster_allMetastores(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_hbase_cluster", "test") r := HDInsightHBaseClusterResource{} @@ -400,43 +420,6 @@ func TestAccHDInsightHBaseCluster_updateMonitor(t *testing.T) { }) } -func TestAccAzureRMHDInsightHBaseCluster_autoscale(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_hdinsight_hbase_cluster", "test") - r := HDInsightHBaseClusterResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ - { - Config: r.autoscale_schedule(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("https_endpoint").Exists(), - check.That(data.ResourceName).Key("ssh_endpoint").Exists(), - ), - }, - data.ImportStep("roles.0.head_node.0.password", - "roles.0.head_node.0.vm_size", - "roles.0.worker_node.0.password", - "roles.0.worker_node.0.vm_size", - "roles.0.zookeeper_node.0.password", - "roles.0.zookeeper_node.0.vm_size", - "storage_account"), - { - Config: r.basic(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("https_endpoint").Exists(), - check.That(data.ResourceName).Key("ssh_endpoint").Exists(), - ), - }, - data.ImportStep("roles.0.head_node.0.password", - "roles.0.head_node.0.vm_size", - "roles.0.worker_node.0.password", - "roles.0.worker_node.0.vm_size", - "roles.0.zookeeper_node.0.password", - "roles.0.zookeeper_node.0.vm_size", - "storage_account"), - }) -} - func testAccHDInsightHBaseCluster_securityProfile(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_hbase_cluster", "test") r := HDInsightHBaseClusterResource{} @@ -1361,6 +1344,61 @@ resource "azurerm_hdinsight_hbase_cluster" "test" { `, r.template(data), data.RandomInteger) } +func (r HDInsightHBaseClusterResource) diskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_hdinsight_hbase_cluster" "test" { + name = "acctesthdi-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + cluster_version = "4.0" + tier = "Standard" + tls_min_version = "1.2" + + component_version { + hbase = "2.1" + } + + gateway { + username = "acctestusrgw" + password = "TerrAform123!" + } + + storage_account { + storage_container_id = azurerm_storage_container.test.id + storage_account_key = azurerm_storage_account.test.primary_access_key + is_default = true + } + + disk_encryption { + encryption_at_host_enabled = true + } + + roles { + head_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + + worker_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + target_instance_count = 2 + } + + zookeeper_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r HDInsightHBaseClusterResource) allMetastores(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -1603,71 +1641,6 @@ resource "azurerm_hdinsight_hbase_cluster" "test" { `, r.template(data), data.RandomString, data.RandomInteger, data.RandomInteger) } -func (r HDInsightHBaseClusterResource) autoscale_schedule(data acceptance.TestData) string { - return fmt.Sprintf(` -%s - -resource "azurerm_hdinsight_hbase_cluster" "test" { - name = "acctesthdi-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - cluster_version = "4.0" - tier = "Standard" - - component_version { - hbase = "2.1" - } - - gateway { - username = "acctestusrgw" - password = "TerrAform123!" - } - - storage_account { - storage_container_id = azurerm_storage_container.test.id - storage_account_key = azurerm_storage_account.test.primary_access_key - is_default = true - } - - roles { - head_node { - vm_size = "Standard_D3_V2" - username = "acctestusrvm" - password = "AccTestvdSC4daf986!" - } - - worker_node { - vm_size = "Standard_D3_V2" - username = "acctestusrvm" - password = "AccTestvdSC4daf986!" - target_instance_count = 2 - autoscale { - recurrence { - timezone = "Pacific Standard Time" - schedule { - days = ["Monday"] - time = "10:00" - target_instance_count = 5 - } - schedule { - days = ["Saturday", "Sunday"] - time = "10:00" - target_instance_count = 3 - } - } - } - } - - zookeeper_node { - vm_size = "Standard_D3_V2" - username = "acctestusrvm" - password = "AccTestvdSC4daf986!" - } - } -} -`, r.template(data), data.RandomInteger) -} - func (r HDInsightHBaseClusterResource) securityProfile(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s diff --git a/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource.go b/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource.go index db2ac3331351..1b898494a08a 100644 --- a/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource.go +++ b/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource.go @@ -82,6 +82,8 @@ func resourceHDInsightInteractiveQueryCluster() *pluginsdk.Resource { Computed: true, }, + "disk_encryption": SchemaHDInsightsDiskEncryptionProperties(), + "component_version": { Type: pluginsdk.TypeList, Required: true, @@ -230,6 +232,13 @@ func resourceHDInsightInteractiveQueryClusterCreate(d *pluginsdk.ResourceData, m Identity: identity, } + if diskEncryptionPropertiesRaw, ok := d.GetOk("disk_encryption"); ok { + params.Properties.DiskEncryptionProperties, err = ExpandHDInsightsDiskEncryptionProperties(diskEncryptionPropertiesRaw.([]interface{})) + if err != nil { + return err + } + } + if v, ok := d.GetOk("security_profile"); ok { params.Properties.SecurityProfile = ExpandHDInsightSecurityProfile(v.([]interface{})) @@ -345,6 +354,16 @@ func resourceHDInsightInteractiveQueryClusterRead(d *pluginsdk.ResourceData, met d.Set("encryption_in_transit_enabled", props.EncryptionInTransitProperties.IsEncryptionInTransitEnabled) } + if props.DiskEncryptionProperties != nil { + diskEncryptionProps, err := FlattenHDInsightsDiskEncryptionProperties(*props.DiskEncryptionProperties) + if err != nil { + return err + } + if err := d.Set("disk_encryption", diskEncryptionProps); err != nil { + return fmt.Errorf("flattening `disk_encryption`: %+v", err) + } + } + if props.NetworkProperties != nil { if err := d.Set("network", FlattenHDInsightsNetwork(props.NetworkProperties)); err != nil { return fmt.Errorf("flattening `network`: %+v", err) diff --git a/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource_test.go b/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource_test.go index fac046e93a32..93b627ab021a 100644 --- a/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource_test.go +++ b/internal/services/hdinsight/hdinsight_interactive_query_cluster_resource_test.go @@ -244,6 +244,26 @@ func TestAccHDInsightInteractiveQueryCluster_encryption_in_transit(t *testing.T) }) } +func TestAccHDInsightInteractiveQueryCluster_diskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_interactive_query_cluster", "test") + r := HDInsightInteractiveQueryClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + func TestAccHDInsightInteractiveQueryCluster_allMetastores(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_interactive_query_cluster", "test") r := HDInsightInteractiveQueryClusterResource{} @@ -1452,6 +1472,61 @@ resource "azurerm_hdinsight_interactive_query_cluster" "test" { `, r.template(data), data.RandomInteger) } +func (r HDInsightInteractiveQueryClusterResource) diskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_hdinsight_interactive_query_cluster" "test" { + name = "acctesthdi-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + cluster_version = "4.0" + tier = "Standard" + tls_min_version = "1.2" + + component_version { + interactive_hive = "3.1" + } + + disk_encryption { + encryption_at_host_enabled = true + } + + gateway { + username = "acctestusrgw" + password = "TerrAform123!" + } + + storage_account { + storage_container_id = azurerm_storage_container.test.id + storage_account_key = azurerm_storage_account.test.primary_access_key + is_default = true + } + + roles { + head_node { + vm_size = "Standard_D8a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + + worker_node { + vm_size = "Standard_D16a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + target_instance_count = 2 + } + + zookeeper_node { + vm_size = "Standard_DS2_V2" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r HDInsightInteractiveQueryClusterResource) allMetastores(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/hdinsight/hdinsight_kafka_cluster_resource.go b/internal/services/hdinsight/hdinsight_kafka_cluster_resource.go index 5d2e867f0980..6c495508aa51 100644 --- a/internal/services/hdinsight/hdinsight_kafka_cluster_resource.go +++ b/internal/services/hdinsight/hdinsight_kafka_cluster_resource.go @@ -115,6 +115,8 @@ func resourceHDInsightKafkaCluster() *pluginsdk.Resource { Optional: true, }, + "disk_encryption": SchemaHDInsightsDiskEncryptionProperties(), + "roles": { Type: pluginsdk.TypeList, Required: true, @@ -273,6 +275,13 @@ func resourceHDInsightKafkaClusterCreate(d *pluginsdk.ResourceData, meta interfa } } + if diskEncryptionPropertiesRaw, ok := d.GetOk("disk_encryption"); ok { + params.Properties.DiskEncryptionProperties, err = ExpandHDInsightsDiskEncryptionProperties(diskEncryptionPropertiesRaw.([]interface{})) + if err != nil { + return err + } + } + if v, ok := d.GetOk("security_profile"); ok { params.Properties.SecurityProfile = ExpandHDInsightSecurityProfile(v.([]interface{})) @@ -413,6 +422,16 @@ func resourceHDInsightKafkaClusterRead(d *pluginsdk.ResourceData, meta interface } } + if props.DiskEncryptionProperties != nil { + diskEncryptionProps, err := FlattenHDInsightsDiskEncryptionProperties(*props.DiskEncryptionProperties) + if err != nil { + return err + } + if err := d.Set("disk_encryption", diskEncryptionProps); err != nil { + return fmt.Errorf("flattening `disk_encryption`: %+v", err) + } + } + monitor, err := extensionsClient.GetMonitoringStatus(ctx, resourceGroup, name) if err != nil { return fmt.Errorf("failed reading monitor configuration for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) diff --git a/internal/services/hdinsight/hdinsight_kafka_cluster_resource_test.go b/internal/services/hdinsight/hdinsight_kafka_cluster_resource_test.go index ce732de4c606..f173e48d9491 100644 --- a/internal/services/hdinsight/hdinsight_kafka_cluster_resource_test.go +++ b/internal/services/hdinsight/hdinsight_kafka_cluster_resource_test.go @@ -406,6 +406,28 @@ func TestAccHDInsightKafkaCluster_restProxy(t *testing.T) { }) } +func TestAccHDInsightKafkaCluster_diskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_kafka_cluster", "test") + r := HDInsightKafkaClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "roles.0.kafka_management_node.0.password", + "roles.0.kafka_management_node.0.vm_size", + "storage_account"), + }) +} + func TestAccHDInsightKafkaCluster_encryptionInTransitEnabled(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_kafka_cluster", "test") r := HDInsightKafkaClusterResource{} @@ -1344,6 +1366,61 @@ resource "azurerm_hdinsight_kafka_cluster" "test" { `, r.template(data), data.RandomInteger, data.RandomInteger) } +func (r HDInsightKafkaClusterResource) diskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_hdinsight_kafka_cluster" "test" { + name = "acctesthdi-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + cluster_version = "4.0" + tier = "Standard" + + component_version { + kafka = "2.1" + } + + gateway { + username = "acctestusrgw" + password = "TerrAform123!" + } + + storage_account { + storage_container_id = azurerm_storage_container.test.id + storage_account_key = azurerm_storage_account.test.primary_access_key + is_default = true + } + + disk_encryption { + encryption_at_host_enabled = true + } + + roles { + head_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + + worker_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + target_instance_count = 3 + number_of_disks_per_node = 2 + } + + zookeeper_node { + vm_size = "Standard_DS2_V2" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r HDInsightKafkaClusterResource) encryptionInTransitEnabled(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/hdinsight/hdinsight_spark_cluster_resource.go b/internal/services/hdinsight/hdinsight_spark_cluster_resource.go index a50f1dda8817..02bc3422b036 100644 --- a/internal/services/hdinsight/hdinsight_spark_cluster_resource.go +++ b/internal/services/hdinsight/hdinsight_spark_cluster_resource.go @@ -82,6 +82,8 @@ func resourceHDInsightSparkCluster() *pluginsdk.Resource { Computed: true, }, + "disk_encryption": SchemaHDInsightsDiskEncryptionProperties(), + "component_version": { Type: pluginsdk.TypeList, Required: true, @@ -230,6 +232,13 @@ func resourceHDInsightSparkClusterCreate(d *pluginsdk.ResourceData, meta interfa Identity: identity, } + if diskEncryptionPropertiesRaw, ok := d.GetOk("disk_encryption"); ok { + params.Properties.DiskEncryptionProperties, err = ExpandHDInsightsDiskEncryptionProperties(diskEncryptionPropertiesRaw.([]interface{})) + if err != nil { + return err + } + } + if v, ok := d.GetOk("security_profile"); ok { params.Properties.SecurityProfile = ExpandHDInsightSecurityProfile(v.([]interface{})) @@ -352,6 +361,16 @@ func resourceHDInsightSparkClusterRead(d *pluginsdk.ResourceData, meta interface d.Set("encryption_in_transit_enabled", props.EncryptionInTransitProperties.IsEncryptionInTransitEnabled) } + if props.DiskEncryptionProperties != nil { + diskEncryptionProps, err := FlattenHDInsightsDiskEncryptionProperties(*props.DiskEncryptionProperties) + if err != nil { + return err + } + if err := d.Set("disk_encryption", diskEncryptionProps); err != nil { + return fmt.Errorf("flattening setting `disk_encryption`: %+v", err) + } + } + if props.NetworkProperties != nil { if err := d.Set("network", FlattenHDInsightsNetwork(props.NetworkProperties)); err != nil { return fmt.Errorf("flattening `network`: %+v", err) diff --git a/internal/services/hdinsight/hdinsight_spark_cluster_resource_test.go b/internal/services/hdinsight/hdinsight_spark_cluster_resource_test.go index 3b62179eaa99..60defb4af05b 100644 --- a/internal/services/hdinsight/hdinsight_spark_cluster_resource_test.go +++ b/internal/services/hdinsight/hdinsight_spark_cluster_resource_test.go @@ -244,6 +244,26 @@ func TestAccHDInsightSparkCluster_encryption_in_transit(t *testing.T) { }) } +func TestAccHDInsightSparkCluster_diskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_spark_cluster", "test") + r := HDInsightSparkClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.diskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + func TestAccHDInsightSparkCluster_allMetastores(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_spark_cluster", "test") r := HDInsightSparkClusterResource{} @@ -423,12 +443,12 @@ func TestAccHDInsightSparkCluster_updateMonitor(t *testing.T) { }) } -func TestAccAzureRMHDInsightSparkCluster_autoscale(t *testing.T) { +func TestAccAzureRMHDInsightSparkCluster_autoscaleWithSchedule(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_hdinsight_spark_cluster", "test") r := HDInsightSparkClusterResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.autoscale_capacity(data), + Config: r.autoscale_schedule(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("https_endpoint").Exists(), @@ -443,7 +463,29 @@ func TestAccAzureRMHDInsightSparkCluster_autoscale(t *testing.T) { "roles.0.zookeeper_node.0.vm_size", "storage_account"), { - Config: r.autoscale_schedule(data), + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("https_endpoint").Exists(), + check.That(data.ResourceName).Key("ssh_endpoint").Exists(), + ), + }, + data.ImportStep("roles.0.head_node.0.password", + "roles.0.head_node.0.vm_size", + "roles.0.worker_node.0.password", + "roles.0.worker_node.0.vm_size", + "roles.0.zookeeper_node.0.password", + "roles.0.zookeeper_node.0.vm_size", + "storage_account"), + }) +} + +func TestAccAzureRMHDInsightSparkCluster_autoscaleWithCapacity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_hdinsight_spark_cluster", "test") + r := HDInsightSparkClusterResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoscale_capacity(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("https_endpoint").Exists(), @@ -1452,6 +1494,63 @@ resource "azurerm_hdinsight_spark_cluster" "test" { `, r.template(data), data.RandomInteger) } +func (r HDInsightSparkClusterResource) diskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_hdinsight_spark_cluster" "test" { + name = "acctesthdi-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + cluster_version = "4.0" + tier = "Standard" + tls_min_version = "1.2" + + + component_version { + spark = "2.4" + } + + gateway { + username = "acctestusrgw" + password = "TerrAform123!" + } + + storage_account { + storage_container_id = azurerm_storage_container.test.id + storage_account_key = azurerm_storage_account.test.primary_access_key + is_default = true + } + + disk_encryption { + encryption_at_host_enabled = true + } + + roles { + head_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + + worker_node { + vm_size = "Standard_D4a_V4" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + target_instance_count = 3 + } + + zookeeper_node { + vm_size = "Standard_DS2_V2" + username = "acctestusrvm" + password = "AccTestvdSC4daf986!" + } + } +} +`, r.template(data), data.RandomInteger) + +} + func (r HDInsightSparkClusterResource) allMetastores(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/hdinsight/schema.go b/internal/services/hdinsight/schema.go index 755b0c9d4823..1058e0c2d6e0 100644 --- a/internal/services/hdinsight/schema.go +++ b/internal/services/hdinsight/schema.go @@ -9,7 +9,10 @@ import ( "github.com/Azure/azure-sdk-for-go/services/hdinsight/mgmt/2018-06-01/hdinsight" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/hdinsight/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVault "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -289,6 +292,86 @@ func SchemaHDInsightsSecurityProfile() *pluginsdk.Schema { } } +func SchemaHDInsightsScriptActions() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "uri": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.IsURLWithHTTPorHTTPS, + }, + + "parameters": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +func SchemaHDInsightsHttpsEndpoints() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "access_modes": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "destination_port": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: azValidate.PortNumber, + }, + + "disable_gateway_auth": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "private_ip_address": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.IsIPAddress, + }, + + "sub_domain_suffix": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +type HttpEndpointModel struct { + AccessModes []string `tfschema:"access_modes"` + DestinationPort int32 `tfschema:"destination_port"` + DisableGatewayAuth bool `tfschema:"disable_gateway_auth"` + PrivateIpAddress string `tfschema:"private_ip_address"` + SubDomainSuffix string `tfschema:"sub_domain_suffix"` +} + func ExpandHDInsightsConfigurations(input []interface{}) map[string]interface{} { vs := input[0].(map[string]interface{}) @@ -642,6 +725,107 @@ func SchemaHDInsightsGen2StorageAccounts() *pluginsdk.Schema { } } +func SchemaHDInsightsDiskEncryptionProperties() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "encryption_algorithm": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(hdinsight.JSONWebKeyEncryptionAlgorithmRSA15), + string(hdinsight.JSONWebKeyEncryptionAlgorithmRSAOAEP), + string(hdinsight.JSONWebKeyEncryptionAlgorithmRSAOAEP256), + }, false), + }, + + "encryption_at_host_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "key_vault_managed_identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + }, + + "key_vault_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVault.NestedItemId, + }, + }, + }, + } +} + +func ExpandHDInsightsDiskEncryptionProperties(input []interface{}) (*hdinsight.DiskEncryptionProperties, error) { + v := input[0].(map[string]interface{}) + + encryptionAlgorithm := v["encryption_algorithm"].(string) + encryptionAtHost := v["encryption_at_host_enabled"].(bool) + keyVaultManagedIdentityId := v["key_vault_managed_identity_id"].(string) + + diskEncryptionProps := &hdinsight.DiskEncryptionProperties{ + EncryptionAlgorithm: hdinsight.JSONWebKeyEncryptionAlgorithm(encryptionAlgorithm), + EncryptionAtHost: &encryptionAtHost, + MsiResourceID: &keyVaultManagedIdentityId, + } + + if id, ok := v["key_vault_key_id"]; ok && id.(string) != "" { + keyVaultKeyId, err := parse.ParseNestedItemID(id.(string)) + if err != nil { + return nil, err + } + diskEncryptionProps.KeyName = &keyVaultKeyId.Name + diskEncryptionProps.KeyVersion = &keyVaultKeyId.Version + diskEncryptionProps.VaultURI = &keyVaultKeyId.KeyVaultBaseUrl + } + + return diskEncryptionProps, nil +} + +func FlattenHDInsightsDiskEncryptionProperties(input hdinsight.DiskEncryptionProperties) ([]interface{}, error) { + var encryptionAlgorithm string + var encryptionAtHost bool + var keyName string + var keyVersion string + var msiResourceId string + var keyVaultKeyId string + + if input.EncryptionAtHost != nil { + encryptionAtHost = *input.EncryptionAtHost + } + encryptionAlgorithm = string(input.EncryptionAlgorithm) + if input.KeyName != nil { + keyName = *input.KeyName + } + if input.KeyVersion != nil { + keyVersion = *input.KeyVersion + } + msiResourceId = *input.MsiResourceID + + if keyName != "" || keyVersion != "" { + keyVaultKeyIdRaw, err := parse.NewNestedItemID(*input.VaultURI, "keys", keyName, keyVersion) + if err != nil { + return nil, err + } + keyVaultKeyId = keyVaultKeyIdRaw.ID() + } + + return []interface{}{ + map[string]interface{}{ + "encryption_algorithm": encryptionAlgorithm, + "encryption_at_host_enabled": encryptionAtHost, + "key_vault_key_id": keyVaultKeyId, + "key_vault_managed_identity_id": msiResourceId, + }, + }, nil +} + // ExpandHDInsightsStorageAccounts returns an array of StorageAccount structs, as well as a ClusterIdentity // populated with any managed identities required for accessing Data Lake Gen2 storage. func ExpandHDInsightsStorageAccounts(storageAccounts []interface{}, gen2storageAccounts []interface{}) (*[]hdinsight.StorageAccount, *hdinsight.ClusterIdentity, error) { diff --git a/internal/services/healthcare/client/client.go b/internal/services/healthcare/client/client.go index ac266932cd86..f699d48e44b1 100644 --- a/internal/services/healthcare/client/client.go +++ b/internal/services/healthcare/client/client.go @@ -6,10 +6,12 @@ import ( ) type Client struct { - HealthcareServiceClient *healthcareapis.ServicesClient - HealthcareWorkspaceClient *healthcareapis.WorkspacesClient - HealthcareWorkspaceDicomServiceClient *healthcareapis.DicomServicesClient - HealthcareWorkspaceFhirServiceClient *healthcareapis.FhirServicesClient + HealthcareServiceClient *healthcareapis.ServicesClient + HealthcareWorkspaceClient *healthcareapis.WorkspacesClient + HealthcareWorkspaceDicomServiceClient *healthcareapis.DicomServicesClient + HealthcareWorkspaceFhirServiceClient *healthcareapis.FhirServicesClient + HealthcareWorkspaceMedTechServiceClient *healthcareapis.IotConnectorsClient + HealthcareWorkspaceMedTechServiceFhirDestinationClient *healthcareapis.IotConnectorFhirDestinationClient } func NewClient(o *common.ClientOptions) *Client { @@ -25,10 +27,18 @@ func NewClient(o *common.ClientOptions) *Client { HealthcareWorkspaceFhirServiceClient := healthcareapis.NewFhirServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&HealthcareWorkspaceFhirServiceClient.Client, o.ResourceManagerAuthorizer) + HealthcareWorkspaceMedTechServiceClient := healthcareapis.NewIotConnectorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&HealthcareWorkspaceMedTechServiceClient.Client, o.ResourceManagerAuthorizer) + + HealthcareWorkspaceMedTechServiceFhirDestinationClient := healthcareapis.NewIotConnectorFhirDestinationClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&HealthcareWorkspaceMedTechServiceFhirDestinationClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - HealthcareServiceClient: &HealthcareServiceClient, - HealthcareWorkspaceClient: &HealthcareWorkspaceClient, - HealthcareWorkspaceDicomServiceClient: &HealthcareWorkspaceDicomServiceClient, - HealthcareWorkspaceFhirServiceClient: &HealthcareWorkspaceFhirServiceClient, + HealthcareServiceClient: &HealthcareServiceClient, + HealthcareWorkspaceClient: &HealthcareWorkspaceClient, + HealthcareWorkspaceDicomServiceClient: &HealthcareWorkspaceDicomServiceClient, + HealthcareWorkspaceFhirServiceClient: &HealthcareWorkspaceFhirServiceClient, + HealthcareWorkspaceMedTechServiceClient: &HealthcareWorkspaceMedTechServiceClient, + HealthcareWorkspaceMedTechServiceFhirDestinationClient: &HealthcareWorkspaceMedTechServiceFhirDestinationClient, } } diff --git a/internal/services/healthcare/healthcare_medtech_service_data_source.go b/internal/services/healthcare/healthcare_medtech_service_data_source.go new file mode 100644 index 000000000000..70409f6e36f7 --- /dev/null +++ b/internal/services/healthcare/healthcare_medtech_service_data_source.go @@ -0,0 +1,130 @@ +package healthcare + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func dataSourceHealthcareIotConnector() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceHealthcareIotConnectorRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.MedTechServiceName(), + }, + + "workspace_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.WorkspaceID, + }, + + "identity": commonschema.SystemAssignedIdentityComputed(), + + "eventhub_namespace_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "eventhub_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "eventhub_consumer_group_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "device_mapping_json": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceHealthcareIotConnectorRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceMedTechServiceClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + workspaceId, err := parse.WorkspaceID(d.Get("workspace_id").(string)) + if err != nil { + return fmt.Errorf("parsing workspace id error: %+v", err) + } + + id := parse.NewMedTechServiceID(subscriptionId, workspaceId.ResourceGroup, workspaceId.Name, d.Get("name").(string)) + + resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.SetId(id.ID()) + d.Set("name", id.IotconnectorName) + + d.Set("workspace_id", workspaceId.ID()) + + if err := d.Set("identity", flattenMedTechServiceIdentity(resp.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + if props := resp.IotConnectorProperties; props != nil { + if props.IngestionEndpointConfiguration.EventHubName != nil { + d.Set("eventhub_name", props.IngestionEndpointConfiguration.EventHubName) + } + + if props.IngestionEndpointConfiguration.ConsumerGroup != nil { + d.Set("eventhub_consumer_group_name", props.IngestionEndpointConfiguration.ConsumerGroup) + } + + if props.DeviceMapping != nil { + deviceMapData, err := json.Marshal(props.DeviceMapping) + if err != nil { + return err + } + + var m map[string]*json.RawMessage + if err = json.Unmarshal(deviceMapData, &m); err != nil { + return err + } + mapContent := "" + if v, ok := m["content"]; ok { + contents, err := json.Marshal(v) + if err != nil { + return err + } + mapContent = string(contents) + } + d.Set("device_mapping_json", mapContent) + } + + if props.IngestionEndpointConfiguration.FullyQualifiedEventHubNamespace != nil { + d.Set("eventhub_namespace_name", strings.TrimSuffix(*props.IngestionEndpointConfiguration.FullyQualifiedEventHubNamespace, ".servicebus.windows.net")) + } + } + return nil +} diff --git a/internal/services/healthcare/healthcare_medtech_service_data_source_test.go b/internal/services/healthcare/healthcare_medtech_service_data_source_test.go new file mode 100644 index 000000000000..4533b90d28da --- /dev/null +++ b/internal/services/healthcare/healthcare_medtech_service_data_source_test.go @@ -0,0 +1,35 @@ +package healthcare_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type HealthCareWorkspaceIotConnectorDataSource struct{} + +func TestAccHealthCareWorkspaceIotConnectorDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_healthcare_medtech_service", "test") + r := HealthCareWorkspaceIotConnectorDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists()), + }, + }) +} + +func (HealthCareWorkspaceIotConnectorDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_healthcare_medtech_service" "test" { + name = azurerm_healthcare_medtech_service.test.name + workspace_id = azurerm_healthcare_workspace.test.id +} +`, HealthCareWorkspaceMedTechServiceResource{}.basic(data)) +} diff --git a/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource.go b/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource.go new file mode 100644 index 000000000000..24ea1139ce50 --- /dev/null +++ b/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource.go @@ -0,0 +1,292 @@ +package healthcare + +import ( + "context" + "encoding/json" + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/healthcareapis/mgmt/2021-11-01/healthcareapis" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceHealthcareApisMedTechServiceFhirDestination() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceHealthcareApisMedTechServiceFhirDestinationCreate, + Read: resourceHealthcareApisMedTechServiceFhirDestinationRead, + Update: resourceHealthcareApisMedTechServiceFhirDestinationUpdate, + Delete: resourceHealthcareApisMedTechServiceFhirDestinationDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(90 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(90 * time.Minute), + Delete: pluginsdk.DefaultTimeout(90 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.MedTechServiceFhirDestinationID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.MedTechServiceName(), + }, + + "medtech_service_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.MedTechServiceID, + }, + + "location": commonschema.Location(), + + "destination_fhir_service_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.FhirServiceID, + }, + + "destination_identity_resolution_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(healthcareapis.IotIdentityResolutionTypeCreate), + string(healthcareapis.IotIdentityResolutionTypeLookup), + }, false), + }, + + "destination_fhir_mapping_json": { + Type: pluginsdk.TypeString, + Required: true, + StateFunc: utils.NormalizeJson, + DiffSuppressFunc: suppressJsonOrderingDifference, + }, + }, + } +} + +func resourceHealthcareApisMedTechServiceFhirDestinationCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceMedTechServiceFhirDestinationClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + log.Printf("[INFO] preparing arguments for AzureRM Healthcare Med Tech Service Fhir Destination creation.") + + medTechService, err := parse.MedTechServiceID(d.Get("medtech_service_id").(string)) + if err != nil { + return fmt.Errorf("parsing Med Tech Service error: %+v", err) + } + id := parse.NewMedTechServiceFhirDestinationID(medTechService.SubscriptionId, medTechService.ResourceGroup, medTechService.WorkspaceName, medTechService.IotconnectorName, d.Get("name").(string)) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_healthcare_medtech_service_fhir_destination", id.ID()) + } + } + + fhirServiceId, err := parse.FhirServiceID(d.Get("destination_fhir_service_id").(string)) + if err != nil { + return fmt.Errorf("parsing fhir destination id err: %+v", err) + } + + iotFhirServiceParameters := healthcareapis.IotFhirDestination{ + Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + IotFhirDestinationProperties: &healthcareapis.IotFhirDestinationProperties{ + FhirServiceResourceID: utils.String(fhirServiceId.ID()), + ResourceIdentityResolutionType: healthcareapis.IotIdentityResolutionType(d.Get("destination_identity_resolution_type").(string)), + }, + } + + fhirMap := healthcareapis.IotMappingProperties{} + fhirMappingJson := fmt.Sprintf(`{ "content": %s }`, d.Get("destination_fhir_mapping_json").(string)) + if err := json.Unmarshal([]byte(fhirMappingJson), &fhirMap); err != nil { + return err + } + iotFhirServiceParameters.IotFhirDestinationProperties.FhirMapping = &fhirMap + + medTechServiceDesFuture, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName, iotFhirServiceParameters) + if err != nil { + return fmt.Errorf("updating fhir service %s for the Med Tech Service err: %+v", id, err) + } + if err = medTechServiceDesFuture.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceHealthcareApisMedTechServiceFhirDestinationRead(d, meta) +} + +func resourceHealthcareApisMedTechServiceFhirDestinationRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceMedTechServiceFhirDestinationClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.MedTechServiceFhirDestinationID(d.Id()) + if err != nil { + return err + } + + medTechServiceId := parse.NewMedTechServiceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName) + d.Set("medtech_service_id", medTechServiceId.ID()) + + resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[WARN] Healthcare Apis Med Tech Service Fhir Destination %s was not found", id) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.FhirdestinationName) + + if resp.Location != nil { + d.Set("location", location.NormalizeNilable(resp.Location)) + } + + if props := resp.IotFhirDestinationProperties; props != nil { + if props.FhirServiceResourceID != nil { + d.Set("destination_fhir_service_id", props.FhirServiceResourceID) + } + + if props.FhirMapping.Content != nil { + fhirMapData, err := json.Marshal(props.FhirMapping) + if err != nil { + return err + } + + var m map[string]*json.RawMessage + if err = json.Unmarshal(fhirMapData, &m); err != nil { + return err + } + mapContent := "" + if v, ok := m["content"]; ok { + contents, err := json.Marshal(v) + if err != nil { + return err + } + mapContent = string(contents) + } + d.Set("destination_fhir_mapping_json", mapContent) + } + d.Set("destination_identity_resolution_type", props.ResourceIdentityResolutionType) + } + return nil +} + +func resourceHealthcareApisMedTechServiceFhirDestinationUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceMedTechServiceFhirDestinationClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + medTechService, err := parse.MedTechServiceID(d.Get("medtech_service_id").(string)) + if err != nil { + return fmt.Errorf("parsing Med Tech Service error: %+v", err) + } + id := parse.NewMedTechServiceFhirDestinationID(medTechService.SubscriptionId, medTechService.ResourceGroup, medTechService.WorkspaceName, medTechService.IotconnectorName, d.Get("name").(string)) + + fhirServiceId, err := parse.FhirServiceID(d.Get("destination_fhir_service_id").(string)) + if err != nil { + return fmt.Errorf("parsing fhir destination id err: %+v", err) + } + + medTechFhirServiceParameters := healthcareapis.IotFhirDestination{ + Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + IotFhirDestinationProperties: &healthcareapis.IotFhirDestinationProperties{ + FhirServiceResourceID: utils.String(fhirServiceId.ID()), + ResourceIdentityResolutionType: healthcareapis.IotIdentityResolutionType(d.Get("destination_identity_resolution_type").(string)), + }, + } + + fhirMap := healthcareapis.IotMappingProperties{} + fhirMappingJson := fmt.Sprintf(`{ "content": %s }`, d.Get("destination_fhir_mapping_json").(string)) + if err := json.Unmarshal([]byte(fhirMappingJson), &fhirMap); err != nil { + return err + } + medTechFhirServiceParameters.IotFhirDestinationProperties.FhirMapping = &fhirMap + + medTechServiceDesFuture, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName, medTechFhirServiceParameters) + if err != nil { + return fmt.Errorf("updating fhir service %s for the Med Tech Service err: %+v", id, err) + } + if err = medTechServiceDesFuture.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for update of %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceHealthcareApisMedTechServiceFhirDestinationRead(d, meta) +} + +func resourceHealthcareApisMedTechServiceFhirDestinationDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceMedTechServiceFhirDestinationClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.MedTechServiceFhirDestinationID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("deleting %s: %+v", *id, err) + } + log.Printf("[DEBUG] Waiting for %s to be deleted..", id) + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Pending"}, + Target: []string{"Deleted"}, + Refresh: healthcareApiMedTechServiceFhirDestinationStateCodeRefreshFunc(ctx, client, *id), + Timeout: d.Timeout(pluginsdk.TimeoutDelete), + ContinuousTargetOccurence: 3, + PollInterval: 10 * time.Second, + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be deleted: %+v", id, err) + } + return nil +} + +func healthcareApiMedTechServiceFhirDestinationStateCodeRefreshFunc(ctx context.Context, client *healthcareapis.IotConnectorFhirDestinationClient, id parse.MedTechServiceFhirDestinationId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName) + + if err != nil { + if utils.ResponseWasNotFound(res.Response) { + return res, "Deleted", nil + } + return nil, "Error", fmt.Errorf("polling for the status of %s: %+v", id, err) + } + + return res, "Pending", nil + } +} diff --git a/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource_test.go b/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource_test.go new file mode 100644 index 000000000000..56c2f86b5c94 --- /dev/null +++ b/internal/services/healthcare/healthcare_medtech_service_fhir_destination_resource_test.go @@ -0,0 +1,233 @@ +package healthcare_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type HealthCareMedTechServiceFhirDestinationResource struct{} + +func TestAccHealthCareMedTechServiceFhirDestination_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_medtech_service_fhir_destination", "test") + r := HealthCareMedTechServiceFhirDestinationResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareMedTechServiceFhirDestination_updateTemplate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_medtech_service_fhir_destination", "test") + r := HealthCareMedTechServiceFhirDestinationResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + { + Config: r.updateTemplate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareMedTechServiceFhirDestination_updateFhir(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_medtech_service_fhir_destination", "test") + r := HealthCareMedTechServiceFhirDestinationResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + { + Config: r.updateFhirId(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareMedTechServiceFhirDestination_updateResolutionType(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_medtech_service_fhir_destination", "test") + r := HealthCareMedTechServiceFhirDestinationResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + { + Config: r.updateResolutionType(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func (r HealthCareMedTechServiceFhirDestinationResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.MedTechServiceFhirDestinationID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.HealthCare.HealthcareWorkspaceMedTechServiceFhirDestinationClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.IotconnectorName, id.FhirdestinationName) + if err != nil { + return nil, fmt.Errorf("retrieving %s, %+v", *id, err) + } + return utils.Bool(resp.IotFhirDestinationProperties != nil), nil +} + +func (r HealthCareMedTechServiceFhirDestinationResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_healthcare_medtech_service_fhir_destination" "test" { + name = "des%d" + location = azurerm_resource_group.test.location + medtech_service_id = azurerm_healthcare_medtech_service.test.id + destination_fhir_service_id = azurerm_healthcare_fhir_service.test.id + destination_identity_resolution_type = "Create" + + destination_fhir_mapping_json = <= ago(newExceptionsTimeRange) + | extend stack = tostring(details[0].rawStack) + | summarize count(), dcount(user_AuthenticatedId), min(timestamp), max(timestamp), any(stack) by problemId + ) on problemId + | order by count_ desc + BODY +} +`, r.template(data), data.RandomInteger) +} + +func (r LogAnalyticsQueryPackQueryResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_log_analytics_query_pack" "test" { + name = "acctestlaqp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_log_analytics_query_pack_query" "test" { + name = "%[3]s" + query_pack_id = azurerm_log_analytics_query_pack.test.id + display_name = "Exceptions - New in the last 24 hours" + description = "my description" + categories = ["network"] + resource_types = ["microsoft.web/sites"] + solutions = ["LogManagement"] + + body = <= ago(newExceptionsTimeRange) + | extend stack = tostring(details[0].rawStack) + | summarize count(), dcount(user_AuthenticatedId), min(timestamp), max(timestamp), any(stack) by problemId + ) on problemId + | order by count_ desc + BODY + + additional_settings_json = <= ago(newExceptionsTimeRange) + | extend stack = tostring(details[0].rawStack) + | summarize count(), dcount(user_AuthenticatedId), min(timestamp), max(timestamp), any(stack) by problemId + ) on problemId + | order by count_ desc + BODY + + additional_settings_json = < 0 { existing := (*existingList)[0] - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_maintenance_assignment_dedicated_host", *existing.ID) + if existing.Id != nil && *existing.Id != "" { + return tf.ImportAsExistsError("azurerm_maintenance_assignment_dedicated_host", configurationId.ID()) } } - maintenanceConfigurationID := d.Get("maintenance_configuration_id").(string) - configurationId, _ := parse.MaintenanceConfigurationIDInsensitively(maintenanceConfigurationID) - // set assignment name to configuration name - assignmentName := configurationId.Name - configurationAssignment := maintenance.ConfigurationAssignment{ + assignmentName := configurationId.ResourceName + configurationAssignment := configurationassignments.ConfigurationAssignment{ Name: utils.String(assignmentName), Location: utils.String(location.Normalize(d.Get("location").(string))), - ConfigurationAssignmentProperties: &maintenance.ConfigurationAssignmentProperties{ - MaintenanceConfigurationID: utils.String(maintenanceConfigurationID), - ResourceID: utils.String(dedicatedHostIdRaw), + Properties: &configurationassignments.ConfigurationAssignmentProperties{ + MaintenanceConfigurationId: utils.String(configurationId.ID()), + ResourceId: utils.String(dedicatedHostId.ID()), }, } // It may take a few minutes after starting a VM for it to become available to assign to a configuration + + id := configurationassignments.NewProviders2ConfigurationAssignmentID(dedicatedHostId.SubscriptionId, dedicatedHostId.ResourceGroupName, "Microsoft.Compute", "hostGroups", dedicatedHostId.HostGroupName, "hosts", dedicatedHostId.HostName, assignmentName) err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError { - if _, err := client.CreateOrUpdateParent(ctx, dedicatedHostId.ResourceGroup, "Microsoft.Compute", "hostGroups", dedicatedHostId.HostGroupName, "hosts", dedicatedHostId.HostName, assignmentName, configurationAssignment); err != nil { + + if _, err := client.CreateOrUpdateParent(ctx, id, configurationAssignment); err != nil { + if strings.Contains(err.Error(), "It may take a few minutes after starting a VM for it to become available to assign to a configuration") { return pluginsdk.RetryableError(fmt.Errorf("expected VM is available to assign to a configuration but was in pending state, retrying")) } - return pluginsdk.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (Dedicated Host ID %q): %+v", dedicatedHostIdRaw, err)) + return pluginsdk.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (Dedicated Host ID %q): %+v", dedicatedHostId.ID(), err)) } return nil @@ -107,19 +115,8 @@ func resourceArmMaintenanceAssignmentDedicatedHostCreate(d *pluginsdk.ResourceDa return err } - resp, err := getMaintenanceAssignmentDedicatedHost(ctx, client, dedicatedHostId, dedicatedHostIdRaw) - if err != nil { - return err - } - if resp == nil || len(*resp) == 0 { - return fmt.Errorf("could not find Maintenance assignment (virtual machine scale set ID: %q)", dedicatedHostIdRaw) - } - assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { - return fmt.Errorf("empty or nil ID of Maintenance Assignment (Dedicated Host ID %q)", dedicatedHostIdRaw) - } + d.SetId(id.ID()) - d.SetId(*assignment.ID) return resourceArmMaintenanceAssignmentDedicatedHostRead(d, meta) } @@ -142,18 +139,22 @@ func resourceArmMaintenanceAssignmentDedicatedHostRead(d *pluginsdk.ResourceData return nil } assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { + if assignment.Id == nil || *assignment.Id == "" { return fmt.Errorf("empty or nil ID of Maintenance Assignment (Dedicated Host ID: %q", id.DedicatedHostIdRaw) } - dedicatedHostId := "" - if id.DedicatedHostId != nil { - dedicatedHostId = id.DedicatedHostId.ID() - } - d.Set("dedicated_host_id", dedicatedHostId) + d.Set("dedicated_host_id", id.DedicatedHostId.ID()) - if props := assignment.ConfigurationAssignmentProperties; props != nil { - d.Set("maintenance_configuration_id", props.MaintenanceConfigurationID) + if props := assignment.Properties; props != nil { + maintenanceConfigurationId := "" + if props.MaintenanceConfigurationId != nil { + parsedId, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(*props.MaintenanceConfigurationId) + if err != nil { + return fmt.Errorf("parsing %q: %+v", *props.MaintenanceConfigurationId, err) + } + maintenanceConfigurationId = parsedId.ID() + } + d.Set("maintenance_configuration_id", maintenanceConfigurationId) } return nil } @@ -168,20 +169,26 @@ func resourceArmMaintenanceAssignmentDedicatedHostDelete(d *pluginsdk.ResourceDa return err } - if _, err := client.DeleteParent(ctx, id.DedicatedHostId.ResourceGroup, "Microsoft.Compute", "hostGroups", id.DedicatedHostId.HostGroupName, "hosts", id.DedicatedHostId.HostName, id.Name); err != nil { + providerConfigAssignmentId := configurationassignments.NewProviders2ConfigurationAssignmentID(id.DedicatedHostId.SubscriptionId, id.DedicatedHostId.ResourceGroupName, "Microsoft.Compute", "hostGroups", id.DedicatedHostId.HostGroupName, "hosts", id.DedicatedHostId.HostName, id.Name) + + if _, err := client.DeleteParent(ctx, providerConfigAssignmentId); err != nil { return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", id.DedicatedHostIdRaw, err) } return nil } -func getMaintenanceAssignmentDedicatedHost(ctx context.Context, client *maintenance.ConfigurationAssignmentsClient, id *parseCompute.DedicatedHostId, dedicatedHostId string) (result *[]maintenance.ConfigurationAssignment, err error) { - resp, err := client.ListParent(ctx, id.ResourceGroup, "Microsoft.Compute", "hostGroups", id.HostGroupName, "hosts", id.HostName) +func getMaintenanceAssignmentDedicatedHost(ctx context.Context, client *configurationassignments.ConfigurationAssignmentsClient, hostId dedicatedhosts.HostId, dedicatedHostId string) (result *[]configurationassignments.ConfigurationAssignment, err error) { + + id := configurationassignments.NewResourceGroupProviderID(hostId.SubscriptionId, hostId.ResourceGroupName, "Microsoft.Compute", "hostGroups", hostId.HostGroupName, "hosts", hostId.HostName) + + resp, err := client.ListParent(ctx, id) + if err != nil { - if !utils.ResponseWasNotFound(resp.Response) { + if !response.WasNotFound(resp.HttpResponse) { err = fmt.Errorf("checking for presence of existing Maintenance assignment (Dedicated Host ID %q): %+v", dedicatedHostId, err) - return + return nil, err } } - return resp.Value, nil + return resp.Model.Value, nil } diff --git a/internal/services/maintenance/maintenance_assignment_dedicated_host_resource_test.go b/internal/services/maintenance/maintenance_assignment_dedicated_host_resource_test.go index 7693b4b1b826..cfdaad295c88 100644 --- a/internal/services/maintenance/maintenance_assignment_dedicated_host_resource_test.go +++ b/internal/services/maintenance/maintenance_assignment_dedicated_host_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/configurationassignments" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -51,12 +52,14 @@ func (MaintenanceAssignmentDedicatedHostResource) Exists(ctx context.Context, cl return nil, err } - resp, err := clients.Maintenance.ConfigurationAssignmentsClient.ListParent(ctx, id.DedicatedHostId.ResourceGroup, "Microsoft.Compute", "hostGroups", id.DedicatedHostId.HostGroupName, "hosts", id.DedicatedHostId.HostName) + rgProviderId := configurationassignments.NewResourceGroupProviderID(id.DedicatedHostId.SubscriptionId, id.DedicatedHostId.ResourceGroupName, "Microsoft.Compute", "hostGroups", id.DedicatedHostId.HostGroupName, "hosts", id.DedicatedHostId.HostName) + resp, err := clients.Maintenance.ConfigurationAssignmentsClient.ListParent(ctx, rgProviderId) + if err != nil { return nil, fmt.Errorf("retrieving Maintenance Assignment Dedicated Host (target resource id: %q): %v", id.DedicatedHostIdRaw, err) } - return utils.Bool(resp.Value != nil && len(*resp.Value) != 0), nil + return utils.Bool(resp.Model != nil && resp.Model.Value != nil && len(*resp.Model.Value) != 0), nil } func (r MaintenanceAssignmentDedicatedHostResource) basic(data acceptance.TestData) string { @@ -98,7 +101,7 @@ resource "azurerm_maintenance_configuration" "test" { name = "acctest-MC%[1]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - scope = "All" + scope = "Host" } resource "azurerm_dedicated_host_group" "test" { diff --git a/internal/services/maintenance/maintenance_assignment_virtual_machine_resource.go b/internal/services/maintenance/maintenance_assignment_virtual_machine_resource.go index b264e0bfed37..9b735287916e 100644 --- a/internal/services/maintenance/maintenance_assignment_virtual_machine_resource.go +++ b/internal/services/maintenance/maintenance_assignment_virtual_machine_resource.go @@ -6,15 +6,16 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/maintenance/mgmt/2021-05-01/maintenance" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/configurationassignments" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" parseCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" validateCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -42,10 +43,11 @@ func resourceArmMaintenanceAssignmentVirtualMachine() *pluginsdk.Resource { "location": azure.SchemaLocation(), "maintenance_configuration_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.MaintenanceConfigurationID, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: maintenanceconfigurations.ValidateMaintenanceConfigurationID, + DiffSuppressFunc: suppress.CaseDifference, // TODO remove in 4.0 with a work around or when https://github.com/Azure/azure-rest-api-specs/issues/8653 is fixed }, "virtual_machine_id": { @@ -53,7 +55,7 @@ func resourceArmMaintenanceAssignmentVirtualMachine() *pluginsdk.Resource { Required: true, ForceNew: true, ValidateFunc: validateCompute.VirtualMachineID, - DiffSuppressFunc: suppress.CaseDifference, + DiffSuppressFunc: suppress.CaseDifference, // TODO remove in 4.0 }, }, } @@ -64,41 +66,47 @@ func resourceArmMaintenanceAssignmentVirtualMachineCreate(d *pluginsdk.ResourceD ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - virtualMachineIdRaw := d.Get("virtual_machine_id").(string) - virtualMachineId, _ := parseCompute.VirtualMachineID(virtualMachineIdRaw) + virtualMachineId, err := parseCompute.VirtualMachineID(d.Get("virtual_machine_id").(string)) + if err != nil { + return err + } - existingList, err := getMaintenanceAssignmentVirtualMachine(ctx, client, virtualMachineId, virtualMachineIdRaw) + configurationId, err := maintenanceconfigurations.ParseMaintenanceConfigurationID(d.Get("maintenance_configuration_id").(string)) + if err != nil { + return err + } + + existingList, err := getMaintenanceAssignmentVirtualMachine(ctx, client, virtualMachineId, virtualMachineId.ID()) if err != nil { return err } if existingList != nil && len(*existingList) > 0 { existing := (*existingList)[0] - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_maintenance_assignment_virtual_machine", *existing.ID) + if existing.Id != nil && *existing.Id != "" { + return tf.ImportAsExistsError("azurerm_maintenance_assignment_virtual_machine", configurationId.ID()) } } - maintenanceConfigurationID := d.Get("maintenance_configuration_id").(string) - configurationId, _ := parse.MaintenanceConfigurationIDInsensitively(maintenanceConfigurationID) - // set assignment name to configuration name - assignmentName := configurationId.Name - configurationAssignment := maintenance.ConfigurationAssignment{ + assignmentName := configurationId.ResourceName + configurationAssignment := configurationassignments.ConfigurationAssignment{ Name: utils.String(assignmentName), Location: utils.String(location.Normalize(d.Get("location").(string))), - ConfigurationAssignmentProperties: &maintenance.ConfigurationAssignmentProperties{ - MaintenanceConfigurationID: utils.String(maintenanceConfigurationID), - ResourceID: utils.String(virtualMachineIdRaw), + Properties: &configurationassignments.ConfigurationAssignmentProperties{ + MaintenanceConfigurationId: utils.String(configurationId.ID()), + ResourceId: utils.String(virtualMachineId.ID()), }, } + id := configurationassignments.NewConfigurationAssignmentID(virtualMachineId.SubscriptionId, virtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", virtualMachineId.Name, assignmentName) + // It may take a few minutes after starting a VM for it to become available to assign to a configuration err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError { - if _, err := client.CreateOrUpdate(ctx, virtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", virtualMachineId.Name, assignmentName, configurationAssignment); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, configurationAssignment); err != nil { if strings.Contains(err.Error(), "It may take a few minutes after starting a VM for it to become available to assign to a configuration") { return pluginsdk.RetryableError(fmt.Errorf("expected VM is available to assign to a configuration but was in pending state, retrying")) } - return pluginsdk.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (virtual machine ID %q): %+v", virtualMachineIdRaw, err)) + return pluginsdk.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (virtual machine ID %q): %+v", virtualMachineId.ID(), err)) } return nil @@ -107,19 +115,7 @@ func resourceArmMaintenanceAssignmentVirtualMachineCreate(d *pluginsdk.ResourceD return err } - resp, err := getMaintenanceAssignmentVirtualMachine(ctx, client, virtualMachineId, virtualMachineIdRaw) - if err != nil { - return err - } - if resp == nil || len(*resp) == 0 { - return fmt.Errorf("could not find Maintenance assignment (virtual machine ID: %q)", virtualMachineIdRaw) - } - assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { - return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine ID %q)", virtualMachineIdRaw) - } - - d.SetId(*assignment.ID) + d.SetId(id.ID()) return resourceArmMaintenanceAssignmentVirtualMachineRead(d, meta) } @@ -142,7 +138,7 @@ func resourceArmMaintenanceAssignmentVirtualMachineRead(d *pluginsdk.ResourceDat return nil } assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { + if assignment.Id == nil || *assignment.Id == "" { return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine ID id: %q", id.VirtualMachineIdRaw) } @@ -152,8 +148,16 @@ func resourceArmMaintenanceAssignmentVirtualMachineRead(d *pluginsdk.ResourceDat virtualMachineId = id.VirtualMachineId.ID() } d.Set("virtual_machine_id", virtualMachineId) - if props := assignment.ConfigurationAssignmentProperties; props != nil { - d.Set("maintenance_configuration_id", props.MaintenanceConfigurationID) + if props := assignment.Properties; props != nil { + maintenanceConfigurationId := "" + if props.MaintenanceConfigurationId != nil { + parsedId, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(*props.MaintenanceConfigurationId) + if err != nil { + return fmt.Errorf("parsing %q: %+v", *props.MaintenanceConfigurationId, err) + } + maintenanceConfigurationId = parsedId.ID() + } + d.Set("maintenance_configuration_id", maintenanceConfigurationId) } return nil } @@ -163,25 +167,30 @@ func resourceArmMaintenanceAssignmentVirtualMachineDelete(d *pluginsdk.ResourceD ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MaintenanceAssignmentVirtualMachineID(d.Id()) + vmId, err := parse.MaintenanceAssignmentVirtualMachineID(d.Id()) if err != nil { return err } - if _, err := client.Delete(ctx, id.VirtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", id.VirtualMachineId.Name, id.Name); err != nil { - return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", id.VirtualMachineIdRaw, err) + id := configurationassignments.NewConfigurationAssignmentID(vmId.VirtualMachineId.SubscriptionId, vmId.VirtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", vmId.VirtualMachineId.Name, vmId.Name) + + if _, err := client.Delete(ctx, id); err != nil { + return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", vmId.VirtualMachineIdRaw, err) } return nil } -func getMaintenanceAssignmentVirtualMachine(ctx context.Context, client *maintenance.ConfigurationAssignmentsClient, id *parseCompute.VirtualMachineId, virtualMachineId string) (result *[]maintenance.ConfigurationAssignment, err error) { - resp, err := client.List(ctx, id.ResourceGroup, "Microsoft.Compute", "virtualMachines", id.Name) +func getMaintenanceAssignmentVirtualMachine(ctx context.Context, client *configurationassignments.ConfigurationAssignmentsClient, vmId *parseCompute.VirtualMachineId, virtualMachineId string) (result *[]configurationassignments.ConfigurationAssignment, err error) { + + id := configurationassignments.NewProviderID(vmId.SubscriptionId, vmId.ResourceGroup, "Microsoft.Compute", "virtualMachines", vmId.Name) + resp, err := client.List(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(resp.Response) { + if !response.WasNotFound(resp.HttpResponse) { err = fmt.Errorf("checking for presence of existing Maintenance assignment (virtual machine ID: %q): %+v", virtualMachineId, err) return } } - return resp.Value, nil + + return resp.Model.Value, nil } diff --git a/internal/services/maintenance/maintenance_assignment_virtual_machine_resource_test.go b/internal/services/maintenance/maintenance_assignment_virtual_machine_resource_test.go index 137aeb11467e..a65215d2f288 100644 --- a/internal/services/maintenance/maintenance_assignment_virtual_machine_resource_test.go +++ b/internal/services/maintenance/maintenance_assignment_virtual_machine_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/configurationassignments" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -47,17 +48,19 @@ func TestAccMaintenanceAssignmentVirtualMachine_requiresImport(t *testing.T) { } func (MaintenanceAssignmentVirtualMachineResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MaintenanceAssignmentVirtualMachineID(state.ID) + maintenanceAssignmentVirtualMachineId, err := parse.MaintenanceAssignmentVirtualMachineID(state.ID) if err != nil { return nil, err } - resp, err := clients.Maintenance.ConfigurationAssignmentsClient.List(ctx, id.VirtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", id.VirtualMachineId.Name) + id := configurationassignments.NewProviderID(maintenanceAssignmentVirtualMachineId.VirtualMachineId.SubscriptionId, maintenanceAssignmentVirtualMachineId.VirtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", maintenanceAssignmentVirtualMachineId.VirtualMachineId.Name) + + resp, err := clients.Maintenance.ConfigurationAssignmentsClient.List(ctx, id) if err != nil { - return nil, fmt.Errorf("retrieving Maintenance Assignment Virtual Machine (target resource id: %q): %v", id.VirtualMachineIdRaw, err) + return nil, fmt.Errorf("retrieving Maintenance Assignment Virtual Machine (target resource id: %q): %v", maintenanceAssignmentVirtualMachineId.VirtualMachineIdRaw, err) } - return utils.Bool(resp.Value != nil && len(*resp.Value) != 0), nil + return utils.Bool(resp.Model != nil && resp.Model.Value != nil && len(*resp.Model.Value) != 0), nil } func (r MaintenanceAssignmentVirtualMachineResource) basic(data acceptance.TestData) string { @@ -99,7 +102,7 @@ resource "azurerm_maintenance_configuration" "test" { name = "acctest-MC%[1]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - scope = "All" + scope = "SQLDB" } resource "azurerm_virtual_network" "test" { diff --git a/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource.go b/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource.go index a98abe2ca8c8..ad1f667c525f 100644 --- a/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource.go +++ b/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource.go @@ -5,15 +5,16 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/maintenance/mgmt/2021-05-01/maintenance" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/configurationassignments" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" parseCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" validateCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -41,10 +42,11 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSet() *pluginsdk.Resourc "location": azure.SchemaLocation(), "maintenance_configuration_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.MaintenanceConfigurationID, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: maintenanceconfigurations.ValidateMaintenanceConfigurationID, + DiffSuppressFunc: suppress.CaseDifference, // TODO remove in 4.0 with a work around or when https://github.com/Azure/azure-rest-api-specs/issues/8653 is fixed }, "virtual_machine_scale_set_id": { @@ -52,7 +54,7 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSet() *pluginsdk.Resourc Required: true, ForceNew: true, ValidateFunc: validateCompute.VirtualMachineScaleSetID, - DiffSuppressFunc: suppress.CaseDifference, + DiffSuppressFunc: suppress.CaseDifference, // TODO remove in 4.0 }, }, } @@ -63,52 +65,44 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSetCreate(d *pluginsdk.R ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - virtualMachineScaleSetIdRaw := d.Get("virtual_machine_scale_set_id").(string) - virtualMachineScaleSetId, _ := parseCompute.VirtualMachineScaleSetID(virtualMachineScaleSetIdRaw) + virtualMachineScaleSetId, err := parseCompute.VirtualMachineScaleSetID(d.Get("virtual_machine_scale_set_id").(string)) + if err != nil { + return err + } - existingList, err := getMaintenanceAssignmentVirtualMachineScaleSet(ctx, client, virtualMachineScaleSetId, virtualMachineScaleSetIdRaw) + maintenanceConfigurationID, err := maintenanceconfigurations.ParseMaintenanceConfigurationID(d.Get("maintenance_configuration_id").(string)) + if err != nil { + return err + } + + configAssignmentId := configurationassignments.NewConfigurationAssignmentID(virtualMachineScaleSetId.SubscriptionId, virtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", virtualMachineScaleSetId.Name, maintenanceConfigurationID.ResourceName) + + existingList, err := getMaintenanceAssignmentVirtualMachineScaleSet(ctx, client, virtualMachineScaleSetId) if err != nil { return err } if existingList != nil && len(*existingList) > 0 { existing := (*existingList)[0] - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_maintenance_assignment_virtual_machine_scale_set", *existing.ID) + if existing.Id != nil && *existing.Id != "" { + return tf.ImportAsExistsError("azurerm_maintenance_assignment_virtual_machine_scale_set", configAssignmentId.ID()) } } - maintenanceConfigurationID := d.Get("maintenance_configuration_id").(string) - configurationId, _ := parse.MaintenanceConfigurationIDInsensitively(maintenanceConfigurationID) - - // set assignment name to configuration name - assignmentName := configurationId.Name - configurationAssignment := maintenance.ConfigurationAssignment{ - Name: utils.String(assignmentName), + configurationAssignment := configurationassignments.ConfigurationAssignment{ + Name: utils.String(maintenanceConfigurationID.ResourceName), Location: utils.String(location.Normalize(d.Get("location").(string))), - ConfigurationAssignmentProperties: &maintenance.ConfigurationAssignmentProperties{ - MaintenanceConfigurationID: utils.String(maintenanceConfigurationID), - ResourceID: utils.String(virtualMachineScaleSetIdRaw), + Properties: &configurationassignments.ConfigurationAssignmentProperties{ + MaintenanceConfigurationId: utils.String(maintenanceConfigurationID.ID()), + ResourceId: utils.String(virtualMachineScaleSetId.ID()), }, } - _, err = client.CreateOrUpdate(ctx, virtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", virtualMachineScaleSetId.Name, assignmentName, configurationAssignment) + _, err = client.CreateOrUpdate(ctx, configAssignmentId, configurationAssignment) if err != nil { return err } - resp, err := getMaintenanceAssignmentVirtualMachineScaleSet(ctx, client, virtualMachineScaleSetId, virtualMachineScaleSetIdRaw) - if err != nil { - return err - } - if resp == nil || len(*resp) == 0 { - return fmt.Errorf("could not find Maintenance assignment (virtual machine scale set ID: %q)", virtualMachineScaleSetIdRaw) - } - assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { - return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine scale set ID %q)", virtualMachineScaleSetIdRaw) - } - - d.SetId(*assignment.ID) + d.SetId(configAssignmentId.ID()) return resourceArmMaintenanceAssignmentVirtualMachineScaleSetRead(d, meta) } @@ -122,7 +116,7 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSetRead(d *pluginsdk.Res return err } - resp, err := getMaintenanceAssignmentVirtualMachineScaleSet(ctx, client, id.VirtualMachineScaleSetId, id.VirtualMachineScaleSetIdRaw) + resp, err := getMaintenanceAssignmentVirtualMachineScaleSet(ctx, client, id.VirtualMachineScaleSetId) if err != nil { return err } @@ -131,7 +125,7 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSetRead(d *pluginsdk.Res return nil } assignment := (*resp)[0] - if assignment.ID == nil || *assignment.ID == "" { + if assignment.Id == nil || *assignment.Id == "" { return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine scale set ID id: %q", id.VirtualMachineScaleSetIdRaw) } @@ -141,8 +135,16 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSetRead(d *pluginsdk.Res virtualMachineScaleSetId = id.VirtualMachineScaleSetId.ID() } d.Set("virtual_machine_scale_set_id", virtualMachineScaleSetId) - if props := assignment.ConfigurationAssignmentProperties; props != nil { - d.Set("maintenance_configuration_id", props.MaintenanceConfigurationID) + if props := assignment.Properties; props != nil { + maintenanceConfigurationId := "" + if props.MaintenanceConfigurationId != nil { + parsedId, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(*props.MaintenanceConfigurationId) + if err != nil { + return fmt.Errorf("parsing %q: %+v", *props.MaintenanceConfigurationId, err) + } + maintenanceConfigurationId = parsedId.ID() + } + d.Set("maintenance_configuration_id", maintenanceConfigurationId) } return nil } @@ -152,25 +154,30 @@ func resourceArmMaintenanceAssignmentVirtualMachineScaleSetDelete(d *pluginsdk.R ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MaintenanceAssignmentVirtualMachineScaleSetID(d.Id()) + maintenanceAssignmentVmScaleSetId, err := parse.MaintenanceAssignmentVirtualMachineScaleSetID(d.Id()) if err != nil { return err } - if _, err := client.Delete(ctx, id.VirtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", id.VirtualMachineScaleSetId.Name, id.Name); err != nil { - return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", id.VirtualMachineScaleSetIdRaw, err) + id := configurationassignments.NewConfigurationAssignmentID(maintenanceAssignmentVmScaleSetId.VirtualMachineScaleSetId.SubscriptionId, maintenanceAssignmentVmScaleSetId.VirtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", maintenanceAssignmentVmScaleSetId.VirtualMachineScaleSetId.Name, maintenanceAssignmentVmScaleSetId.Name) + + if _, err := client.Delete(ctx, id); err != nil { + return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", maintenanceAssignmentVmScaleSetId.VirtualMachineScaleSetIdRaw, err) } return nil } -func getMaintenanceAssignmentVirtualMachineScaleSet(ctx context.Context, client *maintenance.ConfigurationAssignmentsClient, id *parseCompute.VirtualMachineScaleSetId, virtualMachineScaleSetId string) (result *[]maintenance.ConfigurationAssignment, err error) { - resp, err := client.List(ctx, id.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", id.Name) +func getMaintenanceAssignmentVirtualMachineScaleSet(ctx context.Context, client *configurationassignments.ConfigurationAssignmentsClient, vmScaleSetId *parseCompute.VirtualMachineScaleSetId) (result *[]configurationassignments.ConfigurationAssignment, err error) { + + id := configurationassignments.NewProviderID(vmScaleSetId.SubscriptionId, vmScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", vmScaleSetId.Name) + + resp, err := client.List(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(resp.Response) { - err = fmt.Errorf("checking for presence of existing Maintenance assignment (virtual machine scale set ID: %q): %+v", virtualMachineScaleSetId, err) + if !response.WasNotFound(resp.HttpResponse) { + err = fmt.Errorf("checking for presence of existing Maintenance assignment (virtual machine scale set ID: %q): %+v", vmScaleSetId.ID(), err) return } } - return resp.Value, nil + return resp.Model.Value, nil } diff --git a/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource_test.go b/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource_test.go index c4953a476159..2636c7bed85e 100644 --- a/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource_test.go +++ b/internal/services/maintenance/maintenance_assignment_virtual_machine_scale_set_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/configurationassignments" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -47,17 +48,19 @@ func TestAccMaintenanceAssignmentVirtualMachineScaleSet_requiresImport(t *testin } func (MaintenanceAssignmentVirtualMachineScaleSetResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MaintenanceAssignmentVirtualMachineScaleSetID(state.ID) + maVmScaleSetID, err := parse.MaintenanceAssignmentVirtualMachineScaleSetID(state.ID) if err != nil { return nil, err } - resp, err := clients.Maintenance.ConfigurationAssignmentsClient.List(ctx, id.VirtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", id.VirtualMachineScaleSetId.Name) + id := configurationassignments.NewProviderID(maVmScaleSetID.VirtualMachineScaleSetId.SubscriptionId, maVmScaleSetID.VirtualMachineScaleSetId.ResourceGroup, "Microsoft.Compute", "virtualMachineScaleSets", maVmScaleSetID.VirtualMachineScaleSetId.Name) + + resp, err := clients.Maintenance.ConfigurationAssignmentsClient.List(ctx, id) if err != nil { - return nil, fmt.Errorf("retrieving Maintenance Assignment Virtual Machine Scale Set (target resource id: %q): %v", id.VirtualMachineScaleSetIdRaw, err) + return nil, fmt.Errorf("retrieving Maintenance Assignment Virtual Machine Scale Set (target resource id: %q): %v", maVmScaleSetID.VirtualMachineScaleSetIdRaw, err) } - return utils.Bool(resp.Value != nil && len(*resp.Value) != 0), nil + return utils.Bool(resp.Model != nil && resp.Model.Value != nil && len(*resp.Model.Value) != 0), nil } func (r MaintenanceAssignmentVirtualMachineScaleSetResource) basic(data acceptance.TestData) string { diff --git a/internal/services/maintenance/maintenance_configuration_data_source.go b/internal/services/maintenance/maintenance_configuration_data_source.go index bc58a751ce0a..f4bffb7fde7a 100644 --- a/internal/services/maintenance/maintenance_configuration_data_source.go +++ b/internal/services/maintenance/maintenance_configuration_data_source.go @@ -4,15 +4,15 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceMaintenanceConfiguration() *pluginsdk.Resource { @@ -92,10 +92,10 @@ func dataSourceArmMaintenanceConfigurationRead(d *pluginsdk.ResourceData, meta i ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewMaintenanceConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + id := maintenanceconfigurations.NewMaintenanceConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } @@ -103,20 +103,25 @@ func dataSourceArmMaintenanceConfigurationRead(d *pluginsdk.ResourceData, meta i } d.SetId(id.ID()) - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) - - if props := resp.ConfigurationProperties; props != nil { - d.Set("scope", props.MaintenanceScope) - d.Set("visibility", props.Visibility) - d.Set("properties", props.ExtensionProperties) - - window := flattenMaintenanceConfigurationWindow(props.Window) - if err := d.Set("window", window); err != nil { - return fmt.Errorf("setting `window`: %+v", err) + d.Set("name", id.ResourceName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("scope", props.MaintenanceScope) + d.Set("visibility", props.Visibility) + d.Set("properties", props.ExtensionProperties) + + window := flattenMaintenanceConfigurationWindow(props.MaintenanceWindow) + if err := d.Set("window", window); err != nil { + return fmt.Errorf("setting `window`: %+v", err) + } + } + d.Set("location", location.NormalizeNilable(model.Location)) + if err = tags.FlattenAndSet(d, flattenTags(model.Tags)); err != nil { + return err } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/maintenance/maintenance_configuration_resource.go b/internal/services/maintenance/maintenance_configuration_resource.go index 59c7a00757b7..94d3d90172f2 100644 --- a/internal/services/maintenance/maintenance_configuration_resource.go +++ b/internal/services/maintenance/maintenance_configuration_resource.go @@ -6,13 +6,13 @@ import ( "regexp" "time" - "github.com/Azure/azure-sdk-for-go/services/maintenance/mgmt/2021-05-01/maintenance" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/migration" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -41,7 +41,7 @@ func resourceArmMaintenanceConfiguration() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MaintenanceConfigurationIDInsensitively(id) + _, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(id) return err }), @@ -59,25 +59,23 @@ func resourceArmMaintenanceConfiguration() *pluginsdk.Resource { "scope": { Type: pluginsdk.TypeString, - Optional: true, - Default: "All", + Required: true, ValidateFunc: validation.StringInSlice([]string{ - "All", // All is still accepted by the API - string(maintenance.ScopeExtension), - string(maintenance.ScopeHost), - string(maintenance.ScopeInGuestPatch), - string(maintenance.ScopeOSImage), - string(maintenance.ScopeSQLDB), - string(maintenance.ScopeSQLManagedInstance), + string(maintenanceconfigurations.MaintenanceScopeExtension), + string(maintenanceconfigurations.MaintenanceScopeHost), + string(maintenanceconfigurations.MaintenanceScopeInGuestPatch), + string(maintenanceconfigurations.MaintenanceScopeOSImage), + string(maintenanceconfigurations.MaintenanceScopeSQLDB), + string(maintenanceconfigurations.MaintenanceScopeSQLManagedInstance), }, false), }, "visibility": { Type: pluginsdk.TypeString, Optional: true, - Default: string(maintenance.VisibilityCustom), + Default: string(maintenanceconfigurations.VisibilityCustom), ValidateFunc: validation.StringInSlice([]string{ - string(maintenance.VisibilityCustom), + string(maintenanceconfigurations.VisibilityCustom), // Creating public configurations doesn't appear to be supported, API returns `Public Maintenance Configuration must set correct properties` // string(maintenance.VisibilityPublic), }, false), @@ -139,40 +137,38 @@ func resourceArmMaintenanceConfigurationCreateUpdate(d *pluginsdk.ResourceData, ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewMaintenanceConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + id := maintenanceconfigurations.NewMaintenanceConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_maintenance_configuration", id.ID()) } } - scope := d.Get("scope").(string) - visibility := d.Get("visibility").(string) + scope := maintenanceconfigurations.MaintenanceScope(d.Get("scope").(string)) + visibility := maintenanceconfigurations.Visibility(d.Get("visibility").(string)) windowRaw := d.Get("window").([]interface{}) window := expandMaintenanceConfigurationWindow(windowRaw) - extensionProperties := utils.ExpandMapStringPtrString(d.Get("properties").(map[string]interface{})) - - configuration := maintenance.Configuration{ - Name: utils.String(id.Name), + configuration := maintenanceconfigurations.MaintenanceConfiguration{ + Name: utils.String(id.ResourceName), Location: utils.String(location.Normalize(d.Get("location").(string))), - ConfigurationProperties: &maintenance.ConfigurationProperties{ - MaintenanceScope: maintenance.Scope(scope), - Visibility: maintenance.Visibility(visibility), + Properties: &maintenanceconfigurations.MaintenanceConfigurationProperties{ + MaintenanceScope: &scope, + Visibility: &visibility, Namespace: utils.String("Microsoft.Maintenance"), - Window: window, - ExtensionProperties: extensionProperties, + MaintenanceWindow: window, + ExtensionProperties: expandExtensionProperties(d.Get("properties").(map[string]interface{})), }, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + Tags: expandTags(d.Get("tags").(map[string]interface{})), } - if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, configuration); err != nil { + if _, err := client.CreateOrUpdate(ctx, id, configuration); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } @@ -185,14 +181,14 @@ func resourceArmMaintenanceConfigurationRead(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MaintenanceConfigurationIDInsensitively(d.Id()) + id, err := maintenanceconfigurations.ParseMaintenanceConfigurationID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] maintenance %q does not exist - removing from state", d.Id()) d.SetId("") return nil @@ -200,20 +196,26 @@ func resourceArmMaintenanceConfigurationRead(d *pluginsdk.ResourceData, meta int return fmt.Errorf("retrieving %s: %+v", id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) - if props := resp.ConfigurationProperties; props != nil { - d.Set("scope", props.MaintenanceScope) - d.Set("visibility", props.Visibility) - d.Set("properties", props.ExtensionProperties) - - window := flattenMaintenanceConfigurationWindow(props.Window) - if err := d.Set("window", window); err != nil { - return fmt.Errorf("setting `window`: %+v", err) + d.Set("name", id.ResourceName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("scope", props.MaintenanceScope) + d.Set("visibility", props.Visibility) + d.Set("properties", props.ExtensionProperties) + + window := flattenMaintenanceConfigurationWindow(props.MaintenanceWindow) + if err := d.Set("window", window); err != nil { + return fmt.Errorf("setting `window`: %+v", err) + } + } + d.Set("location", location.NormalizeNilable(model.Location)) + if err = tags.FlattenAndSet(d, flattenTags(model.Tags)); err != nil { + return err } } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourceArmMaintenanceConfigurationDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -221,18 +223,18 @@ func resourceArmMaintenanceConfigurationDelete(d *pluginsdk.ResourceData, meta i ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MaintenanceConfigurationIDInsensitively(d.Id()) + id, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(d.Id()) if err != nil { return err } - if _, err := client.Delete(ctx, id.ResourceGroup, id.Name); err != nil { + if _, err := client.Delete(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } return nil } -func expandMaintenanceConfigurationWindow(input []interface{}) *maintenance.Window { +func expandMaintenanceConfigurationWindow(input []interface{}) *maintenanceconfigurations.MaintenanceWindow { if len(input) == 0 { return nil } @@ -243,7 +245,7 @@ func expandMaintenanceConfigurationWindow(input []interface{}) *maintenance.Wind duration := v["duration"].(string) timeZone := v["time_zone"].(string) recurEvery := v["recur_every"].(string) - window := maintenance.Window{ + window := maintenanceconfigurations.MaintenanceWindow{ StartDateTime: utils.String(startDateTime), ExpirationDateTime: utils.String(expirationDateTime), Duration: utils.String(duration), @@ -253,7 +255,7 @@ func expandMaintenanceConfigurationWindow(input []interface{}) *maintenance.Wind return &window } -func flattenMaintenanceConfigurationWindow(input *maintenance.Window) []interface{} { +func flattenMaintenanceConfigurationWindow(input *maintenanceconfigurations.MaintenanceWindow) []interface{} { results := make([]interface{}, 0) if v := input; v != nil { @@ -284,3 +286,11 @@ func flattenMaintenanceConfigurationWindow(input *maintenance.Window) []interfac return results } + +func expandExtensionProperties(input map[string]interface{}) *map[string]string { + output := make(map[string]string) + for k, v := range input { + output[k] = v.(string) + } + return &output +} diff --git a/internal/services/maintenance/maintenance_configuration_resource_test.go b/internal/services/maintenance/maintenance_configuration_resource_test.go index 5696b78b802b..d621c0cf3101 100644 --- a/internal/services/maintenance/maintenance_configuration_resource_test.go +++ b/internal/services/maintenance/maintenance_configuration_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -24,7 +24,7 @@ func TestAccMaintenanceConfiguration_basic(t *testing.T) { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("scope").HasValue("All"), + check.That(data.ResourceName).Key("scope").HasValue("SQLDB"), check.That(data.ResourceName).Key("visibility").HasValue("Custom"), ), }, @@ -82,7 +82,7 @@ func TestAccMaintenanceConfiguration_update(t *testing.T) { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("scope").HasValue("All"), + check.That(data.ResourceName).Key("scope").HasValue("SQLDB"), check.That(data.ResourceName).Key("visibility").HasValue("Custom"), check.That(data.ResourceName).Key("tags.%").HasValue("0"), check.That(data.ResourceName).Key("window.#").HasValue("0"), @@ -112,7 +112,7 @@ func TestAccMaintenanceConfiguration_update(t *testing.T) { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("scope").HasValue("All"), + check.That(data.ResourceName).Key("scope").HasValue("SQLDB"), check.That(data.ResourceName).Key("visibility").HasValue("Custom"), check.That(data.ResourceName).Key("tags.%").HasValue("0"), check.That(data.ResourceName).Key("window.#").HasValue("0"), @@ -124,17 +124,17 @@ func TestAccMaintenanceConfiguration_update(t *testing.T) { } func (MaintenanceConfigurationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MaintenanceConfigurationIDInsensitively(state.ID) + id, err := maintenanceconfigurations.ParseMaintenanceConfigurationIDInsensitively(state.ID) if err != nil { return nil, err } - resp, err := clients.Maintenance.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.Maintenance.ConfigurationsClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving Maintenance Configuration %s (resource group: %s): %v", id.Name, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.ConfigurationProperties != nil), nil + return utils.Bool(resp.Model != nil && resp.Model.Properties != nil), nil } func (MaintenanceConfigurationResource) basic(data acceptance.TestData) string { @@ -152,7 +152,7 @@ resource "azurerm_maintenance_configuration" "test" { name = "acctest-MC%d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - scope = "All" + scope = "SQLDB" visibility = "Custom" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) diff --git a/internal/services/maintenance/migration/configuration_v0_to_v1.go b/internal/services/maintenance/migration/configuration_v0_to_v1.go index e446451fce9e..02cfd60786e3 100644 --- a/internal/services/maintenance/migration/configuration_v0_to_v1.go +++ b/internal/services/maintenance/migration/configuration_v0_to_v1.go @@ -4,8 +4,8 @@ import ( "context" "log" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/maintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -57,7 +57,7 @@ func (ConfigurationV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { name := rawState["name"].(string) resourceGroup := rawState["resource_group_name"].(string) - id := parse.NewMaintenanceConfigurationID(subscriptionId, resourceGroup, name) + id := maintenanceconfigurations.NewMaintenanceConfigurationID(subscriptionId, resourceGroup, name) rawState["id"] = id.ID() diff --git a/internal/services/maintenance/parse/maintenance_assignment_dedicated_host.go b/internal/services/maintenance/parse/maintenance_assignment_dedicated_host.go index 1aeb0b31897c..0335b7f1d436 100644 --- a/internal/services/maintenance/parse/maintenance_assignment_dedicated_host.go +++ b/internal/services/maintenance/parse/maintenance_assignment_dedicated_host.go @@ -4,11 +4,11 @@ import ( "fmt" "regexp" - parseCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" ) type MaintenanceAssignmentDedicatedHostId struct { - DedicatedHostId *parseCompute.DedicatedHostId + DedicatedHostId dedicatedhosts.HostId DedicatedHostIdRaw string Name string } @@ -20,13 +20,13 @@ func MaintenanceAssignmentDedicatedHostID(input string) (*MaintenanceAssignmentD } targetResourceId, name := groups[1], groups[2] - dedicatedHostID, err := parseCompute.DedicatedHostID(targetResourceId) + dedicatedHostID, err := dedicatedhosts.ParseHostIDInsensitively(targetResourceId) if err != nil { return nil, fmt.Errorf("parsing Maintenance Assignment Dedicated Host ID: %q: Expected valid Dedicated Host ID", input) } return &MaintenanceAssignmentDedicatedHostId{ - DedicatedHostId: dedicatedHostID, + DedicatedHostId: *dedicatedHostID, DedicatedHostIdRaw: targetResourceId, Name: name, }, nil diff --git a/internal/services/maintenance/parse/maintenance_assignment_dedicated_host_test.go b/internal/services/maintenance/parse/maintenance_assignment_dedicated_host_test.go index 2c47d2a31039..1ef18a12f9e6 100644 --- a/internal/services/maintenance/parse/maintenance_assignment_dedicated_host_test.go +++ b/internal/services/maintenance/parse/maintenance_assignment_dedicated_host_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - parseCompute "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" + "github.com/hashicorp/go-azure-sdk/resource-manager/compute/2021-11-01/dedicatedhosts" ) func TestMaintenanceAssignmentDedicatedHostID(t *testing.T) { @@ -55,11 +55,11 @@ func TestMaintenanceAssignmentDedicatedHostID(t *testing.T) { Error: false, Expect: &MaintenanceAssignmentDedicatedHostId{ DedicatedHostIdRaw: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resGroup1/providers/microsoft.compute/hostGroups/group1/hosts/host1", - DedicatedHostId: &parseCompute.DedicatedHostId{ - SubscriptionId: "00000000-0000-0000-0000-000000000000", - ResourceGroup: "resGroup1", - HostGroupName: "group1", - HostName: "host1", + DedicatedHostId: dedicatedhosts.HostId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroupName: "resGroup1", + HostGroupName: "group1", + HostName: "host1", }, Name: "assign1", }, diff --git a/internal/services/maintenance/parse/maintenance_configuration.go b/internal/services/maintenance/parse/maintenance_configuration.go deleted file mode 100644 index 06a7602d2dd1..000000000000 --- a/internal/services/maintenance/parse/maintenance_configuration.go +++ /dev/null @@ -1,113 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MaintenanceConfigurationId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewMaintenanceConfigurationID(subscriptionId, resourceGroup, name string) MaintenanceConfigurationId { - return MaintenanceConfigurationId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id MaintenanceConfigurationId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Maintenance Configuration", segmentsStr) -} - -func (id MaintenanceConfigurationId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Maintenance/maintenanceConfigurations/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// MaintenanceConfigurationID parses a MaintenanceConfiguration ID into an MaintenanceConfigurationId struct -func MaintenanceConfigurationID(input string) (*MaintenanceConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MaintenanceConfigurationId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("maintenanceConfigurations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} - -// MaintenanceConfigurationIDInsensitively parses an MaintenanceConfiguration ID into an MaintenanceConfigurationId struct, insensitively -// This should only be used to parse an ID for rewriting, the MaintenanceConfigurationID -// method should be used instead for validation etc. -// -// Whilst this may seem strange, this enables Terraform have consistent casing -// which works around issues in Core, whilst handling broken API responses. -func MaintenanceConfigurationIDInsensitively(input string) (*MaintenanceConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MaintenanceConfigurationId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - // find the correct casing for the 'maintenanceConfigurations' segment - maintenanceConfigurationsKey := "maintenanceConfigurations" - for key := range id.Path { - if strings.EqualFold(key, maintenanceConfigurationsKey) { - maintenanceConfigurationsKey = key - break - } - } - if resourceId.Name, err = id.PopSegment(maintenanceConfigurationsKey); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/maintenance/parse/maintenance_configuration_test.go b/internal/services/maintenance/parse/maintenance_configuration_test.go deleted file mode 100644 index 3f89fe685581..000000000000 --- a/internal/services/maintenance/parse/maintenance_configuration_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MaintenanceConfigurationId{} - -func TestMaintenanceConfigurationIDFormatter(t *testing.T) { - actual := NewMaintenanceConfigurationID("12345678-1234-9876-4563-123456789012", "resGroup1", "maintenanceConfiguration1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/maintenanceConfiguration1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMaintenanceConfigurationID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MaintenanceConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/maintenanceConfiguration1", - Expected: &MaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "maintenanceConfiguration1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MAINTENANCE/MAINTENANCECONFIGURATIONS/MAINTENANCECONFIGURATION1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MaintenanceConfigurationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} - -func TestMaintenanceConfigurationIDInsensitively(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MaintenanceConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/maintenanceConfiguration1", - Expected: &MaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "maintenanceConfiguration1", - }, - }, - - { - // lower-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceconfigurations/maintenanceConfiguration1", - Expected: &MaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "maintenanceConfiguration1", - }, - }, - - { - // upper-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/MAINTENANCECONFIGURATIONS/maintenanceConfiguration1", - Expected: &MaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "maintenanceConfiguration1", - }, - }, - - { - // mixed-cased segment names - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/MaInTeNaNcEcOnFiGuRaTiOnS/maintenanceConfiguration1", - Expected: &MaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "maintenanceConfiguration1", - }, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MaintenanceConfigurationIDInsensitively(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/maintenance/parse/public_maintenance_configuration.go b/internal/services/maintenance/parse/public_maintenance_configuration.go deleted file mode 100644 index da48a54c8c4a..000000000000 --- a/internal/services/maintenance/parse/public_maintenance_configuration.go +++ /dev/null @@ -1,61 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type PublicMaintenanceConfigurationId struct { - SubscriptionId string - Name string -} - -func NewPublicMaintenanceConfigurationID(subscriptionId, name string) PublicMaintenanceConfigurationId { - return PublicMaintenanceConfigurationId{ - SubscriptionId: subscriptionId, - Name: name, - } -} - -func (id PublicMaintenanceConfigurationId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Public Maintenance Configuration", segmentsStr) -} - -func (id PublicMaintenanceConfigurationId) ID() string { - fmtString := "/subscriptions/%s/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.Name) -} - -// PublicMaintenanceConfigurationID parses a PublicMaintenanceConfiguration ID into an PublicMaintenanceConfigurationId struct -func PublicMaintenanceConfigurationID(input string) (*PublicMaintenanceConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := PublicMaintenanceConfigurationId{ - SubscriptionId: id.SubscriptionID, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.Name, err = id.PopSegment("publicMaintenanceConfigurations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/maintenance/parse/public_maintenance_configuration_test.go b/internal/services/maintenance/parse/public_maintenance_configuration_test.go deleted file mode 100644 index 1773538c7b18..000000000000 --- a/internal/services/maintenance/parse/public_maintenance_configuration_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = PublicMaintenanceConfigurationId{} - -func TestPublicMaintenanceConfigurationIDFormatter(t *testing.T) { - actual := NewPublicMaintenanceConfigurationID("12345678-1234-9876-4563-123456789012", "publicMaintenanceConfiguration1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/publicMaintenanceConfiguration1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestPublicMaintenanceConfigurationID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *PublicMaintenanceConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/publicMaintenanceConfiguration1", - Expected: &PublicMaintenanceConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - Name: "publicMaintenanceConfiguration1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.MAINTENANCE/PUBLICMAINTENANCECONFIGURATIONS/PUBLICMAINTENANCECONFIGURATION1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := PublicMaintenanceConfigurationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/maintenance/public_maintenance_configurations_data_source.go b/internal/services/maintenance/public_maintenance_configurations_data_source.go index 9b4f02fd4a88..dacbedc9ccc9 100644 --- a/internal/services/maintenance/public_maintenance_configurations_data_source.go +++ b/internal/services/maintenance/public_maintenance_configurations_data_source.go @@ -4,13 +4,14 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/maintenance/mgmt/2021-05-01/maintenance" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/publicmaintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) const recurMondayToThursday = "Monday-Thursday" @@ -36,13 +37,12 @@ func dataSourcePublicMaintenanceConfigurations() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{ - "All", // All is still accepted by the API - string(maintenance.ScopeExtension), - string(maintenance.ScopeHost), - string(maintenance.ScopeInGuestPatch), - string(maintenance.ScopeOSImage), - string(maintenance.ScopeSQLDB), - string(maintenance.ScopeSQLManagedInstance), + string(publicmaintenanceconfigurations.MaintenanceScopeExtension), + string(publicmaintenanceconfigurations.MaintenanceScopeHost), + string(publicmaintenanceconfigurations.MaintenanceScopeInGuestPatch), + string(publicmaintenanceconfigurations.MaintenanceScopeOSImage), + string(publicmaintenanceconfigurations.MaintenanceScopeSQLDB), + string(publicmaintenanceconfigurations.MaintenanceScopeSQLManagedInstance), }, false), }, @@ -108,12 +108,14 @@ func dataSourcePublicMaintenanceConfigurations() *pluginsdk.Resource { func dataSourcePublicMaintenanceConfigurationsRead(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Maintenance.PublicConfigurationsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - resp, err := client.List(ctx) + subId := commonids.NewSubscriptionID(subscriptionId) + resp, err := client.List(ctx, subId) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("no Public Maintenance Configurations were found") } return fmt.Errorf("retrieving Public Maintenance Configurations: %+v", err) @@ -131,36 +133,38 @@ func dataSourcePublicMaintenanceConfigurationsRead(d *pluginsdk.ResourceData, me locationFilter := azure.NormalizeLocation(d.Get("location").(string)) scopeFilter := d.Get("scope").(string) - if resp.Value != nil { - for _, maintenanceConfig := range *resp.Value { + if resp.Model != nil { + if resp.Model.Value != nil { + for _, maintenanceConfig := range *resp.Model.Value { - var configLocation, configRecurEvery, configScope string - if maintenanceConfig.Location != nil { - configLocation = azure.NormalizeLocation(*maintenanceConfig.Location) - } - if props := maintenanceConfig.ConfigurationProperties; props != nil { - if props.Window != nil && props.Window.RecurEvery != nil { - configRecurEvery = *props.Window.RecurEvery + var configLocation, configRecurEvery, configScope string + if maintenanceConfig.Location != nil { + configLocation = azure.NormalizeLocation(*maintenanceConfig.Location) } - if string(props.MaintenanceScope) != "" { - configScope = string(props.MaintenanceScope) + if props := maintenanceConfig.Properties; props != nil { + if props.MaintenanceWindow != nil && props.MaintenanceWindow.RecurEvery != nil { + configRecurEvery = *props.MaintenanceWindow.RecurEvery + } + + if props.MaintenanceScope != nil { + configScope = string(*props.MaintenanceScope) + } } - } - if locationFilter != "" && locationFilter != configLocation { - continue - } - if recurEveryFilter != "" && recurEveryFilter != configRecurEvery { - continue - } - if scopeFilter != "" && scopeFilter != configScope { - continue - } + if locationFilter != "" && locationFilter != configLocation { + continue + } + if recurEveryFilter != "" && recurEveryFilter != configRecurEvery { + continue + } + if scopeFilter != "" && scopeFilter != configScope { + continue + } - filteredPublicConfigs = append(filteredPublicConfigs, flattenPublicMaintenanceConfiguration(maintenanceConfig)) + filteredPublicConfigs = append(filteredPublicConfigs, flattenPublicMaintenanceConfiguration(maintenanceConfig)) + } } } - if len(filteredPublicConfigs) == 0 { return fmt.Errorf("no Public Maintenance Configurations were found") } @@ -173,7 +177,7 @@ func dataSourcePublicMaintenanceConfigurationsRead(d *pluginsdk.ResourceData, me return nil } -func flattenPublicMaintenanceConfiguration(config maintenance.Configuration) map[string]interface{} { +func flattenPublicMaintenanceConfiguration(config publicmaintenanceconfigurations.MaintenanceConfiguration) map[string]interface{} { output := make(map[string]interface{}) output["name"] = "" @@ -182,8 +186,8 @@ func flattenPublicMaintenanceConfiguration(config maintenance.Configuration) map } output["id"] = "" - if config.ID != nil { - output["id"] = *config.ID + if config.Id != nil { + output["id"] = *config.Id } output["location"] = "" @@ -191,24 +195,28 @@ func flattenPublicMaintenanceConfiguration(config maintenance.Configuration) map output["location"] = azure.NormalizeLocation(*config.Location) } - output["maintenance_scope"] = string(config.MaintenanceScope) - - var description, recurEvery, timeZone, duration string - if props := config.ConfigurationProperties; props != nil { + var description, recurEvery, timeZone, duration, scope string + if props := config.Properties; props != nil { if props.ExtensionProperties != nil { - if configDescription, ok := props.ExtensionProperties["description"]; ok { - description = *configDescription + extensionProperties := *props.ExtensionProperties + if configDescription, ok := extensionProperties["description"]; ok { + description = configDescription } } - if props.Window != nil { - if props.Window.RecurEvery != nil { - recurEvery = *props.Window.RecurEvery + + if config.Properties.MaintenanceScope != nil { + scope = string(*config.Properties.MaintenanceScope) + } + + if props.MaintenanceWindow != nil { + if props.MaintenanceWindow.RecurEvery != nil { + recurEvery = *props.MaintenanceWindow.RecurEvery } - if props.Window.TimeZone != nil { - timeZone = *props.Window.TimeZone + if props.MaintenanceWindow.TimeZone != nil { + timeZone = *props.MaintenanceWindow.TimeZone } - if props.Window.Duration != nil { - duration = *props.Window.Duration + if props.MaintenanceWindow.Duration != nil { + duration = *props.MaintenanceWindow.Duration } } } @@ -217,6 +225,7 @@ func flattenPublicMaintenanceConfiguration(config maintenance.Configuration) map output["recur_every"] = recurEvery output["time_zone"] = timeZone output["duration"] = duration + output["maintenance_scope"] = scope return output } diff --git a/internal/services/maintenance/resourceids.go b/internal/services/maintenance/resourceids.go deleted file mode 100644 index 359b0603e7f4..000000000000 --- a/internal/services/maintenance/resourceids.go +++ /dev/null @@ -1,4 +0,0 @@ -package maintenance - -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MaintenanceConfiguration -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/maintenanceConfiguration1 -rewrite=true -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=PublicMaintenanceConfiguration -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/publicMaintenanceConfiguration1 diff --git a/internal/services/maintenance/transition.go b/internal/services/maintenance/transition.go new file mode 100644 index 000000000000..4ff4895f8431 --- /dev/null +++ b/internal/services/maintenance/transition.go @@ -0,0 +1,23 @@ +package maintenance + +import "github.com/hashicorp/terraform-provider-azurerm/utils" + +func expandTags(input map[string]interface{}) *map[string]string { + output := make(map[string]string) + for k, v := range input { + output[k] = v.(string) + } + return &output +} + +func flattenTags(input *map[string]string) map[string]*string { + output := make(map[string]*string) + + if input != nil { + for k, v := range *input { + output[k] = utils.String(v) + } + } + + return output +} diff --git a/internal/services/maintenance/validate/maintenance_configuration_id.go b/internal/services/maintenance/validate/maintenance_configuration_id.go deleted file mode 100644 index e4917c1f227b..000000000000 --- a/internal/services/maintenance/validate/maintenance_configuration_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" -) - -func MaintenanceConfigurationID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MaintenanceConfigurationID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/maintenance/validate/maintenance_configuration_id_test.go b/internal/services/maintenance/validate/maintenance_configuration_id_test.go deleted file mode 100644 index f893d393070c..000000000000 --- a/internal/services/maintenance/validate/maintenance_configuration_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMaintenanceConfigurationID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Maintenance/maintenanceConfigurations/maintenanceConfiguration1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MAINTENANCE/MAINTENANCECONFIGURATIONS/MAINTENANCECONFIGURATION1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MaintenanceConfigurationID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/maintenance/validate/public_maintenance_configuration_id.go b/internal/services/maintenance/validate/public_maintenance_configuration_id.go deleted file mode 100644 index 14898cb15959..000000000000 --- a/internal/services/maintenance/validate/public_maintenance_configuration_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" -) - -func PublicMaintenanceConfigurationID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.PublicMaintenanceConfigurationID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/maintenance/validate/public_maintenance_configuration_id_test.go b/internal/services/maintenance/validate/public_maintenance_configuration_id_test.go deleted file mode 100644 index b5df41d3362b..000000000000 --- a/internal/services/maintenance/validate/public_maintenance_configuration_id_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestPublicMaintenanceConfigurationID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/publicMaintenanceConfiguration1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/PROVIDERS/MICROSOFT.MAINTENANCE/PUBLICMAINTENANCECONFIGURATIONS/PUBLICMAINTENANCECONFIGURATION1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := PublicMaintenanceConfigurationID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/managementgroup/management_group_data_source.go b/internal/services/managementgroup/management_group_data_source.go index d69ec737f846..88b0de7ee328 100644 --- a/internal/services/managementgroup/management_group_data_source.go +++ b/internal/services/managementgroup/management_group_data_source.go @@ -44,10 +44,27 @@ func dataSourceManagementGroup() *pluginsdk.Resource { }, "subscription_ids": { - Type: pluginsdk.TypeSet, + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + }, + + "management_group_ids": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + }, + + "all_subscription_ids": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + }, + + "all_management_group_ids": { + Type: pluginsdk.TypeList, Computed: true, Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, - Set: pluginsdk.HashString, }, }, } @@ -90,11 +107,29 @@ func dataSourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{}) if props := resp.Properties; props != nil { d.Set("display_name", props.DisplayName) - subscriptionIds, err := flattenManagementGroupDataSourceSubscriptionIds(props.Children) - if err != nil { - return fmt.Errorf("flattening `subscription_ids`: %+v", err) + subscriptionIds := []interface{}{} + mgmtgroupIds := []interface{}{} + if err := flattenManagementGroupDataSourceChildren(&subscriptionIds, &mgmtgroupIds, props.Children, false); err != nil { + return fmt.Errorf("flattening direct children resources: %+v", err) + } + if err := d.Set("subscription_ids", subscriptionIds); err != nil { + return fmt.Errorf("setting `subscription_ids`: %v", err) + } + if err := d.Set("management_group_ids", mgmtgroupIds); err != nil { + return fmt.Errorf("setting `management_group_ids`: %v", err) + } + + subscriptionIds = []interface{}{} + mgmtgroupIds = []interface{}{} + if err := flattenManagementGroupDataSourceChildren(&subscriptionIds, &mgmtgroupIds, props.Children, true); err != nil { + return fmt.Errorf("flattening all children resources: %+v", err) + } + if err := d.Set("all_subscription_ids", subscriptionIds); err != nil { + return fmt.Errorf("setting `all_subscription_ids`: %v", err) + } + if err := d.Set("all_management_group_ids", mgmtgroupIds); err != nil { + return fmt.Errorf("setting `all_management_group_ids`: %v", err) } - d.Set("subscription_ids", subscriptionIds) parentId := "" if details := props.Details; details != nil { @@ -141,26 +176,37 @@ func getManagementGroupNameByDisplayName(ctx context.Context, client *management return results[0], nil } -func flattenManagementGroupDataSourceSubscriptionIds(input *[]managementgroups.ChildInfo) (*pluginsdk.Set, error) { - subscriptionIds := &pluginsdk.Set{F: pluginsdk.HashString} +func flattenManagementGroupDataSourceChildren(subscriptionIds, mgmtgroupIds *[]interface{}, input *[]managementgroups.ChildInfo, recursive bool) error { if input == nil { - return subscriptionIds, nil + return nil } for _, child := range *input { if child.ID == nil { continue } - - id, err := parseManagementGroupSubscriptionID(*child.ID) - if err != nil { - return nil, fmt.Errorf("Unable to parse child Subscription ID %+v", err) + switch child.Type { + case managementgroups.Type1MicrosoftManagementmanagementGroups: + id, err := parse.ManagementGroupID(*child.ID) + if err != nil { + return fmt.Errorf("Unable to parse child Management Group ID %+v", err) + } + *mgmtgroupIds = append(*mgmtgroupIds, id.ID()) + case managementgroups.Type1Subscriptions: + id, err := parseManagementGroupSubscriptionID(*child.ID) + if err != nil { + return fmt.Errorf("Unable to parse child Subscription ID %+v", err) + } + *subscriptionIds = append(*subscriptionIds, id.subscriptionId) + default: + continue } - - if id != nil { - subscriptionIds.Add(id.subscriptionId) + if recursive { + if err := flattenManagementGroupDataSourceChildren(subscriptionIds, mgmtgroupIds, child.Children, recursive); err != nil { + return err + } } } - return subscriptionIds, nil + return nil } diff --git a/internal/services/managementgroup/management_group_data_source_test.go b/internal/services/managementgroup/management_group_data_source_test.go index 5a2ffd3b5e5c..7d041eb88e39 100644 --- a/internal/services/managementgroup/management_group_data_source_test.go +++ b/internal/services/managementgroup/management_group_data_source_test.go @@ -40,6 +40,22 @@ func TestAccManagementGroupDataSource_basicByDisplayName(t *testing.T) { }) } +func TestAccManagementGroupDataSource_nestedManagmentGroup(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_management_group", "test") + r := ManagementGroupDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.nestedManagementGroup(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("display_name").HasValue(fmt.Sprintf("acctest Management Group %d", data.RandomInteger)), + check.That(data.ResourceName).Key("management_group_ids.#").HasValue("1"), + check.That(data.ResourceName).Key("all_management_group_ids.#").HasValue("2"), + ), + }, + }) +} + func (ManagementGroupDataSource) basicByName(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -71,3 +87,30 @@ data "azurerm_management_group" "test" { } `, data.RandomInteger) } + +func (ManagementGroupDataSource) nestedManagementGroup(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_management_group" "test" { + display_name = "acctest Management Group %[1]d" +} + +resource "azurerm_management_group" "child" { + display_name = "acctest child Management Group %[1]d" + parent_management_group_id = azurerm_management_group.test.id +} + +resource "azurerm_management_group" "grand_child" { + display_name = "acctest grand child Management Group %[1]d" + parent_management_group_id = azurerm_management_group.child.id +} + +data "azurerm_management_group" "test" { + name = azurerm_management_group.test.name + depends_on = [azurerm_management_group.grand_child] +} +`, data.RandomInteger) +} diff --git a/internal/services/mariadb/client/client.go b/internal/services/mariadb/client/client.go index 0e1cb2344c0d..deba55b33098 100644 --- a/internal/services/mariadb/client/client.go +++ b/internal/services/mariadb/client/client.go @@ -1,32 +1,36 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/configurations" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/databases" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/firewallrules" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/virtualnetworkrules" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - ConfigurationsClient *mariadb.ConfigurationsClient - DatabasesClient *mariadb.DatabasesClient - FirewallRulesClient *mariadb.FirewallRulesClient - ServersClient *mariadb.ServersClient - VirtualNetworkRulesClient *mariadb.VirtualNetworkRulesClient + ConfigurationsClient *configurations.ConfigurationsClient + DatabasesClient *databases.DatabasesClient + FirewallRulesClient *firewallrules.FirewallRulesClient + ServersClient *servers.ServersClient + VirtualNetworkRulesClient *virtualnetworkrules.VirtualNetworkRulesClient } func NewClient(o *common.ClientOptions) *Client { - configurationsClient := mariadb.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + configurationsClient := configurations.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&configurationsClient.Client, o.ResourceManagerAuthorizer) - DatabasesClient := mariadb.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + DatabasesClient := databases.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&DatabasesClient.Client, o.ResourceManagerAuthorizer) - FirewallRulesClient := mariadb.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + FirewallRulesClient := firewallrules.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) - ServersClient := mariadb.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + ServersClient := servers.NewServersClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) - VirtualNetworkRulesClient := mariadb.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + VirtualNetworkRulesClient := virtualnetworkrules.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) return &Client{ diff --git a/internal/services/mariadb/mariadb_configuration_resource.go b/internal/services/mariadb/mariadb_configuration_resource.go index abedc306decc..f16ad1325046 100644 --- a/internal/services/mariadb/mariadb_configuration_resource.go +++ b/internal/services/mariadb/mariadb_configuration_resource.go @@ -5,10 +5,10 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/configurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -22,7 +22,7 @@ func resourceMariaDbConfiguration() *pluginsdk.Resource { Read: resourceMariaDbConfigurationRead, Delete: resourceMariaDbConfigurationDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MariaDBConfigurationID(id) + _, err := configurations.ParseConfigurationID(id) return err }), @@ -65,23 +65,17 @@ func resourceMariaDbConfigurationCreateUpdate(d *pluginsdk.ResourceData, meta in ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for AzureRM MariaDb Configuration creation.") - id := parse.NewMariaDBConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := configurations.NewConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) value := d.Get("value").(string) - properties := mariadb.Configuration{ - ConfigurationProperties: &mariadb.ConfigurationProperties{ + properties := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ Value: utils.String(value), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName, properties) - if err != nil { - return fmt.Errorf("issuing create/update request for %s: %v", id, err) - } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %v", id, err) + if err := client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) } d.SetId(id.ID()) @@ -94,26 +88,35 @@ func resourceMariaDbConfigurationRead(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found", *id) d.SetId("") return nil } - return fmt.Errorf("making Read request on %s: %+v", *id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.ConfigurationName) d.Set("server_name", id.ServerName) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("value", resp.ConfigurationProperties.Value) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + value := "" + if v := props.Value; v != nil { + value = *v + } + d.Set("value", value) + } + } return nil } @@ -123,28 +126,37 @@ func resourceMariaDbConfigurationDelete(d *pluginsdk.ResourceData, meta interfac ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } // "delete" = resetting this to the default value - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName) + resp, err := client.Get(ctx, *id) if err != nil { return fmt.Errorf("retrieving %s: %+v", *id, err) } - properties := mariadb.Configuration{ - ConfigurationProperties: &mariadb.ConfigurationProperties{ + defaultValue := "" + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if v := props.DefaultValue; v != nil { + defaultValue = *v + } + } + } + + properties := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ // we can alternatively set `source: "system-default"` - Value: resp.DefaultValue, + Value: &defaultValue, }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName, properties) - if err != nil { - return err + if err := client.CreateOrUpdateThenPoll(ctx, *id, properties); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) } - return future.WaitForCompletionRef(ctx, client.Client) + return nil } diff --git a/internal/services/mariadb/mariadb_configuration_resource_test.go b/internal/services/mariadb/mariadb_configuration_resource_test.go index b6ec842f3a80..70af8fccc732 100644 --- a/internal/services/mariadb/mariadb_configuration_resource_test.go +++ b/internal/services/mariadb/mariadb_configuration_resource_test.go @@ -5,9 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/configurations" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -84,39 +85,41 @@ func TestAccMariaDbConfiguration_logSlowAdminStatements(t *testing.T) { } func (MariaDbConfigurationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MariaDBConfigurationID(state.ID) + id, err := configurations.ParseConfigurationID(state.ID) if err != nil { return nil, err } - resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName) + resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.ConfigurationProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func checkValueIs(value string) acceptance.ClientCheckFunc { return func(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) error { - id, err := parse.MariaDBConfigurationID(state.ID) + id, err := configurations.ParseConfigurationID(state.ID) if err != nil { return err } - resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.ServerName, id.ConfigurationName) + resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, *id) if err != nil { return fmt.Errorf("retrieving %s: %v", *id, err) } - if resp.Value == nil { - return fmt.Errorf("%s Value is nil", *id) - } - - actualValue := *resp.Value - - if value != actualValue { - return fmt.Errorf("%s Value (%s) != expected (%s)", *id, actualValue, value) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if v := props.Value; v != nil { + if value != *v { + return fmt.Errorf("%s Value (%s) != expected (%s)", *id, *v, value) + } + } else { + return fmt.Errorf("%s Value is nil", *id) + } + } } return nil @@ -125,28 +128,41 @@ func checkValueIs(value string) acceptance.ClientCheckFunc { func checkValueIsReset(configurationName string) acceptance.ClientCheckFunc { return func(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) error { - id, err := parse.ServerID(state.ID) + serverId, err := servers.ParseServerID(state.ID) if err != nil { return err } - resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.Name, configurationName) + id := configurations.NewConfigurationID(serverId.SubscriptionId, serverId.ResourceGroupName, serverId.ServerName, configurationName) + + resp, err := clients.MariaDB.ConfigurationsClient.Get(ctx, id) if err != nil { - return fmt.Errorf("retrieving MariaDB Configuration %q (Server %q / Resource Group %q): %v", configurationName, id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %v", id, err) + } + + actualValue := "" + defaultValue := "" + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if v := props.Value; v != nil { + actualValue = *v + } + if v := props.DefaultValue; v != nil { + defaultValue = *v + } + } } - if resp.Value == nil { - return fmt.Errorf("MariaDB Configuration %q (Server %q / Resource Group %q) Value is nil", configurationName, id.Name, id.ResourceGroup) + if actualValue == "" { + return fmt.Errorf("%s Value is nil", id) } - if resp.DefaultValue == nil { - return fmt.Errorf("MariaDB Configuration %q (Server %q / Resource Group %q) Default Value is nil", configurationName, id.Name, id.ResourceGroup) + if defaultValue == "" { + return fmt.Errorf("%s Default Value is nil", id) } - actualValue := *resp.Value - defaultValue := *resp.DefaultValue if defaultValue != actualValue { - return fmt.Errorf("MariaDB Configuration %q (Server %q / Resource Group %q) Value (%s) != Default (%s)", configurationName, id.Name, id.ResourceGroup, actualValue, defaultValue) + return fmt.Errorf("%s Value (%s) != Default (%s)", id, actualValue, defaultValue) } return nil diff --git a/internal/services/mariadb/mariadb_database_resource.go b/internal/services/mariadb/mariadb_database_resource.go index 4c3226a63ecf..391a05b861df 100644 --- a/internal/services/mariadb/mariadb_database_resource.go +++ b/internal/services/mariadb/mariadb_database_resource.go @@ -6,11 +6,11 @@ import ( "regexp" "time" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/databases" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -25,7 +25,7 @@ func resourceMariaDbDatabase() *pluginsdk.Resource { Delete: resourceMariaDbDatabaseDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MariaDBDatabaseID(id) + _, err := databases.ParseDatabaseID(id) return err }), @@ -87,17 +87,17 @@ func resourceMariaDbDatabaseCreateUpdate(d *pluginsdk.ResourceData, meta interfa log.Printf("[INFO] preparing arguments for AzureRM MariaDB database creation") - id := parse.NewMariaDBDatabaseID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := databases.NewDatabaseID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.DatabaseName) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %s", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_mariadb_database", id.ID()) } } @@ -105,22 +105,17 @@ func resourceMariaDbDatabaseCreateUpdate(d *pluginsdk.ResourceData, meta interfa charset := d.Get("charset").(string) collation := d.Get("collation").(string) - properties := mariadb.Database{ - DatabaseProperties: &mariadb.DatabaseProperties{ + properties := databases.Database{ + Properties: &databases.DatabaseProperties{ Charset: utils.String(charset), Collation: utils.String(collation), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.DatabaseName, properties) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for completion of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourceMariaDbDatabaseRead(d, meta) @@ -131,31 +126,43 @@ func resourceMariaDbDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBDatabaseID(d.Id()) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { - return fmt.Errorf("cannot parse MariaDB database %q ID:\n%+v", d.Id(), err) + return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.DatabaseName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found", *id) d.SetId("") return nil } - return fmt.Errorf("making read request on %s:\n%+v", *id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.DatabaseName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - if properties := resp.DatabaseProperties; properties != nil { - d.Set("charset", properties.Charset) - d.Set("collation", properties.Collation) + charset := "" + collation := "" + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if v := props.Charset; v != nil { + charset = *v + } + if v := props.Collation; v != nil { + collation = *v + } + } } + d.Set("charset", charset) + d.Set("collation", collation) + return nil } @@ -164,18 +171,13 @@ func resourceMariaDbDatabaseDelete(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBDatabaseID(d.Id()) - if err != nil { - return fmt.Errorf("cannot parse MariaDB database %q ID:\n%+v", d.Id(), err) - } - - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.DatabaseName) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { - return fmt.Errorf("making delete request on %s:\n%+v", *id, err) + return err } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s:\n%+v", *id, err) + if err := client.DeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil diff --git a/internal/services/mariadb/mariadb_database_resource_test.go b/internal/services/mariadb/mariadb_database_resource_test.go index 1c67f2b7fbed..6a124f3a00a6 100644 --- a/internal/services/mariadb/mariadb_database_resource_test.go +++ b/internal/services/mariadb/mariadb_database_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/databases" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -51,17 +51,17 @@ func TestAccMariaDbDatabase_requiresImport(t *testing.T) { } func (MariaDbDatabaseResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MariaDBDatabaseID(state.ID) + id, err := databases.ParseDatabaseID(state.ID) if err != nil { return nil, err } - resp, err := clients.MariaDB.DatabasesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.DatabaseName) + resp, err := clients.MariaDB.DatabasesClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.DatabaseProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (MariaDbDatabaseResource) basic(data acceptance.TestData) string { diff --git a/internal/services/mariadb/mariadb_firewall_rule_resource.go b/internal/services/mariadb/mariadb_firewall_rule_resource.go index 50a217f36203..14a9a8fc64bb 100644 --- a/internal/services/mariadb/mariadb_firewall_rule_resource.go +++ b/internal/services/mariadb/mariadb_firewall_rule_resource.go @@ -5,16 +5,15 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmMariaDBFirewallRule() *pluginsdk.Resource { @@ -24,7 +23,7 @@ func resourceArmMariaDBFirewallRule() *pluginsdk.Resource { Update: resourceArmMariaDBFirewallRuleCreateUpdate, Delete: resourceArmMariaDBFirewallRuleDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MariaDBFirewallRuleID(id) + _, err := firewallrules.ParseFirewallRuleID(id) return err }), @@ -75,37 +74,32 @@ func resourceArmMariaDBFirewallRuleCreateUpdate(d *pluginsdk.ResourceData, meta log.Printf("[INFO] preparing arguments for AzureRM MariaDB Firewall Rule creation.") - id := parse.NewMariaDBFirewallRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := firewallrules.NewFirewallRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) startIPAddress := d.Get("start_ip_address").(string) endIPAddress := d.Get("end_ip_address").(string) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.FirewallRuleName) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_mariadb_firewall_rule", id.ID()) } } - properties := mariadb.FirewallRule{ - FirewallRuleProperties: &mariadb.FirewallRuleProperties{ - StartIPAddress: utils.String(startIPAddress), - EndIPAddress: utils.String(endIPAddress), + properties := firewallrules.FirewallRule{ + Properties: firewallrules.FirewallRuleProperties{ + StartIPAddress: startIPAddress, + EndIPAddress: endIPAddress, }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.FirewallRuleName, properties) - if err != nil { - return fmt.Errorf("issuing create/update request for %s: %v", id, err) - } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting on create/update future for %s: %v", id, err) + if err := client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { + return fmt.Errorf("creating/updated %s: %v", id, err) } d.SetId(id.ID()) @@ -118,25 +112,28 @@ func resourceArmMariaDBFirewallRuleRead(d *pluginsdk.ResourceData, meta interfac ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBFirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.FirewallRuleName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { d.SetId("") return nil } - return fmt.Errorf("making Read request on %s: %+v", *id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.FirewallRuleName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - d.Set("start_ip_address", resp.StartIPAddress) - d.Set("end_ip_address", resp.EndIPAddress) + + if model := resp.Model; model != nil { + d.Set("start_ip_address", model.Properties.StartIPAddress) + d.Set("end_ip_address", model.Properties.EndIPAddress) + } return nil } @@ -146,15 +143,14 @@ func resourceArmMariaDBFirewallRuleDelete(d *pluginsdk.ResourceData, meta interf ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBFirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.FirewallRuleName) - if err != nil { - return err + if err := client.DeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) } - return future.WaitForCompletionRef(ctx, client.Client) + return nil } diff --git a/internal/services/mariadb/mariadb_firewall_rule_resource_test.go b/internal/services/mariadb/mariadb_firewall_rule_resource_test.go index b0e1b50c8c61..0dcfa0c465dd 100644 --- a/internal/services/mariadb/mariadb_firewall_rule_resource_test.go +++ b/internal/services/mariadb/mariadb_firewall_rule_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -49,17 +49,17 @@ func TestAccMariaDbFirewallRule_requiresImport(t *testing.T) { } func (MariaDbFirewallRuleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MariaDBFirewallRuleID(state.ID) + id, err := firewallrules.ParseFirewallRuleID(state.ID) if err != nil { return nil, err } - resp, err := clients.MariaDB.FirewallRulesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.FirewallRuleName) + resp, err := clients.MariaDB.FirewallRulesClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.FirewallRuleProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (MariaDbFirewallRuleResource) basic(data acceptance.TestData) string { diff --git a/internal/services/mariadb/mariadb_server_data_source.go b/internal/services/mariadb/mariadb_server_data_source.go index 287aef2f68e1..180c47d9f7c3 100644 --- a/internal/services/mariadb/mariadb_server_data_source.go +++ b/internal/services/mariadb/mariadb_server_data_source.go @@ -5,15 +5,15 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourceMariaDbServer() *pluginsdk.Resource { @@ -91,7 +91,7 @@ func dataSourceMariaDbServer() *pluginsdk.Resource { Computed: true, }, - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } @@ -102,10 +102,10 @@ func dataSourceMariaDbServerRead(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + id := servers.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } @@ -113,18 +113,45 @@ func dataSourceMariaDbServerRead(d *pluginsdk.ResourceData, meta interface{}) er } d.SetId(id.ID()) - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) - if sku := resp.Sku; sku != nil { - d.Set("sku_name", sku.Name) - } + d.Set("name", id.ServerName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + d.Set("location", location.Normalize(model.Location)) + + if sku := model.Sku; sku != nil { + d.Set("sku_name", sku.Name) + } - if props := resp.ServerProperties; props != nil { - d.Set("administrator_login", props.AdministratorLogin) - d.Set("fqdn", props.FullyQualifiedDomainName) - d.Set("ssl_enforcement", string(props.SslEnforcement)) - d.Set("version", string(props.Version)) + if props := model.Properties; props != nil { + adminLogin := "" + if v := props.AdministratorLogin; v != nil { + adminLogin = *v + } + + fqdn := "" + if v := props.FullyQualifiedDomainName; v != nil { + fqdn = *v + } + + sslEnforcement := "" + if v := props.SslEnforcement; v != nil { + sslEnforcement = string(*v) + } + + version := "" + if v := props.Version; v != nil { + version = string(*v) + } + + d.Set("administrator_login", adminLogin) + d.Set("fqdn", fqdn) + d.Set("ssl_enforcement", sslEnforcement) + d.Set("version", version) + } + + return tags.FlattenAndSet(d, model.Tags) } - return tags.FlattenAndSet(d, resp.Tags) + + return nil } diff --git a/internal/services/mariadb/mariadb_server_resource.go b/internal/services/mariadb/mariadb_server_resource.go index 3f31eee1774e..424e5f58cf7b 100644 --- a/internal/services/mariadb/mariadb_server_resource.go +++ b/internal/services/mariadb/mariadb_server_resource.go @@ -8,14 +8,14 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" - "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -30,7 +30,7 @@ func resourceMariaDbServer() *pluginsdk.Resource { Delete: resourceMariaDbServerDelete, Importer: pluginsdk.ImporterValidatingResourceIdThen(func(id string) error { - _, err := parse.ServerID(id) + _, err := servers.ParseServerID(id) return err }, func(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) ([]*pluginsdk.ResourceData, error) { d.Set("create_mode", "Default") @@ -42,7 +42,7 @@ func resourceMariaDbServer() *pluginsdk.Resource { }), Timeouts: &pluginsdk.ResourceTimeout{ - Create: pluginsdk.DefaultTimeout(60 * time.Minute), + Create: pluginsdk.DefaultTimeout(90 * time.Minute), Read: pluginsdk.DefaultTimeout(5 * time.Minute), Update: pluginsdk.DefaultTimeout(60 * time.Minute), Delete: pluginsdk.DefaultTimeout(60 * time.Minute), @@ -85,19 +85,19 @@ func resourceMariaDbServer() *pluginsdk.Resource { "create_mode": { Type: pluginsdk.TypeString, Optional: true, - Default: string(mariadb.CreateModeDefault), + Default: string(servers.CreateModeDefault), ValidateFunc: validation.StringInSlice([]string{ - string(mariadb.CreateModeDefault), - string(mariadb.CreateModeGeoRestore), - string(mariadb.CreateModePointInTimeRestore), - string(mariadb.CreateModeReplica), + string(servers.CreateModeDefault), + string(servers.CreateModeGeoRestore), + string(servers.CreateModePointInTimeRestore), + string(servers.CreateModeReplica), }, false), }, "creation_source_server_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: validate.ServerID, + ValidateFunc: servers.ValidateServerID, }, "fqdn": { @@ -160,7 +160,7 @@ func resourceMariaDbServer() *pluginsdk.Resource { ), }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), "version": { Type: pluginsdk.TypeString, @@ -181,45 +181,45 @@ func resourceMariaDbServerCreate(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + id := servers.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_mariadb_server", id.ID()) } } location := azure.NormalizeLocation(d.Get("location").(string)) - mode := mariadb.CreateMode(d.Get("create_mode").(string)) + mode := servers.CreateMode(d.Get("create_mode").(string)) source := d.Get("creation_source_server_id").(string) - version := mariadb.ServerVersion(d.Get("version").(string)) + version := servers.ServerVersion(d.Get("version").(string)) sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { return fmt.Errorf("expanding `sku_name`: %+v", err) } - publicAccess := mariadb.PublicNetworkAccessEnumEnabled + publicAccess := servers.PublicNetworkAccessEnumEnabled if v := d.Get("public_network_access_enabled"); !v.(bool) { - publicAccess = mariadb.PublicNetworkAccessEnumDisabled + publicAccess = servers.PublicNetworkAccessEnumDisabled } - ssl := mariadb.SslEnforcementEnumEnabled + ssl := servers.SslEnforcementEnumEnabled if v := d.Get("ssl_enforcement_enabled").(bool); !v { - ssl = mariadb.SslEnforcementEnumDisabled + ssl = servers.SslEnforcementEnumDisabled } storage := expandMariaDbStorageProfile(d) - var props mariadb.BasicServerPropertiesForCreate + var props servers.ServerPropertiesForCreate switch mode { - case mariadb.CreateModeDefault: + case servers.CreateModeDefault: admin := d.Get("administrator_login").(string) pass := d.Get("administrator_login_password").(string) @@ -234,68 +234,57 @@ func resourceMariaDbServerCreate(d *pluginsdk.ResourceData, meta interface{}) er return fmt.Errorf("`restore_point_in_time` cannot be set when `create_mode` is `default`") } - props = &mariadb.ServerPropertiesForDefaultCreate{ - AdministratorLogin: &admin, - AdministratorLoginPassword: &pass, - CreateMode: mode, - PublicNetworkAccess: publicAccess, - SslEnforcement: ssl, + props = servers.ServerPropertiesForDefaultCreate{ + AdministratorLogin: admin, + AdministratorLoginPassword: pass, + PublicNetworkAccess: &publicAccess, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, + Version: &version, } - case mariadb.CreateModePointInTimeRestore: + + case servers.CreateModePointInTimeRestore: v, ok := d.GetOk("restore_point_in_time") if !ok || v.(string) == "" { return fmt.Errorf("restore_point_in_time must be set when create_mode is PointInTimeRestore") } - time, _ := time.Parse(time.RFC3339, v.(string)) // should be validated by the schema - props = &mariadb.ServerPropertiesForRestore{ - CreateMode: mode, - SourceServerID: &source, - RestorePointInTime: &date.Time{ - Time: time, - }, - PublicNetworkAccess: publicAccess, - SslEnforcement: ssl, + props = &servers.ServerPropertiesForRestore{ + SourceServerId: source, + RestorePointInTime: v.(string), + PublicNetworkAccess: &publicAccess, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, + Version: &version, } - case mariadb.CreateModeGeoRestore: - props = &mariadb.ServerPropertiesForGeoRestore{ - CreateMode: mode, - SourceServerID: &source, - PublicNetworkAccess: publicAccess, - SslEnforcement: ssl, + case servers.CreateModeGeoRestore: + props = &servers.ServerPropertiesForGeoRestore{ + SourceServerId: source, + PublicNetworkAccess: &publicAccess, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, + Version: &version, } - case mariadb.CreateModeReplica: - props = &mariadb.ServerPropertiesForReplica{ - CreateMode: mode, - SourceServerID: &source, - PublicNetworkAccess: publicAccess, - SslEnforcement: ssl, - Version: version, + case servers.CreateModeReplica: + props = &servers.ServerPropertiesForReplica{ + SourceServerId: source, + PublicNetworkAccess: &publicAccess, + SslEnforcement: &ssl, + Version: &version, } } - server := mariadb.ServerForCreate{ - Location: &location, + server := servers.ServerForCreate{ + Location: location, Properties: props, Sku: sku, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.Create(ctx, id.ResourceGroup, id.Name, server) - if err != nil { + if err := client.CreateThenPoll(ctx, id, server); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the creation of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourceMariaDbServerRead(d, meta) } @@ -307,7 +296,7 @@ func resourceMariaDbServerUpdate(d *pluginsdk.ResourceData, meta interface{}) er log.Printf("[INFO] preparing arguments for AzureRM MariaDB Server update.") - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return err } @@ -317,39 +306,34 @@ func resourceMariaDbServerUpdate(d *pluginsdk.ResourceData, meta interface{}) er return fmt.Errorf("expanding `sku_name`: %+v", err) } - publicAccess := mariadb.PublicNetworkAccessEnumEnabled + publicAccess := servers.PublicNetworkAccessEnumEnabled if v := d.Get("public_network_access_enabled").(bool); !v { - publicAccess = mariadb.PublicNetworkAccessEnumDisabled + publicAccess = servers.PublicNetworkAccessEnumDisabled } - ssl := mariadb.SslEnforcementEnumEnabled + ssl := servers.SslEnforcementEnumEnabled if v := d.Get("ssl_enforcement_enabled").(bool); !v { - ssl = mariadb.SslEnforcementEnumDisabled + ssl = servers.SslEnforcementEnumDisabled } storageProfile := expandMariaDbStorageProfile(d) - - properties := mariadb.ServerUpdateParameters{ - ServerUpdateParametersProperties: &mariadb.ServerUpdateParametersProperties{ + serverVersion := servers.ServerVersion(d.Get("version").(string)) + properties := servers.ServerUpdateParameters{ + Properties: &servers.ServerUpdateParametersProperties{ AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)), - PublicNetworkAccess: publicAccess, - SslEnforcement: ssl, + PublicNetworkAccess: &publicAccess, + SslEnforcement: &ssl, StorageProfile: storageProfile, - Version: mariadb.ServerVersion(d.Get("version").(string)), + Version: &serverVersion, }, Sku: sku, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.Update(ctx, id.ResourceGroup, id.Name, properties) - if err != nil { + if err := client.UpdateThenPoll(ctx, *id, properties); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of %s: %+v", *id, err) - } - return resourceMariaDbServerRead(d, meta) } @@ -358,14 +342,14 @@ func resourceMariaDbServerRead(d *pluginsdk.ResourceData, meta interface{}) erro ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -374,35 +358,60 @@ func resourceMariaDbServerRead(d *pluginsdk.ResourceData, meta interface{}) erro return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("name", id.ServerName) + d.Set("resource_group_name", id.ResourceGroupName) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } + if model := resp.Model; model != nil { + d.Set("location", azure.NormalizeLocation(model.Location)) - if sku := resp.Sku; sku != nil { - d.Set("sku_name", sku.Name) - } - - if props := resp.ServerProperties; props != nil { - d.Set("administrator_login", props.AdministratorLogin) - d.Set("public_network_access_enabled", props.PublicNetworkAccess == mariadb.PublicNetworkAccessEnumEnabled) - d.Set("ssl_enforcement_enabled", props.SslEnforcement == mariadb.SslEnforcementEnumEnabled) - d.Set("version", string(props.Version)) - - if storage := props.StorageProfile; storage != nil { - d.Set("auto_grow_enabled", storage.StorageAutogrow == mariadb.StorageAutogrowEnabled) - d.Set("backup_retention_days", storage.BackupRetentionDays) - d.Set("geo_redundant_backup_enabled", storage.GeoRedundantBackup == mariadb.Enabled) - d.Set("storage_mb", storage.StorageMB) + if sku := model.Sku; sku != nil { + d.Set("sku_name", sku.Name) } - // Computed - d.Set("fqdn", props.FullyQualifiedDomainName) - } + if props := model.Properties; props != nil { + d.Set("administrator_login", props.AdministratorLogin) - return tags.FlattenAndSet(d, resp.Tags) + publicNetworkAccess := false + if props.PublicNetworkAccess != nil { + publicNetworkAccess = *props.PublicNetworkAccess == servers.PublicNetworkAccessEnumEnabled + } + d.Set("public_network_access_enabled", publicNetworkAccess) + + sslEnforcement := false + if props.SslEnforcement != nil { + sslEnforcement = *props.SslEnforcement == servers.SslEnforcementEnumEnabled + } + d.Set("ssl_enforcement_enabled", sslEnforcement) + + version := "" + if props.Version != nil { + version = string(*props.Version) + } + d.Set("version", version) + + if storage := props.StorageProfile; storage != nil { + autoGrow := false + if storage.StorageAutogrow != nil { + autoGrow = *storage.StorageAutogrow == servers.StorageAutogrowEnabled + } + d.Set("auto_grow_enabled", autoGrow) + + geoRedundant := false + if storage.GeoRedundantBackup != nil { + geoRedundant = *storage.GeoRedundantBackup == servers.GeoRedundantBackupEnabled + } + d.Set("geo_redundant_backup_enabled", geoRedundant) + d.Set("backup_retention_days", storage.BackupRetentionDays) + d.Set("storage_mb", storage.StorageMB) + + } + + // Computed + d.Set("fqdn", props.FullyQualifiedDomainName) + } + return tags.FlattenAndSet(d, model.Tags) + } + return nil } func resourceMariaDbServerDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -410,37 +419,32 @@ func resourceMariaDbServerDelete(d *pluginsdk.ResourceData, meta interface{}) er ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.Name) - if err != nil { + if err := client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) - } - return nil } -func expandServerSkuName(skuName string) (*mariadb.Sku, error) { +func expandServerSkuName(skuName string) (*servers.Sku, error) { parts := strings.Split(skuName, "_") if len(parts) != 3 { return nil, fmt.Errorf("sku_name (%s) has the wrong number of parts (%d) after splitting on _", skuName, len(parts)) } - var tier mariadb.SkuTier + var tier servers.SkuTier switch parts[0] { case "B": - tier = mariadb.Basic + tier = servers.SkuTierBasic case "GP": - tier = mariadb.GeneralPurpose + tier = servers.SkuTierGeneralPurpose case "MO": - tier = mariadb.MemoryOptimized + tier = servers.SkuTierMemoryOptimized default: return nil, fmt.Errorf("sku_name %s has unknown sku tier %s", skuName, parts[0]) } @@ -450,37 +454,39 @@ func expandServerSkuName(skuName string) (*mariadb.Sku, error) { return nil, fmt.Errorf("cannot convert `sku_name` %q capacity %s to int", skuName, parts[2]) } - return &mariadb.Sku{ - Name: utils.String(skuName), - Tier: tier, - Capacity: utils.Int32(int32(capacity)), + return &servers.Sku{ + Name: skuName, + Tier: &tier, + Capacity: utils.Int64(int64(capacity)), Family: utils.String(parts[1]), }, nil } -func expandMariaDbStorageProfile(d *pluginsdk.ResourceData) *mariadb.StorageProfile { - storage := mariadb.StorageProfile{} +func expandMariaDbStorageProfile(d *pluginsdk.ResourceData) *servers.StorageProfile { + storage := servers.StorageProfile{} // now override whatever we may have from the block with the top level properties if v, ok := d.GetOk("auto_grow_enabled"); ok { - storage.StorageAutogrow = mariadb.StorageAutogrowDisabled + autogrowEnabled := servers.StorageAutogrowDisabled if v.(bool) { - storage.StorageAutogrow = mariadb.StorageAutogrowEnabled + autogrowEnabled = servers.StorageAutogrowEnabled } + storage.StorageAutogrow = &autogrowEnabled } if v, ok := d.GetOk("backup_retention_days"); ok { - storage.BackupRetentionDays = utils.Int32(int32(v.(int))) + storage.BackupRetentionDays = utils.Int64(int64(v.(int))) } if v, ok := d.GetOk("geo_redundant_backup_enabled"); ok { - storage.GeoRedundantBackup = mariadb.Disabled + geoRedundantBackup := servers.GeoRedundantBackupDisabled if v.(bool) { - storage.GeoRedundantBackup = mariadb.Enabled + geoRedundantBackup = servers.GeoRedundantBackupEnabled } + storage.GeoRedundantBackup = &geoRedundantBackup } if v, ok := d.GetOk("storage_mb"); ok { - storage.StorageMB = utils.Int32(int32(v.(int))) + storage.StorageMB = utils.Int64(int64(v.(int))) } return &storage diff --git a/internal/services/mariadb/mariadb_server_resource_test.go b/internal/services/mariadb/mariadb_server_resource_test.go index 45b2bde401a7..25e2f5b3b064 100644 --- a/internal/services/mariadb/mariadb_server_resource_test.go +++ b/internal/services/mariadb/mariadb_server_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -199,17 +199,17 @@ func TestAccMariaDbServer_createPointInTimeRestore(t *testing.T) { } func (MariaDbServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ServerID(state.ID) + id, err := servers.ParseServerID(state.ID) if err != nil { return nil, err } - resp, err := clients.MariaDB.ServersClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.MariaDB.ServersClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving MariaDB Server %q (Resource Group %q): %v", id.Name, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.ServerProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (MariaDbServerResource) basic(data acceptance.TestData, version string) string { diff --git a/internal/services/mariadb/mariadb_virtual_network_rule_resource.go b/internal/services/mariadb/mariadb_virtual_network_rule_resource.go index c7af2b36673a..00347fe43794 100644 --- a/internal/services/mariadb/mariadb_virtual_network_rule_resource.go +++ b/internal/services/mariadb/mariadb_virtual_network_rule_resource.go @@ -6,12 +6,11 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/virtualnetworkrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/validate" validate2 "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -26,7 +25,7 @@ func resourceMariaDbVirtualNetworkRule() *pluginsdk.Resource { Update: resourceMariaDbVirtualNetworkRuleCreateUpdate, Delete: resourceMariaDbVirtualNetworkRuleDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.MariaDBVirtualNetworkRuleID(id) + _, err := virtualnetworkrules.ParseVirtualNetworkRuleID(id) return err }), @@ -69,44 +68,40 @@ func resourceMariaDbVirtualNetworkRuleCreateUpdate(d *pluginsdk.ResourceData, me ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewMariaDBVirtualNetworkRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := virtualnetworkrules.NewVirtualNetworkRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) subnetId := d.Get("subnet_id").(string) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_mariadb_virtual_network_rule", id.ID()) } } - parameters := mariadb.VirtualNetworkRule{ - VirtualNetworkRuleProperties: &mariadb.VirtualNetworkRuleProperties{ - VirtualNetworkSubnetID: utils.String(subnetId), + parameters := virtualnetworkrules.VirtualNetworkRule{ + Properties: &virtualnetworkrules.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetId: subnetId, IgnoreMissingVnetServiceEndpoint: utils.Bool(false), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName, parameters) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation/update of %q: %+v", id, err) - } // Wait for the provisioning state to become ready log.Printf("[DEBUG] Waiting for %s to become ready", id) stateConf := &pluginsdk.StateChangeConf{ Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, - Refresh: mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName), + Refresh: mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, id), MinTimeout: 1 * time.Minute, ContinuousTargetOccurence: 5, } @@ -130,14 +125,14 @@ func resourceMariaDbVirtualNetworkRuleRead(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBVirtualNetworkRuleID(d.Id()) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] Error reading MariaDb Virtual Network Rule %q - removing from state", d.Id()) d.SetId("") return nil @@ -147,11 +142,13 @@ func resourceMariaDbVirtualNetworkRuleRead(d *pluginsdk.ResourceData, meta inter } d.Set("name", id.VirtualNetworkRuleName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - if props := resp.VirtualNetworkRuleProperties; props != nil { - d.Set("subnet_id", props.VirtualNetworkSubnetID) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("subnet_id", props.VirtualNetworkSubnetId) + } } return nil @@ -162,44 +159,40 @@ func resourceMariaDbVirtualNetworkRuleDelete(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.MariaDBVirtualNetworkRuleID(d.Id()) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName) - if err != nil { + if err := client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - if !response.WasNotFound(future.Response()) { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } - } - return nil } -func mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *mariadb.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) pluginsdk.StateRefreshFunc { +func mariaDbVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *virtualnetworkrules.VirtualNetworkRulesClient, id virtualnetworkrules.VirtualNetworkRuleId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := client.Get(ctx, resourceGroup, serverName, name) + resp, err := client.Get(ctx, id) 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) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] Retrieving %s returned 404.", id) return nil, "ResponseNotFound", nil } - return nil, "", fmt.Errorf("polling for the state of the MariaDb Virtual Network Rule %q (MariaDb Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + return nil, "", fmt.Errorf("polling for the state of %s: %+v", id, 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 + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + log.Printf("[DEBUG] Retrieving %s returned Status %s", id, *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) + log.Printf("[DEBUG] Retrieving %s returned empty VirtualNetworkRuleProperties", id) return resp, "Unknown", nil } } diff --git a/internal/services/mariadb/mariadb_virtual_network_rule_resource_test.go b/internal/services/mariadb/mariadb_virtual_network_rule_resource_test.go index b399f8fdbb93..aa37b5bbd321 100644 --- a/internal/services/mariadb/mariadb_virtual_network_rule_resource_test.go +++ b/internal/services/mariadb/mariadb_virtual_network_rule_resource_test.go @@ -6,10 +6,10 @@ import ( "regexp" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/virtualnetworkrules" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -91,17 +91,17 @@ func TestAccMariaDbVirtualNetworkRule_multipleSubnets(t *testing.T) { } func (MariaDbVirtualNetworkRuleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.MariaDBVirtualNetworkRuleID(state.ID) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(state.ID) if err != nil { return nil, err } - resp, err := clients.MariaDB.VirtualNetworkRulesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName) + resp, err := clients.MariaDB.VirtualNetworkRulesClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %v", *id, err) } - return utils.Bool(resp.VirtualNetworkRuleProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (MariaDbVirtualNetworkRuleResource) basic(data acceptance.TestData) string { diff --git a/internal/services/mariadb/parse/maria_db_configuration.go b/internal/services/mariadb/parse/maria_db_configuration.go deleted file mode 100644 index 5f3140e28630..000000000000 --- a/internal/services/mariadb/parse/maria_db_configuration.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MariaDBConfigurationId struct { - SubscriptionId string - ResourceGroup string - ServerName string - ConfigurationName string -} - -func NewMariaDBConfigurationID(subscriptionId, resourceGroup, serverName, configurationName string) MariaDBConfigurationId { - return MariaDBConfigurationId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - ConfigurationName: configurationName, - } -} - -func (id MariaDBConfigurationId) String() string { - segments := []string{ - fmt.Sprintf("Configuration Name %q", id.ConfigurationName), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Maria D B Configuration", segmentsStr) -} - -func (id MariaDBConfigurationId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforMariaDB/servers/%s/configurations/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.ConfigurationName) -} - -// MariaDBConfigurationID parses a MariaDBConfiguration ID into an MariaDBConfigurationId struct -func MariaDBConfigurationID(input string) (*MariaDBConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MariaDBConfigurationId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.ConfigurationName, err = id.PopSegment("configurations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/mariadb/parse/maria_db_configuration_test.go b/internal/services/mariadb/parse/maria_db_configuration_test.go deleted file mode 100644 index 63e32230b1c1..000000000000 --- a/internal/services/mariadb/parse/maria_db_configuration_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MariaDBConfigurationId{} - -func TestMariaDBConfigurationIDFormatter(t *testing.T) { - actual := NewMariaDBConfigurationID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "config1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/config1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMariaDBConfigurationID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MariaDBConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Error: true, - }, - - { - // missing ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Error: true, - }, - - { - // missing value for ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/config1", - Expected: &MariaDBConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - ConfigurationName: "config1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/CONFIGURATIONS/CONFIG1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MariaDBConfigurationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.ConfigurationName != v.Expected.ConfigurationName { - t.Fatalf("Expected %q but got %q for ConfigurationName", v.Expected.ConfigurationName, actual.ConfigurationName) - } - } -} diff --git a/internal/services/mariadb/parse/maria_db_database.go b/internal/services/mariadb/parse/maria_db_database.go deleted file mode 100644 index 6dd9d3339acc..000000000000 --- a/internal/services/mariadb/parse/maria_db_database.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MariaDBDatabaseId struct { - SubscriptionId string - ResourceGroup string - ServerName string - DatabaseName string -} - -func NewMariaDBDatabaseID(subscriptionId, resourceGroup, serverName, databaseName string) MariaDBDatabaseId { - return MariaDBDatabaseId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - DatabaseName: databaseName, - } -} - -func (id MariaDBDatabaseId) String() string { - segments := []string{ - fmt.Sprintf("Database Name %q", id.DatabaseName), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Maria D B Database", segmentsStr) -} - -func (id MariaDBDatabaseId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforMariaDB/servers/%s/databases/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.DatabaseName) -} - -// MariaDBDatabaseID parses a MariaDBDatabase ID into an MariaDBDatabaseId struct -func MariaDBDatabaseID(input string) (*MariaDBDatabaseId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MariaDBDatabaseId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.DatabaseName, err = id.PopSegment("databases"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/mariadb/parse/maria_db_database_test.go b/internal/services/mariadb/parse/maria_db_database_test.go deleted file mode 100644 index c5d9b1b08c95..000000000000 --- a/internal/services/mariadb/parse/maria_db_database_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MariaDBDatabaseId{} - -func TestMariaDBDatabaseIDFormatter(t *testing.T) { - actual := NewMariaDBDatabaseID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "db1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/db1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMariaDBDatabaseID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MariaDBDatabaseId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Error: true, - }, - - { - // missing DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Error: true, - }, - - { - // missing value for DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/db1", - Expected: &MariaDBDatabaseId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - DatabaseName: "db1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/DATABASES/DB1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MariaDBDatabaseID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.DatabaseName != v.Expected.DatabaseName { - t.Fatalf("Expected %q but got %q for DatabaseName", v.Expected.DatabaseName, actual.DatabaseName) - } - } -} diff --git a/internal/services/mariadb/parse/maria_db_firewall_rule.go b/internal/services/mariadb/parse/maria_db_firewall_rule.go deleted file mode 100644 index 3153fd283942..000000000000 --- a/internal/services/mariadb/parse/maria_db_firewall_rule.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MariaDBFirewallRuleId struct { - SubscriptionId string - ResourceGroup string - ServerName string - FirewallRuleName string -} - -func NewMariaDBFirewallRuleID(subscriptionId, resourceGroup, serverName, firewallRuleName string) MariaDBFirewallRuleId { - return MariaDBFirewallRuleId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - FirewallRuleName: firewallRuleName, - } -} - -func (id MariaDBFirewallRuleId) String() string { - segments := []string{ - fmt.Sprintf("Firewall Rule Name %q", id.FirewallRuleName), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Maria D B Firewall Rule", segmentsStr) -} - -func (id MariaDBFirewallRuleId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforMariaDB/servers/%s/firewallRules/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.FirewallRuleName) -} - -// MariaDBFirewallRuleID parses a MariaDBFirewallRule ID into an MariaDBFirewallRuleId struct -func MariaDBFirewallRuleID(input string) (*MariaDBFirewallRuleId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MariaDBFirewallRuleId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.FirewallRuleName, err = id.PopSegment("firewallRules"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/mariadb/parse/maria_db_firewall_rule_test.go b/internal/services/mariadb/parse/maria_db_firewall_rule_test.go deleted file mode 100644 index 86aaca3e4ec4..000000000000 --- a/internal/services/mariadb/parse/maria_db_firewall_rule_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MariaDBFirewallRuleId{} - -func TestMariaDBFirewallRuleIDFormatter(t *testing.T) { - actual := NewMariaDBFirewallRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "firewallRule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/firewallRule1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMariaDBFirewallRuleID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MariaDBFirewallRuleId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Error: true, - }, - - { - // missing FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Error: true, - }, - - { - // missing value for FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/firewallRule1", - Expected: &MariaDBFirewallRuleId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - FirewallRuleName: "firewallRule1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/FIREWALLRULES/FIREWALLRULE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MariaDBFirewallRuleID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.FirewallRuleName != v.Expected.FirewallRuleName { - t.Fatalf("Expected %q but got %q for FirewallRuleName", v.Expected.FirewallRuleName, actual.FirewallRuleName) - } - } -} diff --git a/internal/services/mariadb/parse/maria_db_virtual_network_rule.go b/internal/services/mariadb/parse/maria_db_virtual_network_rule.go deleted file mode 100644 index c1ef6a2c7d1a..000000000000 --- a/internal/services/mariadb/parse/maria_db_virtual_network_rule.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type MariaDBVirtualNetworkRuleId struct { - SubscriptionId string - ResourceGroup string - ServerName string - VirtualNetworkRuleName string -} - -func NewMariaDBVirtualNetworkRuleID(subscriptionId, resourceGroup, serverName, virtualNetworkRuleName string) MariaDBVirtualNetworkRuleId { - return MariaDBVirtualNetworkRuleId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - VirtualNetworkRuleName: virtualNetworkRuleName, - } -} - -func (id MariaDBVirtualNetworkRuleId) String() string { - segments := []string{ - fmt.Sprintf("Virtual Network Rule Name %q", id.VirtualNetworkRuleName), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Maria D B Virtual Network Rule", segmentsStr) -} - -func (id MariaDBVirtualNetworkRuleId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforMariaDB/servers/%s/virtualNetworkRules/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.VirtualNetworkRuleName) -} - -// MariaDBVirtualNetworkRuleID parses a MariaDBVirtualNetworkRule ID into an MariaDBVirtualNetworkRuleId struct -func MariaDBVirtualNetworkRuleID(input string) (*MariaDBVirtualNetworkRuleId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := MariaDBVirtualNetworkRuleId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.VirtualNetworkRuleName, err = id.PopSegment("virtualNetworkRules"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/mariadb/parse/maria_db_virtual_network_rule_test.go b/internal/services/mariadb/parse/maria_db_virtual_network_rule_test.go deleted file mode 100644 index 245d578ecd4d..000000000000 --- a/internal/services/mariadb/parse/maria_db_virtual_network_rule_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = MariaDBVirtualNetworkRuleId{} - -func TestMariaDBVirtualNetworkRuleIDFormatter(t *testing.T) { - actual := NewMariaDBVirtualNetworkRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "vnetrule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/vnetrule1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestMariaDBVirtualNetworkRuleID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *MariaDBVirtualNetworkRuleId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Error: true, - }, - - { - // missing VirtualNetworkRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Error: true, - }, - - { - // missing value for VirtualNetworkRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/vnetrule1", - Expected: &MariaDBVirtualNetworkRuleId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - VirtualNetworkRuleName: "vnetrule1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/VIRTUALNETWORKRULES/VNETRULE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := MariaDBVirtualNetworkRuleID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.VirtualNetworkRuleName != v.Expected.VirtualNetworkRuleName { - t.Fatalf("Expected %q but got %q for VirtualNetworkRuleName", v.Expected.VirtualNetworkRuleName, actual.VirtualNetworkRuleName) - } - } -} diff --git a/internal/services/mariadb/parse/server.go b/internal/services/mariadb/parse/server.go deleted file mode 100644 index 20bc1f33fa3d..000000000000 --- a/internal/services/mariadb/parse/server.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ServerId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewServerID(subscriptionId, resourceGroup, name string) ServerId { - return ServerId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id ServerId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Server", segmentsStr) -} - -func (id ServerId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforMariaDB/servers/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// ServerID parses a Server ID into an ServerId struct -func ServerID(input string) (*ServerId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ServerId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("servers"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/mariadb/parse/server_test.go b/internal/services/mariadb/parse/server_test.go deleted file mode 100644 index 5a8802e54766..000000000000 --- a/internal/services/mariadb/parse/server_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ServerId{} - -func TestServerIDFormatter(t *testing.T) { - actual := NewServerID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestServerID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ServerId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1", - Expected: &ServerId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "server1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ServerID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/mariadb/resourceids.go b/internal/services/mariadb/resourceids.go deleted file mode 100644 index da078e4831b7..000000000000 --- a/internal/services/mariadb/resourceids.go +++ /dev/null @@ -1,7 +0,0 @@ -package mariadb - -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Server -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MariaDBFirewallRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/firewallRule1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MariaDBVirtualNetworkRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/vnetrule1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MariaDBConfiguration -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/config1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MariaDBDatabase -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/db1 diff --git a/internal/services/mariadb/validate/maria_db_configuration_id.go b/internal/services/mariadb/validate/maria_db_configuration_id.go deleted file mode 100644 index cb4f33723c41..000000000000 --- a/internal/services/mariadb/validate/maria_db_configuration_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" -) - -func MariaDBConfigurationID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MariaDBConfigurationID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/mariadb/validate/maria_db_configuration_id_test.go b/internal/services/mariadb/validate/maria_db_configuration_id_test.go deleted file mode 100644 index c92c8b16023d..000000000000 --- a/internal/services/mariadb/validate/maria_db_configuration_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMariaDBConfigurationID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Valid: false, - }, - - { - // missing ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Valid: false, - }, - - { - // missing value for ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/configurations/config1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/CONFIGURATIONS/CONFIG1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MariaDBConfigurationID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/mariadb/validate/maria_db_database_id.go b/internal/services/mariadb/validate/maria_db_database_id.go deleted file mode 100644 index 72ace30d7eab..000000000000 --- a/internal/services/mariadb/validate/maria_db_database_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" -) - -func MariaDBDatabaseID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MariaDBDatabaseID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/mariadb/validate/maria_db_database_id_test.go b/internal/services/mariadb/validate/maria_db_database_id_test.go deleted file mode 100644 index 860f79311e1b..000000000000 --- a/internal/services/mariadb/validate/maria_db_database_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMariaDBDatabaseID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Valid: false, - }, - - { - // missing DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Valid: false, - }, - - { - // missing value for DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/databases/db1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/DATABASES/DB1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MariaDBDatabaseID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/mariadb/validate/maria_db_firewall_rule_id.go b/internal/services/mariadb/validate/maria_db_firewall_rule_id.go deleted file mode 100644 index 469e95bf6ac9..000000000000 --- a/internal/services/mariadb/validate/maria_db_firewall_rule_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" -) - -func MariaDBFirewallRuleID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MariaDBFirewallRuleID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/mariadb/validate/maria_db_firewall_rule_id_test.go b/internal/services/mariadb/validate/maria_db_firewall_rule_id_test.go deleted file mode 100644 index 72cf474ee02f..000000000000 --- a/internal/services/mariadb/validate/maria_db_firewall_rule_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMariaDBFirewallRuleID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Valid: false, - }, - - { - // missing FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Valid: false, - }, - - { - // missing value for FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/firewallRules/firewallRule1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/FIREWALLRULES/FIREWALLRULE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MariaDBFirewallRuleID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/mariadb/validate/maria_db_virtual_network_rule_id.go b/internal/services/mariadb/validate/maria_db_virtual_network_rule_id.go deleted file mode 100644 index 8da930b85a65..000000000000 --- a/internal/services/mariadb/validate/maria_db_virtual_network_rule_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" -) - -func MariaDBVirtualNetworkRuleID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.MariaDBVirtualNetworkRuleID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/mariadb/validate/maria_db_virtual_network_rule_id_test.go b/internal/services/mariadb/validate/maria_db_virtual_network_rule_id_test.go deleted file mode 100644 index 99e3013e5da4..000000000000 --- a/internal/services/mariadb/validate/maria_db_virtual_network_rule_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestMariaDBVirtualNetworkRuleID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Valid: false, - }, - - { - // missing VirtualNetworkRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/", - Valid: false, - }, - - { - // missing value for VirtualNetworkRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1/virtualNetworkRules/vnetrule1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1/VIRTUALNETWORKRULES/VNETRULE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := MariaDBVirtualNetworkRuleID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/mariadb/validate/server_id.go b/internal/services/mariadb/validate/server_id.go deleted file mode 100644 index e3c030471093..000000000000 --- a/internal/services/mariadb/validate/server_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" -) - -func ServerID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ServerID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/mariadb/validate/server_id_test.go b/internal/services/mariadb/validate/server_id_test.go deleted file mode 100644 index 2f1d7e91db3e..000000000000 --- a/internal/services/mariadb/validate/server_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestServerID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforMariaDB/servers/server1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORMARIADB/SERVERS/SERVER1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ServerID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/monitor/client/client.go b/internal/services/monitor/client/client.go index 0d4f28d31723..58aceebf24d1 100644 --- a/internal/services/monitor/client/client.go +++ b/internal/services/monitor/client/client.go @@ -6,6 +6,11 @@ import ( "github.com/Azure/azure-sdk-for-go/services/preview/alertsmanagement/mgmt/2019-06-01-preview/alertsmanagement" classic "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-07-01-preview/insights" newActionGroupClient "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-09-01-preview/insights" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionendpoints" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionrules" + diagnosticSettingClient "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-05-01-preview/diagnosticsettings" + diagnosticCategoryClient "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-05-01-preview/diagnosticsettingscategories" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-08-01/scheduledqueryrules" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) @@ -24,13 +29,16 @@ type Client struct { ActionGroupsClient *newActionGroupClient.ActionGroupsClient ActivityLogAlertsClient *insights.ActivityLogAlertsClient AlertRulesClient *classic.AlertRulesClient - DiagnosticSettingsClient *classic.DiagnosticSettingsClient - DiagnosticSettingsCategoryClient *classic.DiagnosticSettingsCategoryClient + DataCollectionRulesClient *datacollectionrules.DataCollectionRulesClient + DataCollectionEndpointsClient *datacollectionendpoints.DataCollectionEndpointsClient + DiagnosticSettingsClient *diagnosticSettingClient.DiagnosticSettingsClient + DiagnosticSettingsCategoryClient *diagnosticCategoryClient.DiagnosticSettingsCategoriesClient LogProfilesClient *classic.LogProfilesClient MetricAlertsClient *classic.MetricAlertsClient PrivateLinkScopesClient *classic.PrivateLinkScopesClient PrivateLinkScopedResourcesClient *classic.PrivateLinkScopedResourcesClient ScheduledQueryRulesClient *classic.ScheduledQueryRulesClient + ScheduledQueryRulesV2Client *scheduledqueryrules.ScheduledQueryRulesClient } func NewClient(o *common.ClientOptions) *Client { @@ -55,10 +63,16 @@ func NewClient(o *common.ClientOptions) *Client { AlertRulesClient := classic.NewAlertRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&AlertRulesClient.Client, o.ResourceManagerAuthorizer) - DiagnosticSettingsClient := classic.NewDiagnosticSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + DataCollectionEndpointsClient := datacollectionendpoints.NewDataCollectionEndpointsClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&DataCollectionEndpointsClient.Client, o.ResourceManagerAuthorizer) + + DataCollectionRulesClient := datacollectionrules.NewDataCollectionRulesClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&DataCollectionRulesClient.Client, o.ResourceManagerAuthorizer) + + DiagnosticSettingsClient := diagnosticSettingClient.NewDiagnosticSettingsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&DiagnosticSettingsClient.Client, o.ResourceManagerAuthorizer) - DiagnosticSettingsCategoryClient := classic.NewDiagnosticSettingsCategoryClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + DiagnosticSettingsCategoryClient := diagnosticCategoryClient.NewDiagnosticSettingsCategoriesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&DiagnosticSettingsCategoryClient.Client, o.ResourceManagerAuthorizer) LogProfilesClient := classic.NewLogProfilesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) @@ -76,6 +90,9 @@ func NewClient(o *common.ClientOptions) *Client { ScheduledQueryRulesClient := classic.NewScheduledQueryRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ScheduledQueryRulesClient.Client, o.ResourceManagerAuthorizer) + ScheduledQueryRulesV2Client := scheduledqueryrules.NewScheduledQueryRulesClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&ScheduledQueryRulesV2Client.Client, o.ResourceManagerAuthorizer) + return &Client{ AADDiagnosticSettingsClient: &AADDiagnosticSettingsClient, AutoscaleSettingsClient: &AutoscaleSettingsClient, @@ -84,6 +101,8 @@ func NewClient(o *common.ClientOptions) *Client { ActionGroupsClient: &ActionGroupsClient, ActivityLogAlertsClient: &ActivityLogAlertsClient, AlertRulesClient: &AlertRulesClient, + DataCollectionEndpointsClient: &DataCollectionEndpointsClient, + DataCollectionRulesClient: &DataCollectionRulesClient, DiagnosticSettingsClient: &DiagnosticSettingsClient, DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient, LogProfilesClient: &LogProfilesClient, @@ -91,5 +110,6 @@ func NewClient(o *common.ClientOptions) *Client { PrivateLinkScopesClient: &PrivateLinkScopesClient, PrivateLinkScopedResourcesClient: &PrivateLinkScopedResourcesClient, ScheduledQueryRulesClient: &ScheduledQueryRulesClient, + ScheduledQueryRulesV2Client: &ScheduledQueryRulesV2Client, } } diff --git a/internal/services/monitor/monitor_aad_diagnostic_setting_resource.go b/internal/services/monitor/monitor_aad_diagnostic_setting_resource.go index dc67c21b7e47..e136a2297adf 100644 --- a/internal/services/monitor/monitor_aad_diagnostic_setting_resource.go +++ b/internal/services/monitor/monitor_aad_diagnostic_setting_resource.go @@ -9,11 +9,10 @@ import ( "github.com/Azure/azure-sdk-for-go/services/aad/mgmt/2017-04-01/aad" "github.com/hashicorp/go-azure-helpers/lang/response" - authRuleParse "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" + authRuleParse "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - logAnalyticsParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse" - logAnalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/validate" storageParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" @@ -72,7 +71,7 @@ func resourceMonitorAADDiagnosticSetting() *pluginsdk.Resource { "log_analytics_workspace_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: logAnalyticsValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, AtLeastOneOf: []string{"eventhub_authorization_rule_id", "log_analytics_workspace_id", "storage_account_id"}, }, @@ -233,7 +232,7 @@ func resourceMonitorAADDiagnosticSettingRead(d *pluginsdk.ResourceData, meta int workspaceId := "" if resp.WorkspaceID != nil && *resp.WorkspaceID != "" { - parsedId, err := logAnalyticsParse.LogAnalyticsWorkspaceID(*resp.WorkspaceID) + parsedId, err := workspaces.ParseWorkspaceID(*resp.WorkspaceID) if err != nil { return err } diff --git a/internal/services/monitor/monitor_action_group_data_source.go b/internal/services/monitor/monitor_action_group_data_source.go index 2534e632daf7..343fabad0d87 100644 --- a/internal/services/monitor/monitor_action_group_data_source.go +++ b/internal/services/monitor/monitor_action_group_data_source.go @@ -5,7 +5,7 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" diff --git a/internal/services/monitor/monitor_action_group_resource.go b/internal/services/monitor/monitor_action_group_resource.go index befb477b696d..fff9ae22787f 100644 --- a/internal/services/monitor/monitor_action_group_resource.go +++ b/internal/services/monitor/monitor_action_group_resource.go @@ -7,7 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-09-01-preview/insights" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/eventhubs" + "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/eventhubs" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" diff --git a/internal/services/monitor/monitor_data_collection_endpoint_data_source.go b/internal/services/monitor/monitor_data_collection_endpoint_data_source.go new file mode 100644 index 000000000000..8dac6f69cc94 --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_endpoint_data_source.go @@ -0,0 +1,135 @@ +package monitor + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionendpoints" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type DataCollectionEndpointDataSource struct{} + +var _ sdk.DataSource = DataCollectionEndpointDataSource{} + +func (d DataCollectionEndpointDataSource) ModelObject() interface{} { + return &DataCollectionEndpoint{} +} + +func (d DataCollectionEndpointDataSource) ResourceType() string { + return "azurerm_monitor_data_collection_endpoint" +} + +func (d DataCollectionEndpointDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + } +} + +func (d DataCollectionEndpointDataSource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "configuration_access_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "location": commonschema.LocationComputed(), + + "logs_ingestion_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "description": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "kind": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + } +} + +func (d DataCollectionEndpointDataSource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.DataCollectionEndpointsClient + subscriptionId := metadata.Client.Account.SubscriptionId + + var state DataCollectionEndpoint + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := datacollectionendpoints.NewDataCollectionEndpointID(subscriptionId, state.ResourceGroupName, state.Name) + metadata.Logger.Infof("retrieving %s", id) + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + metadata.Logger.Infof("%s was not found - removing from state!", id) + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + var enablePublicNetWorkAccess bool + var description, kind, location, configurationAccessEndpoint, logsIngestionEndpoint string + var tag map[string]interface{} + if model := resp.Model; model != nil { + kind = flattenDataCollectionEndpointKind(model.Kind) + location = azure.NormalizeLocation(model.Location) + tag = tags.Flatten(model.Tags) + if prop := model.Properties; prop != nil { + description = flattenDataCollectionEndpointDescription(prop.Description) + if networkAcls := prop.NetworkAcls; networkAcls != nil { + enablePublicNetWorkAccess = flattenDataCollectionEndpointPublicNetworkAccess(networkAcls.PublicNetworkAccess) + } + + if prop.ConfigurationAccess != nil && prop.ConfigurationAccess.Endpoint != nil { + configurationAccessEndpoint = *prop.ConfigurationAccess.Endpoint + } + + if prop.LogsIngestion != nil && prop.LogsIngestion.Endpoint != nil { + logsIngestionEndpoint = *prop.LogsIngestion.Endpoint + } + } + } + + metadata.SetID(id) + + return metadata.Encode(&DataCollectionEndpoint{ + ConfigurationAccessEndpoint: configurationAccessEndpoint, + Description: description, + Kind: kind, + Location: location, + LogsIngestionEndpoint: logsIngestionEndpoint, + Name: id.DataCollectionEndpointName, + EnablePublicNetworkAccess: enablePublicNetWorkAccess, + ResourceGroupName: id.ResourceGroupName, + Tags: tag, + }) + }, + Timeout: 5 * time.Minute, + } +} diff --git a/internal/services/monitor/monitor_data_collection_endpoint_data_source_test.go b/internal/services/monitor/monitor_data_collection_endpoint_data_source_test.go new file mode 100644 index 000000000000..01758c0762c4 --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_endpoint_data_source_test.go @@ -0,0 +1,39 @@ +package monitor_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type MonitorDataCollectionEndpointDataSource struct{} + +func TestAccMonitorDataCollectionEndpointDataSource_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_monitor_data_collection_endpoint", "test") + d := MonitorDataCollectionEndpointDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("kind").HasValue("Windows"), + check.That(data.ResourceName).Key("public_network_access_enabled").HasValue("false"), + check.That(data.ResourceName).Key("configuration_access_endpoint").Exists(), + check.That(data.ResourceName).Key("logs_ingestion_endpoint").Exists(), + ), + }, + }) +} + +func (d MonitorDataCollectionEndpointDataSource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_monitor_data_collection_endpoint" "test" { + name = azurerm_monitor_data_collection_endpoint.test.name + resource_group_name = azurerm_monitor_data_collection_endpoint.test.resource_group_name +} +`, MonitorDataCollectionEndpointResource{}.complete(data)) +} diff --git a/internal/services/monitor/monitor_data_collection_endpoint_resource.go b/internal/services/monitor/monitor_data_collection_endpoint_resource.go new file mode 100644 index 000000000000..698dfdcb6af9 --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_endpoint_resource.go @@ -0,0 +1,319 @@ +package monitor + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionendpoints" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DataCollectionEndpoint struct { + ConfigurationAccessEndpoint string `tfschema:"configuration_access_endpoint"` + Description string `tfschema:"description"` + Kind string `tfschema:"kind"` + Name string `tfschema:"name"` + Location string `tfschema:"location"` + LogsIngestionEndpoint string `tfschema:"logs_ingestion_endpoint"` + EnablePublicNetworkAccess bool `tfschema:"public_network_access_enabled"` + ResourceGroupName string `tfschema:"resource_group_name"` + Tags map[string]interface{} `tfschema:"tags"` +} + +type DataCollectionEndpointResource struct{} + +func (r DataCollectionEndpointResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "kind": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + datacollectionendpoints.PossibleValuesForKnownDataCollectionEndpointResourceKind(), false), + }, + + "tags": commonschema.Tags(), + } +} + +func (r DataCollectionEndpointResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "configuration_access_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "logs_ingestion_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + } +} + +func (r DataCollectionEndpointResource) ResourceType() string { + return "azurerm_monitor_data_collection_endpoint" +} + +func (r DataCollectionEndpointResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return datacollectionendpoints.ValidateDataCollectionEndpointID +} + +func (r DataCollectionEndpointResource) ModelObject() interface{} { + return &DataCollectionEndpoint{} +} + +func (r DataCollectionEndpointResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + metadata.Logger.Info("Decoding state..") + var state DataCollectionEndpoint + if err := metadata.Decode(&state); err != nil { + return err + } + + client := metadata.Client.Monitor.DataCollectionEndpointsClient + subscriptionId := metadata.Client.Account.SubscriptionId + + id := datacollectionendpoints.NewDataCollectionEndpointID(subscriptionId, state.ResourceGroupName, state.Name) + metadata.Logger.Infof("creating %s", id) + + existing, err := client.Get(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for the presence of an existing %s: %+v", id, err) + } + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + input := datacollectionendpoints.DataCollectionEndpointResource{ + Kind: expandDataCollectionEndpointKind(state.Kind), + Location: azure.NormalizeLocation(state.Location), + Name: utils.String(state.Name), + Properties: &datacollectionendpoints.DataCollectionEndpoint{ + Description: utils.String(state.Description), + NetworkAcls: &datacollectionendpoints.NetworkRuleSet{ + PublicNetworkAccess: expandDataCollectionEndpointPublicNetworkAccess(state.EnablePublicNetworkAccess), + }, + }, + Tags: tags.Expand(state.Tags), + } + + if _, err := client.Create(ctx, id, input); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + Timeout: 30 * time.Minute, + } +} + +func (r DataCollectionEndpointResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.DataCollectionEndpointsClient + id, err := datacollectionendpoints.ParseDataCollectionEndpointID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("retrieving %s", *id) + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + metadata.Logger.Infof("%s was not found - removing from state!", *id) + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + var enablePublicNetWorkAccess bool + var description, kind, location, configurationAccessEndpoint, logsIngestionEndpoint string + var tag map[string]interface{} + if model := resp.Model; model != nil { + kind = flattenDataCollectionEndpointKind(model.Kind) + location = azure.NormalizeLocation(model.Location) + tag = tags.Flatten(model.Tags) + if prop := model.Properties; prop != nil { + description = flattenDataCollectionEndpointDescription(prop.Description) + if networkAcls := prop.NetworkAcls; networkAcls != nil { + enablePublicNetWorkAccess = flattenDataCollectionEndpointPublicNetworkAccess(networkAcls.PublicNetworkAccess) + } + + if prop.ConfigurationAccess != nil && prop.ConfigurationAccess.Endpoint != nil { + configurationAccessEndpoint = *prop.ConfigurationAccess.Endpoint + } + + if prop.LogsIngestion != nil && prop.LogsIngestion.Endpoint != nil { + logsIngestionEndpoint = *prop.LogsIngestion.Endpoint + } + } + } + + return metadata.Encode(&DataCollectionEndpoint{ + ConfigurationAccessEndpoint: configurationAccessEndpoint, + Description: description, + Kind: kind, + Location: location, + LogsIngestionEndpoint: logsIngestionEndpoint, + Name: id.DataCollectionEndpointName, + EnablePublicNetworkAccess: enablePublicNetWorkAccess, + ResourceGroupName: id.ResourceGroupName, + Tags: tag, + }) + }, + Timeout: 5 * time.Minute, + } +} + +func (r DataCollectionEndpointResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + id, err := datacollectionendpoints.ParseDataCollectionEndpointID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("updating %s..", *id) + client := metadata.Client.Monitor.DataCollectionEndpointsClient + resp, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + if resp.Model == nil { + return fmt.Errorf("unexpected null model of %s", *id) + } + existing := resp.Model + if existing.Properties == nil { + return fmt.Errorf("unexpected null properties of %s", *id) + } + + var state DataCollectionEndpoint + if err := metadata.Decode(&state); err != nil { + return err + } + + if metadata.ResourceData.HasChange("description") { + existing.Properties.Description = utils.String(state.Description) + } + + if metadata.ResourceData.HasChange("kind") { + existing.Kind = expandDataCollectionEndpointKind(state.Kind) + } + + if metadata.ResourceData.HasChange("public_network_access") { + existing.Properties.NetworkAcls = &datacollectionendpoints.NetworkRuleSet{ + PublicNetworkAccess: expandDataCollectionEndpointPublicNetworkAccess(state.EnablePublicNetworkAccess), + } + } + + if metadata.ResourceData.HasChange("tags") { + existing.Tags = tags.Expand(state.Tags) + } + + if _, err := client.Create(ctx, *id, *existing); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + return nil + + }, + Timeout: 30 * time.Minute, + } +} + +func (r DataCollectionEndpointResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.DataCollectionEndpointsClient + id, err := datacollectionendpoints.ParseDataCollectionEndpointID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("deleting %s..", *id) + resp, err := client.Delete(ctx, *id) + if err != nil && !response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + return nil + }, + Timeout: 30 * time.Minute, + } +} + +func expandDataCollectionEndpointKind(input string) *datacollectionendpoints.KnownDataCollectionEndpointResourceKind { + if input == "" { + return nil + } + + result := datacollectionendpoints.KnownDataCollectionEndpointResourceKind(input) + return &result +} + +func expandDataCollectionEndpointPublicNetworkAccess(input bool) *datacollectionendpoints.KnownPublicNetworkAccessOptions { + var result datacollectionendpoints.KnownPublicNetworkAccessOptions + if input { + result = datacollectionendpoints.KnownPublicNetworkAccessOptionsEnabled + } else { + result = datacollectionendpoints.KnownPublicNetworkAccessOptionsDisabled + } + return &result +} + +func flattenDataCollectionEndpointKind(input *datacollectionendpoints.KnownDataCollectionEndpointResourceKind) string { + if input == nil { + return "" + } + + return string(*input) +} + +func flattenDataCollectionEndpointDescription(input *string) string { + if input == nil { + return "" + } + + return *input +} + +func flattenDataCollectionEndpointPublicNetworkAccess(input *datacollectionendpoints.KnownPublicNetworkAccessOptions) bool { + if input == nil { + return false + } + var result bool + if *input == datacollectionendpoints.KnownPublicNetworkAccessOptionsEnabled { + result = true + } else if *input == datacollectionendpoints.KnownPublicNetworkAccessOptionsDisabled { + result = false + } + return result +} diff --git a/internal/services/monitor/monitor_data_collection_endpoint_resource_test.go b/internal/services/monitor/monitor_data_collection_endpoint_resource_test.go new file mode 100644 index 000000000000..0561fb1b929f --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_endpoint_resource_test.go @@ -0,0 +1,158 @@ +package monitor_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionendpoints" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type MonitorDataCollectionEndpointResource struct{} + +func (r MonitorDataCollectionEndpointResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := datacollectionendpoints.ParseDataCollectionEndpointID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Monitor.DataCollectionEndpointsClient.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + return utils.Bool(true), nil +} + +func TestAccMonitorDataCollectionEndpoint_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_endpoint", "test") + r := MonitorDataCollectionEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorDataCollectionEndpoint_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_endpoint", "test") + r := MonitorDataCollectionEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccMonitorDataCollectionEndpoint_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_endpoint", "test") + r := MonitorDataCollectionEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorDataCollectionEndpoint_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_endpoint", "test") + r := MonitorDataCollectionEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r MonitorDataCollectionEndpointResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s +resource "azurerm_monitor_data_collection_endpoint" "test" { + name = "acctestmdcr-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} +`, r.template(data), data.RandomInteger) +} + +func (r MonitorDataCollectionEndpointResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s +resource "azurerm_monitor_data_collection_endpoint" "test" { + name = "acctestmdce-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + kind = "Windows" + public_network_access_enabled = false + description = "acc test monitor_data_collection_endpoint complete" + tags = { + ENV = "test" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r MonitorDataCollectionEndpointResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_monitor_data_collection_endpoint" "import" { + name = azurerm_monitor_data_collection_endpoint.test.name + resource_group_name = azurerm_monitor_data_collection_endpoint.test.resource_group_name + location = azurerm_monitor_data_collection_endpoint.test.location +} +`, r.basic(data)) +} + +func (r MonitorDataCollectionEndpointResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +resource "azurerm_resource_group" "test" { + name = "acctestRG-DataCollectionEndpoint-%[1]d" + location = "%[2]s" +} +`, data.RandomInteger, data.Locations.Primary) +} diff --git a/internal/services/monitor/monitor_data_collection_rule_resource.go b/internal/services/monitor/monitor_data_collection_rule_resource.go new file mode 100644 index 000000000000..270b72a5af27 --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_rule_resource.go @@ -0,0 +1,1016 @@ +package monitor + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionrules" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DataCollectionRule struct { + DataFlows []DataFlow `tfschema:"data_flow"` + DataSources []DataSource `tfschema:"data_sources"` + Description string `tfschema:"description"` + Destinations []Destination `tfschema:"destinations"` + Kind string `tfschema:"kind"` + Name string `tfschema:"name"` + Location string `tfschema:"location"` + ResourceGroupName string `tfschema:"resource_group_name"` + Tags map[string]interface{} `tfschema:"tags"` +} + +type DataFlow struct { + Destinations []string `tfschema:"destinations"` + Streams []string `tfschema:"streams"` +} + +type DataSource struct { + Extensions []Extension `tfschema:"extension"` + PerformanceCounters []PerfCounter `tfschema:"performance_counter"` + Syslog []Syslog `tfschema:"syslog"` + WindowsEventLogs []WindowsEventLog `tfschema:"windows_event_log"` +} + +type Destination struct { + AzureMonitorMetrics []AzureMonitorMetric `tfschema:"azure_monitor_metrics"` + LogAnalytics []LogAnalytic `tfschema:"log_analytics"` +} + +type Extension struct { + ExtensionName string `tfschema:"extension_name"` + ExtensionSettings string `tfschema:"extension_json"` + InputDataSources []string `tfschema:"input_data_sources"` + Name string `tfschema:"name"` + Streams []string `tfschema:"streams"` +} + +type PerfCounter struct { + CounterSpecifiers []string `tfschema:"counter_specifiers"` + Name string `tfschema:"name"` + SamplingFrequencyInSeconds int64 `tfschema:"sampling_frequency_in_seconds"` + Streams []string `tfschema:"streams"` +} + +type Syslog struct { + FacilityNames []string `tfschema:"facility_names"` + LogLevels []string `tfschema:"log_levels"` + Name string `tfschema:"name"` +} + +type WindowsEventLog struct { + Name string `tfschema:"name"` + Streams []string `tfschema:"streams"` + XPathQueries []string `tfschema:"x_path_queries"` +} + +type AzureMonitorMetric struct { + Name string `tfschema:"name"` +} + +type LogAnalytic struct { + Name string `tfschema:"name"` + WorkspaceResourceId string `tfschema:"workspace_resource_id"` +} + +type DataCollectionRuleResource struct{} + +func (r DataCollectionRuleResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "data_flow": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "destinations": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + "streams": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownDataFlowStreams(), false), + }, + }, + }, + }, + }, + + "destinations": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "azure_monitor_metrics": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + AtLeastOneOf: []string{"destinations.0.azure_monitor_metrics", "destinations.0.log_analytics"}, + }, + "log_analytics": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "workspace_resource_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: workspaces.ValidateWorkspaceID, + }, + }, + }, + AtLeastOneOf: []string{"destinations.0.azure_monitor_metrics", "destinations.0.log_analytics"}, + }, + }, + }, + }, + + "data_sources": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "extension": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "extension_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "streams": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownExtensionDataSourceStreams(), + false), + }, + }, + "extension_json": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: pluginsdk.SuppressJsonDiff, + }, + "input_data_sources": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + "performance_counter": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "sampling_frequency_in_seconds": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 300), + }, + "streams": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownPerfCounterDataSourceStreams(), + false), + }, + }, + "counter_specifiers": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + "syslog": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "facility_names": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownSyslogDataSourceFacilityNames(), + false), + }, + }, + "log_levels": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownSyslogDataSourceLogLevels(), false), + }, + }, + }, + }, + }, + "windows_event_log": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "streams": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownWindowsEventLogDataSourceStreams(), + false), + }, + }, + "x_path_queries": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + }, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "kind": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + datacollectionrules.PossibleValuesForKnownDataCollectionRuleResourceKind(), false), + }, + + "tags": commonschema.Tags(), + } +} + +func (r DataCollectionRuleResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r DataCollectionRuleResource) ResourceType() string { + return "azurerm_monitor_data_collection_rule" +} + +func (r DataCollectionRuleResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return datacollectionrules.ValidateDataCollectionRuleID +} + +func (r DataCollectionRuleResource) ModelObject() interface{} { + return &DataCollectionRule{} +} + +func (r DataCollectionRuleResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + metadata.Logger.Info("Decoding state..") + var state DataCollectionRule + if err := metadata.Decode(&state); err != nil { + return err + } + + client := metadata.Client.Monitor.DataCollectionRulesClient + subscriptionId := metadata.Client.Account.SubscriptionId + + id := datacollectionrules.NewDataCollectionRuleID(subscriptionId, state.ResourceGroupName, state.Name) + metadata.Logger.Infof("creating %s", id) + + existing, err := client.Get(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for the presence of an existing %s: %+v", id, err) + } + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + dataSources, err := expandDataCollectionRuleDataSources(state.DataSources) + if err != nil { + return err + } + + input := datacollectionrules.DataCollectionRuleResource{ + Kind: expandDataCollectionRuleKind(state.Kind), + Location: azure.NormalizeLocation(state.Location), + Name: utils.String(state.Name), + Properties: &datacollectionrules.DataCollectionRule{ + DataFlows: expandDataCollectionRuleDataFlows(state.DataFlows), + DataSources: dataSources, + Description: utils.String(state.Description), + Destinations: expandDataCollectionRuleDestinations(state.Destinations), + }, + Tags: tags.Expand(state.Tags), + } + + if _, err := client.Create(ctx, id, input); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + Timeout: 30 * time.Minute, + } +} + +func (r DataCollectionRuleResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.DataCollectionRulesClient + id, err := datacollectionrules.ParseDataCollectionRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("retrieving %s", *id) + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + metadata.Logger.Infof("%s was not found - removing from state!", *id) + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + var description, kind, location string + var tag map[string]interface{} + var dataFlows []DataFlow + var dataSources []DataSource + var destinations []Destination + + if model := resp.Model; model != nil { + kind = flattenDataCollectionRuleKind(model.Kind) + location = azure.NormalizeLocation(model.Location) + tag = tags.Flatten(model.Tags) + if prop := model.Properties; prop != nil { + description = flattenStringPtr(prop.Description) + dataFlows = flattenDataCollectionRuleDataFlows(prop.DataFlows) + dataSources = flattenDataCollectionRuleDataSources(prop.DataSources) + destinations = flattenDataCollectionRuleDestinations(prop.Destinations) + } + } + + return metadata.Encode(&DataCollectionRule{ + Name: id.DataCollectionRuleName, + ResourceGroupName: id.ResourceGroupName, + DataFlows: dataFlows, + DataSources: dataSources, + Description: description, + Destinations: destinations, + Kind: kind, + Location: location, + Tags: tag, + }) + }, + Timeout: 5 * time.Minute, + } +} + +func (r DataCollectionRuleResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + id, err := datacollectionrules.ParseDataCollectionRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("updating %s..", *id) + client := metadata.Client.Monitor.DataCollectionRulesClient + resp, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + if resp.Model == nil { + return fmt.Errorf("unexpected null model of %s", *id) + } + existing := resp.Model + if existing.Properties == nil { + return fmt.Errorf("unexpected null properties of %s", *id) + } + + var state DataCollectionRule + if err := metadata.Decode(&state); err != nil { + return err + } + + if metadata.ResourceData.HasChange("kind") { + existing.Kind = expandDataCollectionRuleKind(state.Kind) + } + + if metadata.ResourceData.HasChange("tags") { + existing.Tags = tags.Expand(state.Tags) + } + + if metadata.ResourceData.HasChange("data_flow") { + existing.Properties.DataFlows = expandDataCollectionRuleDataFlows(state.DataFlows) + } + + if metadata.ResourceData.HasChange("data_sources") { + dataSource, err := expandDataCollectionRuleDataSources(state.DataSources) + if err != nil { + return err + } + existing.Properties.DataSources = dataSource + } + + if metadata.ResourceData.HasChange("description") { + existing.Properties.Description = utils.String(state.Description) + } + + if metadata.ResourceData.HasChange("destinations") { + existing.Properties.Destinations = expandDataCollectionRuleDestinations(state.Destinations) + } + + // otherwise Service will return an error: "The resource definition is invalid." + existing.SystemData = nil + + if _, err := client.Create(ctx, *id, *existing); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + return nil + + }, + Timeout: 30 * time.Minute, + } +} + +func (r DataCollectionRuleResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.DataCollectionRulesClient + id, err := datacollectionrules.ParseDataCollectionRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("deleting %s..", *id) + resp, err := client.Delete(ctx, *id) + if err != nil && !response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + return nil + }, + Timeout: 30 * time.Minute, + } +} + +func expandDataCollectionRuleKind(input string) *datacollectionrules.KnownDataCollectionRuleResourceKind { + if input == "" { + return nil + } + + result := datacollectionrules.KnownDataCollectionRuleResourceKind(input) + return &result +} + +func stringSlice(input []string) *[]string { + return &input +} + +func expandDataCollectionRuleDataFlows(input []DataFlow) *[]datacollectionrules.DataFlow { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.DataFlow, 0) + for _, v := range input { + result = append(result, datacollectionrules.DataFlow{ + Destinations: stringSlice(v.Destinations), + Streams: expandDataCollectionRuleDataFlowStreams(v.Streams), + }) + } + return &result +} + +func expandDataCollectionRuleDataFlowStreams(input []string) *[]datacollectionrules.KnownDataFlowStreams { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownDataFlowStreams, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownDataFlowStreams(v)) + } + return &result +} + +func expandDataCollectionRuleDataSources(input []DataSource) (*datacollectionrules.DataSourcesSpec, error) { + if len(input) == 0 { + return nil, nil + } + + extension, err := expandDataCollectionRuleDataSourceExtensions(input[0].Extensions) + if err != nil { + return nil, err + } + return &datacollectionrules.DataSourcesSpec{ + Extensions: extension, + PerformanceCounters: expandDataCollectionRuleDataSourcePerfCounters(input[0].PerformanceCounters), + Syslog: expandDataCollectionRuleDataSourceSyslog(input[0].Syslog), + WindowsEventLogs: expandDataCollectionRuleDataSourceWindowsEventLogs(input[0].WindowsEventLogs), + }, nil + +} + +func expandDataCollectionRuleDataSourceExtensions(input []Extension) (*[]datacollectionrules.ExtensionDataSource, error) { + if len(input) == 0 { + return nil, nil + } + + result := make([]datacollectionrules.ExtensionDataSource, 0) + for _, v := range input { + var extensionSettings interface{} + if v.ExtensionSettings != "" { + settings, err := pluginsdk.ExpandJsonFromString(v.ExtensionSettings) + if err != nil { + return nil, err + } + extensionSettings = settings + } + + result = append(result, datacollectionrules.ExtensionDataSource{ + ExtensionName: v.ExtensionName, + ExtensionSettings: &extensionSettings, + InputDataSources: stringSlice(v.InputDataSources), + Name: utils.String(v.Name), + Streams: expandDataCollectionRuleDataSourceExtensionStreams(v.Streams), + }) + } + return &result, nil +} + +func expandDataCollectionRuleDataSourceExtensionStreams(input []string) *[]datacollectionrules.KnownExtensionDataSourceStreams { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownExtensionDataSourceStreams, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownExtensionDataSourceStreams(v)) + } + return &result +} + +func expandDataCollectionRuleDataSourcePerfCounters(input []PerfCounter) *[]datacollectionrules.PerfCounterDataSource { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.PerfCounterDataSource, 0) + for _, v := range input { + result = append(result, datacollectionrules.PerfCounterDataSource{ + CounterSpecifiers: stringSlice(v.CounterSpecifiers), + Name: utils.String(v.Name), + SamplingFrequencyInSeconds: utils.Int64(v.SamplingFrequencyInSeconds), + Streams: expandDataCollectionRuleDataSourcePerfCounterStreams(v.Streams), + }) + } + return &result +} + +func expandDataCollectionRuleDataSourcePerfCounterStreams(input []string) *[]datacollectionrules.KnownPerfCounterDataSourceStreams { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownPerfCounterDataSourceStreams, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownPerfCounterDataSourceStreams(v)) + } + return &result +} + +func expandDataCollectionRuleDataSourceSyslog(input []Syslog) *[]datacollectionrules.SyslogDataSource { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.SyslogDataSource, 0) + for _, v := range input { + result = append(result, datacollectionrules.SyslogDataSource{ + FacilityNames: expandDataCollectionRuleDataSourceSyslogFacilityNames(v.FacilityNames), + LogLevels: expandDataCollectionRuleDataSourceSyslogLogLevels(v.LogLevels), + Name: utils.String(v.Name), + Streams: &[]datacollectionrules.KnownSyslogDataSourceStreams{datacollectionrules.KnownSyslogDataSourceStreamsMicrosoftNegativeSyslog}, + }) + } + return &result +} + +func expandDataCollectionRuleDataSourceSyslogFacilityNames(input []string) *[]datacollectionrules.KnownSyslogDataSourceFacilityNames { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownSyslogDataSourceFacilityNames, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownSyslogDataSourceFacilityNames(v)) + } + return &result +} + +func expandDataCollectionRuleDataSourceSyslogLogLevels(input []string) *[]datacollectionrules.KnownSyslogDataSourceLogLevels { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownSyslogDataSourceLogLevels, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownSyslogDataSourceLogLevels(v)) + } + return &result +} + +func expandDataCollectionRuleDataSourceWindowsEventLogs(input []WindowsEventLog) *[]datacollectionrules.WindowsEventLogDataSource { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.WindowsEventLogDataSource, 0) + for _, v := range input { + result = append(result, datacollectionrules.WindowsEventLogDataSource{ + Name: utils.String(v.Name), + Streams: expandDataCollectionRuleDataSourceWindowsEventLogsStreams(v.Streams), + XPathQueries: stringSlice(v.XPathQueries), + }) + } + return &result +} + +func expandDataCollectionRuleDataSourceWindowsEventLogsStreams(input []string) *[]datacollectionrules.KnownWindowsEventLogDataSourceStreams { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.KnownWindowsEventLogDataSourceStreams, 0) + for _, v := range input { + result = append(result, datacollectionrules.KnownWindowsEventLogDataSourceStreams(v)) + } + return &result +} + +func expandDataCollectionRuleDestinations(input []Destination) *datacollectionrules.DestinationsSpec { + if len(input) == 0 { + return nil + } + + return &datacollectionrules.DestinationsSpec{ + AzureMonitorMetrics: expandDataCollectionRuleDestinationMetrics(input[0].AzureMonitorMetrics), + LogAnalytics: expandDataCollectionRuleDestinationLogAnalytics(input[0].LogAnalytics), + } +} + +func expandDataCollectionRuleDestinationMetrics(input []AzureMonitorMetric) *datacollectionrules.AzureMonitorMetricsDestination { + if len(input) == 0 { + return nil + } + + return &datacollectionrules.AzureMonitorMetricsDestination{ + Name: utils.String(input[0].Name), + } +} + +func expandDataCollectionRuleDestinationLogAnalytics(input []LogAnalytic) *[]datacollectionrules.LogAnalyticsDestination { + if len(input) == 0 { + return nil + } + + result := make([]datacollectionrules.LogAnalyticsDestination, 0) + for _, v := range input { + result = append(result, datacollectionrules.LogAnalyticsDestination{ + Name: utils.String(v.Name), + WorkspaceResourceId: utils.String(v.WorkspaceResourceId), + }) + } + return &result +} + +func flattenDataCollectionRuleKind(input *datacollectionrules.KnownDataCollectionRuleResourceKind) string { + if input == nil { + return "" + } + return string(*input) +} + +func flattenStringPtr(input *string) string { + if input == nil { + return "" + } + return *input +} + +func flattenStringSlicePtr(input *[]string) []string { + if input == nil { + return make([]string, 0) + } + return *input +} + +func flattenDataCollectionRuleDataFlows(input *[]datacollectionrules.DataFlow) []DataFlow { + if input == nil { + return make([]DataFlow, 0) + } + + result := make([]DataFlow, 0) + for _, v := range *input { + result = append(result, DataFlow{ + Destinations: flattenStringSlicePtr(v.Destinations), + Streams: flattenDataCollectionRuleDataFlowStreams(v.Streams), + }) + } + return result +} + +func flattenDataCollectionRuleDataFlowStreams(input *[]datacollectionrules.KnownDataFlowStreams) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleDataSources(input *datacollectionrules.DataSourcesSpec) []DataSource { + if input == nil { + return make([]DataSource, 0) + } + + return []DataSource{{ + Extensions: flattenDataCollectionRuleDataSourceExtensions(input.Extensions), + PerformanceCounters: flattenDataCollectionRuleDataSourcePerfCounters(input.PerformanceCounters), + Syslog: flattenDataCollectionRuleDataSourceSyslog(input.Syslog), + WindowsEventLogs: flattenDataCollectionRuleWindowsEventLogs(input.WindowsEventLogs), + }} +} + +func flattenDataCollectionRuleDataSourceExtensions(input *[]datacollectionrules.ExtensionDataSource) []Extension { + if input == nil { + return make([]Extension, 0) + } + + result := make([]Extension, 0) + for _, v := range *input { + extensionSettings := "" + if v.ExtensionSettings != nil { + settingString, _ := pluginsdk.FlattenJsonToString((*v.ExtensionSettings).(map[string]interface{})) + extensionSettings = settingString + } + result = append(result, Extension{ + ExtensionName: v.ExtensionName, + Name: flattenStringPtr(v.Name), + ExtensionSettings: extensionSettings, + InputDataSources: flattenStringSlicePtr(v.InputDataSources), + Streams: flattenDataCollectionRuleDataSourceExtensionStreams(v.Streams), + }) + } + return result +} + +func flattenDataCollectionRuleDataSourceExtensionStreams(input *[]datacollectionrules.KnownExtensionDataSourceStreams) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleDataSourcePerfCounters(input *[]datacollectionrules.PerfCounterDataSource) []PerfCounter { + if input == nil { + return make([]PerfCounter, 0) + } + + result := make([]PerfCounter, 0) + for _, v := range *input { + result = append(result, PerfCounter{ + Name: flattenStringPtr(v.Name), + CounterSpecifiers: flattenStringSlicePtr(v.CounterSpecifiers), + SamplingFrequencyInSeconds: utils.NormaliseNilableInt64(v.SamplingFrequencyInSeconds), + Streams: flattenDataCollectionRuleDataSourcePerfCounterStreams(v.Streams), + }) + } + return result +} + +func flattenDataCollectionRuleDataSourcePerfCounterStreams(input *[]datacollectionrules.KnownPerfCounterDataSourceStreams) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleDataSourceSyslog(input *[]datacollectionrules.SyslogDataSource) []Syslog { + if input == nil { + return make([]Syslog, 0) + } + + result := make([]Syslog, 0) + for _, v := range *input { + result = append(result, Syslog{ + Name: flattenStringPtr(v.Name), + FacilityNames: flattenDataCollectionRuleDataSourceSyslogFacilityNames(v.FacilityNames), + LogLevels: flattenDataCollectionRuleDataSourceSyslogLogLevels(v.LogLevels), + }) + } + return result +} + +func flattenDataCollectionRuleDataSourceSyslogFacilityNames(input *[]datacollectionrules.KnownSyslogDataSourceFacilityNames) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleDataSourceSyslogLogLevels(input *[]datacollectionrules.KnownSyslogDataSourceLogLevels) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleWindowsEventLogs(input *[]datacollectionrules.WindowsEventLogDataSource) []WindowsEventLog { + if input == nil { + return make([]WindowsEventLog, 0) + } + + result := make([]WindowsEventLog, 0) + for _, v := range *input { + result = append(result, WindowsEventLog{ + Name: flattenStringPtr(v.Name), + XPathQueries: flattenStringSlicePtr(v.XPathQueries), + Streams: flattenDataCollectionRuleWindowsEventLogStreams(v.Streams), + }) + } + return result +} + +func flattenDataCollectionRuleWindowsEventLogStreams(input *[]datacollectionrules.KnownWindowsEventLogDataSourceStreams) []string { + if input == nil { + return make([]string, 0) + } + + result := make([]string, 0) + for _, v := range *input { + result = append(result, string(v)) + } + return result +} + +func flattenDataCollectionRuleDestinations(input *datacollectionrules.DestinationsSpec) []Destination { + if input == nil { + return make([]Destination, 0) + } + + return []Destination{{ + AzureMonitorMetrics: flattenDataCollectionRuleDestinationMetrics(input.AzureMonitorMetrics), + LogAnalytics: flattenDataCollectionRuleDestinationLogAnalytics(input.LogAnalytics), + }} +} + +func flattenDataCollectionRuleDestinationMetrics(input *datacollectionrules.AzureMonitorMetricsDestination) []AzureMonitorMetric { + if input == nil { + return make([]AzureMonitorMetric, 0) + } + + return []AzureMonitorMetric{{ + Name: flattenStringPtr(input.Name), + }} +} + +func flattenDataCollectionRuleDestinationLogAnalytics(input *[]datacollectionrules.LogAnalyticsDestination) []LogAnalytic { + if input == nil { + return make([]LogAnalytic, 0) + } + + result := make([]LogAnalytic, 0) + for _, v := range *input { + result = append(result, LogAnalytic{ + Name: flattenStringPtr(v.Name), + WorkspaceResourceId: flattenStringPtr(v.WorkspaceResourceId), + }) + } + return result +} diff --git a/internal/services/monitor/monitor_data_collection_rule_resource_test.go b/internal/services/monitor/monitor_data_collection_rule_resource_test.go new file mode 100644 index 000000000000..1ea51da7d069 --- /dev/null +++ b/internal/services/monitor/monitor_data_collection_rule_resource_test.go @@ -0,0 +1,387 @@ +package monitor_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-04-01/datacollectionrules" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type MonitorDataCollectionRuleResource struct{} + +func (r MonitorDataCollectionRuleResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := datacollectionrules.ParseDataCollectionRuleID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Monitor.DataCollectionRulesClient.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + return utils.Bool(true), nil +} + +func TestAccMonitorDataCollectionRule_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_rule", "test") + r := MonitorDataCollectionRuleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorDataCollectionRule_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_rule", "test") + r := MonitorDataCollectionRuleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccMonitorDataCollectionRule_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_rule", "test") + r := MonitorDataCollectionRuleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorDataCollectionRule_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_data_collection_rule", "test") + r := MonitorDataCollectionRuleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r MonitorDataCollectionRuleResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_monitor_data_collection_rule" "test" { + name = "acctestmdcr-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + destinations { + azure_monitor_metrics { + name = "test-destination-metrics" + } + } + data_flow { + streams = ["Microsoft-InsightsMetrics"] + destinations = ["test-destination-metrics"] + } +} +`, r.template(data), data.RandomInteger) +} + +func (r MonitorDataCollectionRuleResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_log_analytics_workspace" "test1" { + name = "acctest-law-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_monitor_data_collection_rule" "test" { + name = "acctestmdcr-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + destinations { + log_analytics { + workspace_resource_id = azurerm_log_analytics_workspace.test1.id + name = "test-destination-log" + } + azure_monitor_metrics { + name = "test-destination-metrics" + } + } + + data_flow { + streams = ["Microsoft-InsightsMetrics"] + destinations = ["test-destination-metrics"] + } + + data_flow { + streams = ["Microsoft-InsightsMetrics", "Microsoft-Syslog", "Microsoft-Perf"] + destinations = ["test-destination-log"] + } + + data_sources { + syslog { + facility_names = ["*"] + log_levels = ["*"] + name = "test-datasource-syslog" + } + performance_counter { + streams = ["Microsoft-Perf", "Microsoft-InsightsMetrics"] + sampling_frequency_in_seconds = 10 + counter_specifiers = ["Processor(*)\\%% Processor Time"] + name = "test-datasource-perfcounter" + } + } + + kind = "Linux" + description = "acc test monitor_data_collection_rule" + tags = { + ENV = "test" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r MonitorDataCollectionRuleResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_log_analytics_workspace" "test1" { + name = "acctest-law-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_log_analytics_solution" "test1" { + solution_name = "WindowsEventForwarding" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + workspace_resource_id = azurerm_log_analytics_workspace.test1.id + workspace_name = azurerm_log_analytics_workspace.test1.name + plan { + publisher = "Microsoft" + product = "OMSGallery/WindowsEventForwarding" + } +} + +resource "azurerm_log_analytics_workspace" "test2" { + name = "acctest-law2-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_log_analytics_solution" "test2" { + solution_name = "WindowsEventForwarding" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + workspace_resource_id = azurerm_log_analytics_workspace.test1.id + workspace_name = azurerm_log_analytics_workspace.test1.name + plan { + publisher = "Microsoft" + product = "OMSGallery/WindowsEventForwarding" + } +} + +resource "azurerm_monitor_data_collection_rule" "test" { + name = "acctestmdcr-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + destinations { + log_analytics { + workspace_resource_id = azurerm_log_analytics_workspace.test1.id + name = "test-destination-log1" + } + + log_analytics { + workspace_resource_id = azurerm_log_analytics_workspace.test2.id + name = "test-destination-log2" + } + + azure_monitor_metrics { + name = "test-destination-metrics" + } + } + + data_flow { + streams = ["Microsoft-InsightsMetrics"] + destinations = ["test-destination-metrics"] + } + + data_flow { + streams = ["Microsoft-InsightsMetrics", "Microsoft-Syslog", "Microsoft-Perf"] + destinations = ["test-destination-log1"] + } + + data_flow { + streams = ["Microsoft-Event", "Microsoft-WindowsEvent"] + destinations = ["test-destination-log1", "test-destination-log2"] + } + + data_sources { + syslog { + facility_names = [ + "auth", + "authpriv", + "cron", + "daemon", + "kern", + ] + log_levels = [ + "Debug", + "Info", + "Notice", + ] + name = "test-datasource-syslog" + } + + performance_counter { + streams = ["Microsoft-Perf", "Microsoft-InsightsMetrics"] + sampling_frequency_in_seconds = 10 + counter_specifiers = [ + "Processor(*)\\%% Processor Time", + "Processor(*)\\%% Idle Time", + "Processor(*)\\%% User Time", + "Processor(*)\\%% Nice Time", + "Processor(*)\\%% Privileged Time", + "Processor(*)\\%% IO Wait Time", + "Processor(*)\\%% Interrupt Time", + "Processor(*)\\%% DPC Time", + ] + name = "test-datasource-perfcounter" + } + + performance_counter { + streams = ["Microsoft-Perf"] + sampling_frequency_in_seconds = 20 + counter_specifiers = [ + "Network(*)\\Total Bytes Transmitted", + "Network(*)\\Total Bytes Received", + "Network(*)\\Total Bytes", + "Network(*)\\Total Packets Transmitted", + "Network(*)\\Total Packets Received", + "Network(*)\\Total Rx Errors", + "Network(*)\\Total Tx Errors", + "Network(*)\\Total Collisions" + ] + name = "test-datasource-perfcounter2" + } + + windows_event_log { + streams = ["Microsoft-WindowsEvent"] + x_path_queries = ["*[System/Level=1]"] + name = "test-datasource-wineventlog" + } + + extension { + streams = ["Microsoft-WindowsEvent"] + input_data_sources = ["test-datasource-wineventlog"] + extension_name = "test-extension-name" + extension_json = jsonencode({ + a = 1 + b = "hello" + }) + name = "test-datasource-extension" + } + } + + description = "acc test monitor_data_collection_rule complete" + tags = { + ENV = "test" + ENV2 = "test2" + } + + depends_on = [ + azurerm_log_analytics_solution.test1, + azurerm_log_analytics_solution.test2, + ] +} + + +`, r.template(data), data.RandomInteger) +} + +func (r MonitorDataCollectionRuleResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_data_collection_rule" "import" { + name = azurerm_monitor_data_collection_rule.test.name + resource_group_name = azurerm_monitor_data_collection_rule.test.resource_group_name + location = azurerm_monitor_data_collection_rule.test.location + destinations { + azure_monitor_metrics { + name = azurerm_monitor_data_collection_rule.test.destinations.0.azure_monitor_metrics.0.name + } + } + data_flow { + streams = azurerm_monitor_data_collection_rule.test.data_flow.0.streams + destinations = azurerm_monitor_data_collection_rule.test.data_flow.0.destinations + } +} +`, r.basic(data)) +} + +func (r MonitorDataCollectionRuleResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-DataCollectionRule-%[1]d" + location = "%[2]s" +} + +`, data.RandomInteger, data.Locations.Primary) +} diff --git a/internal/services/monitor/monitor_diagnostic_categories_data_source.go b/internal/services/monitor/monitor_diagnostic_categories_data_source.go index e95e3d2bdc80..6bf806fa63f2 100644 --- a/internal/services/monitor/monitor_diagnostic_categories_data_source.go +++ b/internal/services/monitor/monitor_diagnostic_categories_data_source.go @@ -5,15 +5,17 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-07-01-preview/insights" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-05-01-preview/diagnosticsettingscategories" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" ) func dataSourceMonitorDiagnosticCategories() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Read: dataSourceMonitorDiagnosticCategoriesRead, Timeouts: &pluginsdk.ResourceTimeout{ @@ -27,7 +29,14 @@ func dataSourceMonitorDiagnosticCategories() *pluginsdk.Resource { ValidateFunc: azure.ValidateResourceID, }, - "logs": { + "log_category_types": { + Type: pluginsdk.TypeSet, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + Computed: true, + }, + + "log_category_groups": { Type: pluginsdk.TypeSet, Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, Set: pluginsdk.HashString, @@ -42,6 +51,18 @@ func dataSourceMonitorDiagnosticCategories() *pluginsdk.Resource { }, }, } + + if !features.FourPointOhBeta() { + resource.Schema["logs"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeSet, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + Computed: true, + Deprecated: "`logs` will be removed in favour of the property `log_category_types` in version 4.0 of the AzureRM Provider.", + } + } + + return resource } func dataSourceMonitorDiagnosticCategoriesRead(d *pluginsdk.ResourceData, meta interface{}) error { @@ -49,50 +70,71 @@ func dataSourceMonitorDiagnosticCategoriesRead(d *pluginsdk.ResourceData, meta i ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - actualResourceId := d.Get("resource_id").(string) + actualResourceId := commonids.NewScopeID(d.Get("resource_id").(string)) // trim off the leading `/` since the CheckExistenceByID / List methods don't expect it - resourceId := strings.TrimPrefix(actualResourceId, "/") + resourceId := strings.TrimPrefix(actualResourceId.Scope, "/") + resourceIdToList, err := commonids.ParseScopeID(resourceId) + if err != nil { + return fmt.Errorf("parsing resource id error: %+v", err) + } // then retrieve the possible Diagnostics Categories for this Resource - categories, err := categoriesClient.List(ctx, resourceId) + categories, err := categoriesClient.DiagnosticSettingsCategoryList(ctx, *resourceIdToList) if err != nil { return fmt.Errorf("retrieving Diagnostics Categories for Resource %q: %+v", actualResourceId, err) } - if categories.Value == nil { + if categories.Model == nil && categories.Model.Value == nil { return fmt.Errorf("retrieving Diagnostics Categories for Resource %q: `categories.Value` was nil", actualResourceId) } - d.SetId(actualResourceId) - val := *categories.Value + d.SetId(actualResourceId.ID()) + val := *categories.Model.Value metrics := make([]string, 0) logs := make([]string, 0) + categoryGroups := make([]string, 0) for _, v := range val { if v.Name == nil { continue } - if category := v.DiagnosticSettingsCategory; category != nil { - switch category.CategoryType { - case insights.CategoryTypeLogs: - logs = append(logs, *v.Name) - case insights.CategoryTypeMetrics: - metrics = append(metrics, *v.Name) - default: - return fmt.Errorf("Unsupported category type %q", string(category.CategoryType)) + if category := v.Properties; category != nil { + if category.CategoryGroups != nil { + for _, item := range *category.CategoryGroups { + categoryGroups = append(categoryGroups, item) + } + } + if category.CategoryType != nil { + switch *category.CategoryType { + case diagnosticsettingscategories.CategoryTypeLogs: + logs = append(logs, *v.Name) + case diagnosticsettingscategories.CategoryTypeMetrics: + metrics = append(metrics, *v.Name) + default: + return fmt.Errorf("Unsupported category type %q", string(*category.CategoryType)) + } } } } - if err := d.Set("logs", logs); err != nil { - return fmt.Errorf("setting `logs`: %+v", err) + if err := d.Set("log_category_types", logs); err != nil { + return fmt.Errorf("setting `log_category_types`: %+v", err) + } + + if !features.FourPointOhBeta() { + if err := d.Set("logs", logs); err != nil { + return fmt.Errorf("setting `log`: %+v", err) + } } if err := d.Set("metrics", metrics); err != nil { return fmt.Errorf("setting `metrics`: %+v", err) } + if err := d.Set("log_category_groups", categoryGroups); err != nil { + return fmt.Errorf("setting `log_category_groups`: %+v", err) + } return nil } diff --git a/internal/services/monitor/monitor_diagnostic_categories_data_source_test.go b/internal/services/monitor/monitor_diagnostic_categories_data_source_test.go index 50a045b45581..c93f9c2a940f 100644 --- a/internal/services/monitor/monitor_diagnostic_categories_data_source_test.go +++ b/internal/services/monitor/monitor_diagnostic_categories_data_source_test.go @@ -20,6 +20,8 @@ func TestAccDataSourceMonitorDiagnosticCategories_appService(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("metrics.#").Exists(), check.That(data.ResourceName).Key("logs.#").Exists(), + check.That(data.ResourceName).Key("log_category_types.#").Exists(), + check.That(data.ResourceName).Key("log_category_groups.#").Exists(), ), }, }) @@ -35,6 +37,8 @@ func TestAccDataSourceMonitorDiagnosticCategories_storageAccount(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("metrics.#").Exists(), check.That(data.ResourceName).Key("logs.#").Exists(), + check.That(data.ResourceName).Key("log_category_types.#").Exists(), + check.That(data.ResourceName).Key("log_category_groups.#").Exists(), ), }, }) diff --git a/internal/services/monitor/monitor_diagnostic_setting_resource.go b/internal/services/monitor/monitor_diagnostic_setting_resource.go index d13c4f2290ea..b7c05c3a67fe 100644 --- a/internal/services/monitor/monitor_diagnostic_setting_resource.go +++ b/internal/services/monitor/monitor_diagnostic_setting_resource.go @@ -1,21 +1,21 @@ package monitor import ( + "bytes" "context" "fmt" "log" "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-07-01-preview/insights" "github.com/hashicorp/go-azure-helpers/lang/response" - authRuleParse "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2017-04-01/authorizationrulesnamespaces" + authRuleParse "github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/authorizationrulesnamespaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-05-01-preview/diagnosticsettings" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" eventhubValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate" - logAnalyticsParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse" - logAnalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/validate" storageParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" @@ -76,7 +76,7 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { "log_analytics_workspace_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: logAnalyticsValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, }, "storage_account_id": { @@ -103,7 +103,12 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "category": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, + }, + + "category_group": { + Type: pluginsdk.TypeString, + Optional: true, }, "enabled": { @@ -133,6 +138,7 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { }, }, }, + Set: resourceMonitorDiagnosticLogSettingHash, }, "metric": { @@ -172,6 +178,7 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { }, }, }, + Set: resourceMonitorDiagnosticMetricsSettingHash, }, }, } @@ -185,17 +192,18 @@ func resourceMonitorDiagnosticSettingCreateUpdate(d *pluginsdk.ResourceData, met name := d.Get("name").(string) actualResourceId := d.Get("target_resource_id").(string) + diagnosticSettingId := diagnosticsettings.NewScopedDiagnosticSettingID(actualResourceId, name) if d.IsNewResource() { - existing, err := client.Get(ctx, actualResourceId, name) + existing, err := client.Get(ctx, diagnosticSettingId) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for presence of existing Monitor Diagnostic Setting %q for Resource %q: %s", name, actualResourceId, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing Monitor Diagnostic Setting %q for Resource %q: %s", diagnosticSettingId.Name, diagnosticSettingId.ResourceUri, err) } } - if existing.ID != nil && *existing.ID != "" { - return tf.ImportAsExistsError("azurerm_monitor_diagnostic_setting", *existing.ID) + if existing.Model != nil && existing.Model.Id != nil && *existing.Model.Id != "" { + return tf.ImportAsExistsError("azurerm_monitor_diagnostic_setting", *existing.Model.Id) } } @@ -212,14 +220,14 @@ func resourceMonitorDiagnosticSettingCreateUpdate(d *pluginsdk.ResourceData, met // also if there's none enabled valid := false for _, v := range logs { - if v.Enabled != nil && *v.Enabled { + if v.Enabled { valid = true break } } if !valid { for _, v := range metrics { - if v.Enabled != nil && *v.Enabled { + if v.Enabled { valid = true break } @@ -230,8 +238,8 @@ func resourceMonitorDiagnosticSettingCreateUpdate(d *pluginsdk.ResourceData, met return fmt.Errorf("At least one `log` or `metric` must be enabled") } - properties := insights.DiagnosticSettingsResource{ - DiagnosticSettings: &insights.DiagnosticSettings{ + parameters := diagnosticsettings.DiagnosticSettingsResource{ + Properties: &diagnosticsettings.DiagnosticSettings{ Logs: &logs, Metrics: &metrics, }, @@ -241,26 +249,26 @@ func resourceMonitorDiagnosticSettingCreateUpdate(d *pluginsdk.ResourceData, met eventHubAuthorizationRuleId := d.Get("eventhub_authorization_rule_id").(string) eventHubName := d.Get("eventhub_name").(string) if eventHubAuthorizationRuleId != "" { - properties.DiagnosticSettings.EventHubAuthorizationRuleID = utils.String(eventHubAuthorizationRuleId) - properties.DiagnosticSettings.EventHubName = utils.String(eventHubName) + parameters.Properties.EventHubAuthorizationRuleId = utils.String(eventHubAuthorizationRuleId) + parameters.Properties.EventHubName = utils.String(eventHubName) valid = true } workspaceId := d.Get("log_analytics_workspace_id").(string) if workspaceId != "" { - properties.DiagnosticSettings.WorkspaceID = utils.String(workspaceId) + parameters.Properties.WorkspaceId = utils.String(workspaceId) valid = true } storageAccountId := d.Get("storage_account_id").(string) if storageAccountId != "" { - properties.DiagnosticSettings.StorageAccountID = utils.String(storageAccountId) + parameters.Properties.StorageAccountId = utils.String(storageAccountId) valid = true } if v := d.Get("log_analytics_destination_type").(string); v != "" { if workspaceId != "" { - properties.DiagnosticSettings.LogAnalyticsDestinationType = &v + parameters.Properties.LogAnalyticsDestinationType = &v } else { return fmt.Errorf("`log_analytics_workspace_id` must be set for `log_analytics_destination_type` to be used") } @@ -270,18 +278,16 @@ func resourceMonitorDiagnosticSettingCreateUpdate(d *pluginsdk.ResourceData, met return fmt.Errorf("Either a `eventhub_authorization_rule_id`, `log_analytics_workspace_id` or `storage_account_id` must be set") } - // the Azure SDK prefixes the URI with a `/` such this makes a bad request if we don't trim the `/` - targetResourceId := strings.TrimPrefix(actualResourceId, "/") - if _, err := client.CreateOrUpdate(ctx, targetResourceId, properties, name); err != nil { + if _, err := client.CreateOrUpdate(ctx, diagnosticSettingId, parameters); err != nil { return fmt.Errorf("creating Monitor Diagnostics Setting %q for Resource %q: %+v", name, actualResourceId, err) } - read, err := client.Get(ctx, targetResourceId, name) + read, err := client.Get(ctx, diagnosticSettingId) if err != nil { return err } - if read.ID == nil { - return fmt.Errorf("Cannot read ID for Monitor Diagnostics %q for Resource ID %q", name, actualResourceId) + if read.Model == nil && read.Model.Id == nil { + return fmt.Errorf("Cannot read ID for Monitor Diagnostics %q for Resource ID %q", diagnosticSettingId.Name, diagnosticSettingId.ResourceUri) } d.SetId(fmt.Sprintf("%s|%s", actualResourceId, name)) @@ -299,11 +305,10 @@ func resourceMonitorDiagnosticSettingRead(d *pluginsdk.ResourceData, meta interf return err } - actualResourceId := id.ResourceID - targetResourceId := strings.TrimPrefix(actualResourceId, "/") - resp, err := client.Get(ctx, targetResourceId, id.Name) + actualResourceId := id.ResourceUri + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] Monitor Diagnostics Setting %q was not found for Resource %q - removing from state!", id.Name, actualResourceId) d.SetId("") return nil @@ -313,51 +318,54 @@ func resourceMonitorDiagnosticSettingRead(d *pluginsdk.ResourceData, meta interf } d.Set("name", id.Name) - d.Set("target_resource_id", id.ResourceID) - - d.Set("eventhub_name", resp.EventHubName) - eventhubAuthorizationRuleId := "" - if resp.EventHubAuthorizationRuleID != nil && *resp.EventHubAuthorizationRuleID != "" { - authRuleId := utils.NormalizeNilableString(resp.EventHubAuthorizationRuleID) - parsedId, err := authRuleParse.ParseAuthorizationRuleIDInsensitively(authRuleId) - if err != nil { - return err - } - - eventhubAuthorizationRuleId = parsedId.ID() - } - d.Set("eventhub_authorization_rule_id", eventhubAuthorizationRuleId) + d.Set("target_resource_id", id.ResourceUri) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("eventhub_name", props.EventHubName) + eventhubAuthorizationRuleId := "" + if props.EventHubAuthorizationRuleId != nil && *props.EventHubAuthorizationRuleId != "" { + authRuleId := utils.NormalizeNilableString(props.EventHubAuthorizationRuleId) + parsedId, err := authRuleParse.ParseAuthorizationRuleIDInsensitively(authRuleId) + if err != nil { + return err + } + eventhubAuthorizationRuleId = parsedId.ID() + } + d.Set("eventhub_authorization_rule_id", eventhubAuthorizationRuleId) - workspaceId := "" - if resp.WorkspaceID != nil && *resp.WorkspaceID != "" { - parsedId, err := logAnalyticsParse.LogAnalyticsWorkspaceID(*resp.WorkspaceID) - if err != nil { - return err - } + workspaceId := "" + if props.WorkspaceId != nil && *props.WorkspaceId != "" { + parsedId, err := workspaces.ParseWorkspaceID(*props.WorkspaceId) + if err != nil { + return err + } - workspaceId = parsedId.ID() - } - d.Set("log_analytics_workspace_id", workspaceId) + workspaceId = parsedId.ID() + } + d.Set("log_analytics_workspace_id", workspaceId) - storageAccountId := "" - if resp.StorageAccountID != nil && *resp.StorageAccountID != "" { - parsedId, err := storageParse.StorageAccountID(*resp.StorageAccountID) - if err != nil { - return err - } + storageAccountId := "" + if props.StorageAccountId != nil && *props.StorageAccountId != "" { + parsedId, err := storageParse.StorageAccountID(*props.StorageAccountId) + if err != nil { + return err + } - storageAccountId = parsedId.ID() - } - d.Set("storage_account_id", storageAccountId) + storageAccountId = parsedId.ID() + d.Set("storage_account_id", storageAccountId) + } - d.Set("log_analytics_destination_type", resp.LogAnalyticsDestinationType) + d.Set("log_analytics_destination_type", resp.Model.Properties.LogAnalyticsDestinationType) - if err := d.Set("log", flattenMonitorDiagnosticLogs(resp.Logs)); err != nil { - return fmt.Errorf("setting `log`: %+v", err) - } + if err := d.Set("log", flattenMonitorDiagnosticLogs(resp.Model.Properties.Logs)); err != nil { + return fmt.Errorf("setting `log`: %+v", err) + } - if err := d.Set("metric", flattenMonitorDiagnosticMetrics(resp.Metrics)); err != nil { - return fmt.Errorf("setting `metric`: %+v", err) + if err := d.Set("metric", flattenMonitorDiagnosticMetrics(resp.Model.Properties.Metrics)); err != nil { + return fmt.Errorf("setting `metric`: %+v", err) + } + } } return nil @@ -373,37 +381,36 @@ func resourceMonitorDiagnosticSettingDelete(d *pluginsdk.ResourceData, meta inte return err } - targetResourceId := strings.TrimPrefix(id.ResourceID, "/") - resp, err := client.Delete(ctx, targetResourceId, id.Name) + resp, err := client.Delete(ctx, *id) if err != nil { - if !response.WasNotFound(resp.Response) { - return fmt.Errorf("deleting Monitor Diagnostics Setting %q for Resource %q: %+v", id.Name, targetResourceId, err) + if !response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("deleting Monitor Diagnostics Setting %q for Resource %q: %+v", id.Name, id.ResourceUri, err) } } // API appears to be eventually consistent (identified during tainting this resource) - log.Printf("[DEBUG] Waiting for Monitor Diagnostic Setting %q for Resource %q to disappear", id.Name, id.ResourceID) + log.Printf("[DEBUG] Waiting for Monitor Diagnostic Setting %q for Resource %q to disappear", id.Name, id.ResourceUri) stateConf := &pluginsdk.StateChangeConf{ Pending: []string{"Exists"}, Target: []string{"NotFound"}, - Refresh: monitorDiagnosticSettingDeletedRefreshFunc(ctx, client, targetResourceId, id.Name), + Refresh: monitorDiagnosticSettingDeletedRefreshFunc(ctx, client, *id), MinTimeout: 15 * time.Second, ContinuousTargetOccurence: 5, Timeout: d.Timeout(pluginsdk.TimeoutDelete), } if _, err = stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for Monitor Diagnostic Setting %q for Resource %q to become available: %s", id.Name, id.ResourceID, err) + return fmt.Errorf("waiting for Monitor Diagnostic Setting %q for Resource %q to become available: %s", id.Name, id.ResourceUri, err) } return nil } -func monitorDiagnosticSettingDeletedRefreshFunc(ctx context.Context, client *insights.DiagnosticSettingsClient, targetResourceId string, name string) pluginsdk.StateRefreshFunc { +func monitorDiagnosticSettingDeletedRefreshFunc(ctx context.Context, client *diagnosticsettings.DiagnosticSettingsClient, targetResourceId diagnosticsettings.ScopedDiagnosticSettingId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, targetResourceId, name) + res, err := client.Get(ctx, targetResourceId) if err != nil { - if utils.ResponseWasNotFound(res.Response) { + if response.WasNotFound(res.HttpResponse) { return "NotFound", "NotFound", nil } return nil, "", fmt.Errorf("issuing read request in monitorDiagnosticSettingDeletedRefreshFunc: %s", err) @@ -413,31 +420,36 @@ func monitorDiagnosticSettingDeletedRefreshFunc(ctx context.Context, client *ins } } -func expandMonitorDiagnosticsSettingsLogs(input []interface{}) []insights.LogSettings { - results := make([]insights.LogSettings, 0) +func expandMonitorDiagnosticsSettingsLogs(input []interface{}) []diagnosticsettings.LogSettings { + results := make([]diagnosticsettings.LogSettings, 0) for _, raw := range input { v := raw.(map[string]interface{}) category := v["category"].(string) + categoryGroup := v["category_group"].(string) enabled := v["enabled"].(bool) policiesRaw := v["retention_policy"].([]interface{}) - var retentionPolicy *insights.RetentionPolicy + var retentionPolicy *diagnosticsettings.RetentionPolicy if len(policiesRaw) != 0 { policyRaw := policiesRaw[0].(map[string]interface{}) retentionDays := policyRaw["days"].(int) retentionEnabled := policyRaw["enabled"].(bool) - retentionPolicy = &insights.RetentionPolicy{ - Days: utils.Int32(int32(retentionDays)), - Enabled: utils.Bool(retentionEnabled), + retentionPolicy = &diagnosticsettings.RetentionPolicy{ + Days: int64(retentionDays), + Enabled: retentionEnabled, } } - output := insights.LogSettings{ - Category: utils.String(category), - Enabled: utils.Bool(enabled), + output := diagnosticsettings.LogSettings{ + Enabled: enabled, RetentionPolicy: retentionPolicy, } + if category != "" { + output.Category = utils.String(category) + } else { + output.CategoryGroup = utils.String(categoryGroup) + } results = append(results, output) } @@ -445,7 +457,7 @@ func expandMonitorDiagnosticsSettingsLogs(input []interface{}) []insights.LogSet return results } -func flattenMonitorDiagnosticLogs(input *[]insights.LogSettings) []interface{} { +func flattenMonitorDiagnosticLogs(input *[]diagnosticsettings.LogSettings) []interface{} { results := make([]interface{}, 0) if input == nil { return results @@ -458,22 +470,20 @@ func flattenMonitorDiagnosticLogs(input *[]insights.LogSettings) []interface{} { output["category"] = *v.Category } - if v.Enabled != nil { - output["enabled"] = *v.Enabled + if v.CategoryGroup != nil { + output["category_group"] = *v.CategoryGroup } + output["enabled"] = v.Enabled + policies := make([]interface{}, 0) if inputPolicy := v.RetentionPolicy; inputPolicy != nil { outputPolicy := make(map[string]interface{}) - if inputPolicy.Days != nil { - outputPolicy["days"] = int(*inputPolicy.Days) - } + outputPolicy["days"] = int(inputPolicy.Days) - if inputPolicy.Enabled != nil { - outputPolicy["enabled"] = *inputPolicy.Enabled - } + outputPolicy["enabled"] = inputPolicy.Enabled policies = append(policies, outputPolicy) } @@ -486,8 +496,8 @@ func flattenMonitorDiagnosticLogs(input *[]insights.LogSettings) []interface{} { return results } -func expandMonitorDiagnosticsSettingsMetrics(input []interface{}) []insights.MetricSettings { - results := make([]insights.MetricSettings, 0) +func expandMonitorDiagnosticsSettingsMetrics(input []interface{}) []diagnosticsettings.MetricSettings { + results := make([]diagnosticsettings.MetricSettings, 0) for _, raw := range input { v := raw.(map[string]interface{}) @@ -496,19 +506,19 @@ func expandMonitorDiagnosticsSettingsMetrics(input []interface{}) []insights.Met enabled := v["enabled"].(bool) policiesRaw := v["retention_policy"].([]interface{}) - var retentionPolicy *insights.RetentionPolicy + var retentionPolicy *diagnosticsettings.RetentionPolicy if len(policiesRaw) > 0 && policiesRaw[0] != nil { policyRaw := policiesRaw[0].(map[string]interface{}) retentionDays := policyRaw["days"].(int) retentionEnabled := policyRaw["enabled"].(bool) - retentionPolicy = &insights.RetentionPolicy{ - Days: utils.Int32(int32(retentionDays)), - Enabled: utils.Bool(retentionEnabled), + retentionPolicy = &diagnosticsettings.RetentionPolicy{ + Days: int64(retentionDays), + Enabled: retentionEnabled, } } - output := insights.MetricSettings{ + output := diagnosticsettings.MetricSettings{ Category: utils.String(category), - Enabled: utils.Bool(enabled), + Enabled: enabled, RetentionPolicy: retentionPolicy, } @@ -518,7 +528,7 @@ func expandMonitorDiagnosticsSettingsMetrics(input []interface{}) []insights.Met return results } -func flattenMonitorDiagnosticMetrics(input *[]insights.MetricSettings) []interface{} { +func flattenMonitorDiagnosticMetrics(input *[]diagnosticsettings.MetricSettings) []interface{} { results := make([]interface{}, 0) if input == nil { return results @@ -531,22 +541,16 @@ func flattenMonitorDiagnosticMetrics(input *[]insights.MetricSettings) []interfa output["category"] = *v.Category } - if v.Enabled != nil { - output["enabled"] = *v.Enabled - } + output["enabled"] = v.Enabled policies := make([]interface{}, 0) if inputPolicy := v.RetentionPolicy; inputPolicy != nil { outputPolicy := make(map[string]interface{}) - if inputPolicy.Days != nil { - outputPolicy["days"] = int(*inputPolicy.Days) - } + outputPolicy["days"] = int(inputPolicy.Days) - if inputPolicy.Enabled != nil { - outputPolicy["enabled"] = *inputPolicy.Enabled - } + outputPolicy["enabled"] = inputPolicy.Enabled policies = append(policies, outputPolicy) } @@ -559,20 +563,60 @@ func flattenMonitorDiagnosticMetrics(input *[]insights.MetricSettings) []interfa return results } -type monitorDiagnosticId struct { - ResourceID string - Name string -} - -func ParseMonitorDiagnosticId(monitorId string) (*monitorDiagnosticId, error) { +func ParseMonitorDiagnosticId(monitorId string) (*diagnosticsettings.ScopedDiagnosticSettingId, error) { v := strings.Split(monitorId, "|") if len(v) != 2 { return nil, fmt.Errorf("Expected the Monitor Diagnostics ID to be in the format `{resourceId}|{name}` but got %d segments", len(v)) } - identifier := monitorDiagnosticId{ - ResourceID: v[0], - Name: v[1], + identifier := diagnosticsettings.ScopedDiagnosticSettingId{ + ResourceUri: v[0], + Name: v[1], } return &identifier, nil } + +func resourceMonitorDiagnosticLogSettingHash(input interface{}) int { + var buf bytes.Buffer + if rawData, ok := input.(map[string]interface{}); ok { + if category, ok := rawData["category"]; ok { + buf.WriteString(fmt.Sprintf("%s-", category.(string))) + } + if categoryGroup, ok := rawData["category_group"]; ok { + buf.WriteString(fmt.Sprintf("%s-", categoryGroup.(string))) + } + if enabled, ok := rawData["enabled"]; ok { + buf.WriteString(fmt.Sprintf("%t-", enabled.(bool))) + } + if policy, ok := rawData["retention_policy"].(map[string]interface{}); ok { + if policyEnabled, ok := policy["enabled"]; ok { + buf.WriteString(fmt.Sprintf("%t-", policyEnabled.(bool))) + } + if days, ok := policy["days"]; ok { + buf.WriteString(fmt.Sprintf("%d-", days.(int))) + } + } + } + return pluginsdk.HashString(buf.String()) +} + +func resourceMonitorDiagnosticMetricsSettingHash(input interface{}) int { + var buf bytes.Buffer + if rawData, ok := input.(map[string]interface{}); ok { + if category, ok := rawData["category"]; ok { + buf.WriteString(fmt.Sprintf("%s-", category.(string))) + } + if enabled, ok := rawData["enabled"]; ok { + buf.WriteString(fmt.Sprintf("%t-", enabled.(bool))) + } + if policy, ok := rawData["retention_policy"].(map[string]interface{}); ok { + if policyEnabled, ok := policy["enabled"]; ok { + buf.WriteString(fmt.Sprintf("%t-", policyEnabled.(bool))) + } + if days, ok := policy["days"]; ok { + buf.WriteString(fmt.Sprintf("%d-", days.(int))) + } + } + } + return pluginsdk.HashString(buf.String()) +} diff --git a/internal/services/monitor/monitor_diagnostic_setting_resource_test.go b/internal/services/monitor/monitor_diagnostic_setting_resource_test.go index b0d910a18aa1..6b815a82b277 100644 --- a/internal/services/monitor/monitor_diagnostic_setting_resource_test.go +++ b/internal/services/monitor/monitor_diagnostic_setting_resource_test.go @@ -3,7 +3,6 @@ package monitor_test import ( "context" "fmt" - "strings" "testing" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" @@ -34,6 +33,24 @@ func TestAccMonitorDiagnosticSetting_eventhub(t *testing.T) { }) } +func TestAccMonitorDiagnosticSetting_CategoryGroup(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_diagnostic_setting", "test") + r := MonitorDiagnosticSettingResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.categoryGroup(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("eventhub_name").Exists(), + check.That(data.ResourceName).Key("eventhub_authorization_rule_id").Exists(), + check.That(data.ResourceName).Key("log.#").HasValue("2"), + check.That(data.ResourceName).Key("metric.#").HasValue("1"), + ), + }, + data.ImportStep(), + }) +} + func TestAccMonitorDiagnosticSetting_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_monitor_diagnostic_setting", "test") r := MonitorDiagnosticSettingResource{} @@ -123,15 +140,15 @@ func (t MonitorDiagnosticSettingResource) Exists(ctx context.Context, clients *c if err != nil { return nil, err } - actualResourceId := id.ResourceID - targetResourceId := strings.TrimPrefix(actualResourceId, "/") + // actualResourceId := id.ResourceUri + // targetResourceId := strings.TrimPrefix(actualResourceId, "/") - resp, err := clients.Monitor.DiagnosticSettingsClient.Get(ctx, targetResourceId, id.Name) + resp, err := clients.Monitor.DiagnosticSettingsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading diagnostic setting (%s): %+v", id, err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil && resp.Model.Id != nil), nil } func (MonitorDiagnosticSettingResource) eventhub(data acceptance.TestData) string { @@ -218,6 +235,88 @@ resource "azurerm_monitor_diagnostic_setting" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomIntOfLength(17)) } +func (MonitorDiagnosticSettingResource) categoryGroup(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_eventhub_namespace" "test" { + name = "acctest-EHN-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Basic" +} + +resource "azurerm_eventhub" "test" { + name = "acctest-EH-%[1]d" + namespace_name = azurerm_eventhub_namespace.test.name + resource_group_name = azurerm_resource_group.test.name + partition_count = 2 + message_retention = 1 +} + +resource "azurerm_eventhub_namespace_authorization_rule" "test" { + name = "example" + namespace_name = azurerm_eventhub_namespace.test.name + resource_group_name = azurerm_resource_group.test.name + listen = true + send = true + manage = true +} + +resource "azurerm_key_vault" "test" { + name = "acctest%[3]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 = "standard" +} + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "acctest-DS-%[1]d" + target_resource_id = azurerm_key_vault.test.id + eventhub_authorization_rule_id = azurerm_eventhub_namespace_authorization_rule.test.id + eventhub_name = azurerm_eventhub.test.name + + log { + category_group = "Audit" + enabled = true + + retention_policy { + days = 0 + enabled = false + } + } + + log { + category_group = "allLogs" + enabled = false + + retention_policy { + days = 0 + enabled = false + } + } + + metric { + category = "AllMetrics" + + retention_policy { + enabled = false + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomIntOfLength(17)) +} func (r MonitorDiagnosticSettingResource) requiresImport(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -436,56 +535,6 @@ resource "azurerm_monitor_diagnostic_setting" "test" { } } - log { - category = "AirflowDagProcessingLogs" - enabled = false - - retention_policy { - days = 0 - enabled = false - } - } - - log { - category = "AirflowSchedulerLogs" - enabled = false - - retention_policy { - days = 0 - enabled = false - } - } - - log { - category = "AirflowTaskLogs" - enabled = false - - retention_policy { - days = 0 - enabled = false - } - } - - log { - category = "AirflowWebLogs" - enabled = false - - retention_policy { - days = 0 - enabled = false - } - } - - log { - category = "AirflowWorkerLogs" - enabled = false - - retention_policy { - days = 0 - enabled = false - } - } - metric { category = "AllMetrics" retention_policy { diff --git a/internal/services/monitor/monitor_private_link_scoped_service_resource.go b/internal/services/monitor/monitor_private_link_scoped_service_resource.go index ad289416f588..2c708e4699a1 100644 --- a/internal/services/monitor/monitor_private_link_scoped_service_resource.go +++ b/internal/services/monitor/monitor_private_link_scoped_service_resource.go @@ -6,11 +6,11 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-07-01-preview/insights" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" applicationinsightsvalidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/applicationinsights/validate" - loganalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -62,7 +62,7 @@ func resourceMonitorPrivateLinkScopedService() *pluginsdk.Resource { DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.Any( applicationinsightsvalidate.ComponentID, - loganalyticsValidate.LogAnalyticsWorkspaceID, + workspaces.ValidateWorkspaceID, ), }, }, diff --git a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go new file mode 100644 index 000000000000..5420002f3da4 --- /dev/null +++ b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go @@ -0,0 +1,895 @@ +package monitor + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-08-01/scheduledqueryrules" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + helperValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type ScheduledQueryRulesAlertV2Model struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Actions []ScheduledQueryRulesAlertV2ActionsModel `tfschema:"action"` + AutoMitigate bool `tfschema:"auto_mitigation_enabled"` + CheckWorkspaceAlertsStorageConfigured bool `tfschema:"workspace_alerts_storage_enabled"` + Criteria []ScheduledQueryRulesAlertV2CriteriaModel `tfschema:"criteria"` + Description string `tfschema:"description"` + DisplayName string `tfschema:"display_name"` + Enabled bool `tfschema:"enabled"` + EvaluationFrequency string `tfschema:"evaluation_frequency"` + Location string `tfschema:"location"` + MuteActionsDuration string `tfschema:"mute_actions_after_alert_duration"` + OverrideQueryTimeRange string `tfschema:"query_time_range_override"` + Scopes []string `tfschema:"scopes"` + Severity scheduledqueryrules.AlertSeverity `tfschema:"severity"` + SkipQueryValidation bool `tfschema:"skip_query_validation"` + Tags map[string]string `tfschema:"tags"` + TargetResourceTypes []string `tfschema:"target_resource_types"` + WindowSize string `tfschema:"window_duration"` + CreatedWithApiVersion string `tfschema:"created_with_api_version"` + IsLegacyLogAnalyticsRule bool `tfschema:"is_a_legacy_log_analytics_rule"` + IsWorkspaceAlertsStorageConfigured bool `tfschema:"is_workspace_alerts_storage_configured"` +} + +type ScheduledQueryRulesAlertV2ActionsModel struct { + ActionGroups []string `tfschema:"action_groups"` + CustomProperties map[string]string `tfschema:"custom_properties"` +} + +type ScheduledQueryRulesAlertV2CriteriaModel struct { + Dimensions []ScheduledQueryRulesAlertV2DimensionModel `tfschema:"dimension"` + FailingPeriods []ScheduledQueryRulesAlertV2FailingPeriodsModel `tfschema:"failing_periods"` + MetricMeasureColumn string `tfschema:"metric_measure_column"` + Operator scheduledqueryrules.ConditionOperator `tfschema:"operator"` + Query string `tfschema:"query"` + ResourceIdColumn string `tfschema:"resource_id_column"` + Threshold float64 `tfschema:"threshold"` + TimeAggregation scheduledqueryrules.TimeAggregation `tfschema:"time_aggregation_method"` +} + +type ScheduledQueryRulesAlertV2DimensionModel struct { + Name string `tfschema:"name"` + Operator scheduledqueryrules.DimensionOperator `tfschema:"operator"` + Values []string `tfschema:"values"` +} + +type ScheduledQueryRulesAlertV2FailingPeriodsModel struct { + MinFailingPeriodsToAlert int64 `tfschema:"minimum_failing_periods_to_trigger_alert"` + NumberOfEvaluationPeriods int64 `tfschema:"number_of_evaluation_periods"` +} + +type ScheduledQueryRulesAlertV2Resource struct{} + +var _ sdk.ResourceWithUpdate = ScheduledQueryRulesAlertV2Resource{} + +func (r ScheduledQueryRulesAlertV2Resource) ResourceType() string { + return "azurerm_monitor_scheduled_query_rules_alert_v2" +} + +func (r ScheduledQueryRulesAlertV2Resource) ModelObject() interface{} { + return &ScheduledQueryRulesAlertV2Model{} +} + +func (r ScheduledQueryRulesAlertV2Resource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return scheduledqueryrules.ValidateScheduledQueryRuleID +} + +func (r ScheduledQueryRulesAlertV2Resource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "criteria": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + + Schema: map[string]*pluginsdk.Schema{ + "query": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(scheduledqueryrules.ConditionOperatorEquals), + string(scheduledqueryrules.ConditionOperatorGreaterThan), + string(scheduledqueryrules.ConditionOperatorGreaterThanOrEqual), + string(scheduledqueryrules.ConditionOperatorLessThan), + string(scheduledqueryrules.ConditionOperatorLessThanOrEqual), + }, false), + }, + + "time_aggregation_method": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(scheduledqueryrules.TimeAggregationCount), + string(scheduledqueryrules.TimeAggregationAverage), + string(scheduledqueryrules.TimeAggregationMinimum), + string(scheduledqueryrules.TimeAggregationMaximum), + string(scheduledqueryrules.TimeAggregationTotal), + }, false), + }, + + "threshold": { + Type: pluginsdk.TypeFloat, + Required: true, + }, + + "dimension": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "operator": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(scheduledqueryrules.DimensionOperatorInclude), + string(scheduledqueryrules.DimensionOperatorExclude), + }, false), + }, + + "values": { + Type: pluginsdk.TypeList, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + + "failing_periods": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "minimum_failing_periods_to_trigger_alert": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 6), + }, + + "number_of_evaluation_periods": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 6), + }, + }, + }, + }, + + "metric_measure_column": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_id_column": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "evaluation_frequency": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: helperValidate.ISO8601Duration, + }, + + "scopes": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + ForceNew: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: azure.ValidateResourceID, + }, + }, + + "severity": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 4), + }, + + "window_duration": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: helperValidate.ISO8601Duration, + }, + + "action": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "action_groups": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: azure.ValidateResourceID, + }, + }, + + "custom_properties": { + Type: pluginsdk.TypeMap, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + + "auto_mitigation_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "workspace_alerts_storage_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "display_name": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "mute_actions_after_alert_duration": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: helperValidate.ISO8601Duration, + }, + + "query_time_range_override": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: helperValidate.ISO8601Duration, + }, + + "skip_query_validation": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "tags": commonschema.Tags(), + + "target_resource_types": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + } +} + +func (r ScheduledQueryRulesAlertV2Resource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "created_with_api_version": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "is_a_legacy_log_analytics_rule": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "is_workspace_alerts_storage_configured": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + } +} + +func (r ScheduledQueryRulesAlertV2Resource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model ScheduledQueryRulesAlertV2Model + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.Monitor.ScheduledQueryRulesV2Client + subscriptionId := metadata.Client.Account.SubscriptionId + id := scheduledqueryrules.NewScheduledQueryRuleID(subscriptionId, model.ResourceGroupName, model.Name) + existing, err := client.Get(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + kind := scheduledqueryrules.KindLogAlert + properties := &scheduledqueryrules.ScheduledQueryRuleResource{ + Kind: &kind, + Location: location.Normalize(model.Location), + Properties: scheduledqueryrules.ScheduledQueryRuleProperties{ + AutoMitigate: &model.AutoMitigate, + CheckWorkspaceAlertsStorageConfigured: &model.CheckWorkspaceAlertsStorageConfigured, + Enabled: &model.Enabled, + Scopes: &model.Scopes, + Severity: &model.Severity, + SkipQueryValidation: &model.SkipQueryValidation, + TargetResourceTypes: &model.TargetResourceTypes, + }, + Tags: &model.Tags, + } + + actionsValue, err := expandScheduledQueryRulesAlertV2ActionsModel(model.Actions) + if err != nil { + return err + } + + properties.Properties.Actions = actionsValue + + criteriaValue, err := expandScheduledQueryRulesAlertV2CriteriaModel(model.Criteria) + if err != nil { + return err + } + + properties.Properties.Criteria = criteriaValue + + if model.Description != "" { + properties.Properties.Description = &model.Description + } + + if model.DisplayName != "" { + properties.Properties.DisplayName = &model.DisplayName + } + + if model.EvaluationFrequency != "" { + properties.Properties.EvaluationFrequency = &model.EvaluationFrequency + } + + if model.MuteActionsDuration != "" { + if model.AutoMitigate { + return fmt.Errorf("auto mitigation must be disabled when mute action duration is set") + } + properties.Properties.MuteActionsDuration = &model.MuteActionsDuration + } + + if model.OverrideQueryTimeRange != "" { + properties.Properties.OverrideQueryTimeRange = &model.OverrideQueryTimeRange + } + + if model.WindowSize != "" { + properties.Properties.WindowSize = &model.WindowSize + } + + if _, err := client.CreateOrUpdate(ctx, id, *properties); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r ScheduledQueryRulesAlertV2Resource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.ScheduledQueryRulesV2Client + + id, err := scheduledqueryrules.ParseScheduledQueryRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var resourceModel ScheduledQueryRulesAlertV2Model + if err := metadata.Decode(&resourceModel); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + if metadata.ResourceData.HasChange("action") { + actionsValue, err := expandScheduledQueryRulesAlertV2ActionsModel(resourceModel.Actions) + if err != nil { + return err + } + + model.Properties.Actions = actionsValue + } + + if metadata.ResourceData.HasChange("auto_mitigation_enabled") { + model.Properties.AutoMitigate = &resourceModel.AutoMitigate + } + + if metadata.ResourceData.HasChange("workspace_alerts_storage_enabled") { + model.Properties.CheckWorkspaceAlertsStorageConfigured = &resourceModel.CheckWorkspaceAlertsStorageConfigured + } + + if metadata.ResourceData.HasChange("criteria") { + criteriaValue, err := expandScheduledQueryRulesAlertV2CriteriaModel(resourceModel.Criteria) + if err != nil { + return err + } + + model.Properties.Criteria = criteriaValue + } + + if metadata.ResourceData.HasChange("description") { + model.Properties.Description = &resourceModel.Description + } + + if metadata.ResourceData.HasChange("display_name") { + if resourceModel.DisplayName != "" { + model.Properties.DisplayName = &resourceModel.DisplayName + } else { + model.Properties.DisplayName = nil + } + } + + if metadata.ResourceData.HasChange("enabled") { + model.Properties.Enabled = &resourceModel.Enabled + } + + if metadata.ResourceData.HasChange("evaluation_frequency") { + model.Properties.EvaluationFrequency = &resourceModel.EvaluationFrequency + } + + if metadata.ResourceData.HasChange("mute_actions_after_alert_duration") { + if resourceModel.MuteActionsDuration != "" { + if resourceModel.AutoMitigate { + return fmt.Errorf("auto mitigation must be disabled when mute action duration is set") + } + model.Properties.MuteActionsDuration = &resourceModel.MuteActionsDuration + } else { + model.Properties.MuteActionsDuration = nil + } + } + + if metadata.ResourceData.HasChange("query_time_range_override") { + if resourceModel.OverrideQueryTimeRange != "" { + model.Properties.OverrideQueryTimeRange = &resourceModel.OverrideQueryTimeRange + } else { + model.Properties.OverrideQueryTimeRange = nil + } + } + + if metadata.ResourceData.HasChange("severity") { + model.Properties.Severity = &resourceModel.Severity + } + + if metadata.ResourceData.HasChange("skip_query_validation") { + model.Properties.SkipQueryValidation = &resourceModel.SkipQueryValidation + } + + if metadata.ResourceData.HasChange("target_resource_types") { + model.Properties.TargetResourceTypes = &resourceModel.TargetResourceTypes + } + + if metadata.ResourceData.HasChange("window_duration") { + if resourceModel.WindowSize != "" { + model.Properties.WindowSize = &resourceModel.WindowSize + } else { + model.Properties.WindowSize = nil + } + } + + model.SystemData = nil + + if metadata.ResourceData.HasChange("tags") { + model.Tags = &resourceModel.Tags + } + + if _, err := client.CreateOrUpdate(ctx, *id, *model); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r ScheduledQueryRulesAlertV2Resource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.ScheduledQueryRulesV2Client + + id, err := scheduledqueryrules.ParseScheduledQueryRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + state := ScheduledQueryRulesAlertV2Model{ + Name: id.RuleName, + ResourceGroupName: id.ResourceGroupName, + Location: location.Normalize(model.Location), + } + + properties := &model.Properties + actionsValue, err := flattenScheduledQueryRulesAlertV2ActionsModel(properties.Actions) + if err != nil { + return err + } + + state.Actions = actionsValue + + if properties.AutoMitigate != nil { + state.AutoMitigate = *properties.AutoMitigate + } + + if properties.CheckWorkspaceAlertsStorageConfigured != nil { + state.CheckWorkspaceAlertsStorageConfigured = *properties.CheckWorkspaceAlertsStorageConfigured + } + + if properties.CreatedWithApiVersion != nil { + state.CreatedWithApiVersion = *properties.CreatedWithApiVersion + } + + criteriaValue, err := flattenScheduledQueryRulesAlertV2CriteriaModel(properties.Criteria) + if err != nil { + return err + } + + state.Criteria = criteriaValue + + if properties.Description != nil { + state.Description = *properties.Description + } + + if properties.DisplayName != nil { + state.DisplayName = *properties.DisplayName + } + + if properties.Enabled != nil { + state.Enabled = *properties.Enabled + } + + if properties.EvaluationFrequency != nil { + state.EvaluationFrequency = *properties.EvaluationFrequency + } + + if properties.IsLegacyLogAnalyticsRule != nil { + state.IsLegacyLogAnalyticsRule = *properties.IsLegacyLogAnalyticsRule + } + + if properties.IsWorkspaceAlertsStorageConfigured != nil { + state.IsWorkspaceAlertsStorageConfigured = *properties.IsWorkspaceAlertsStorageConfigured + } + + if properties.MuteActionsDuration != nil { + state.MuteActionsDuration = *properties.MuteActionsDuration + } + + if properties.OverrideQueryTimeRange != nil { + state.OverrideQueryTimeRange = *properties.OverrideQueryTimeRange + } + + if properties.Scopes != nil { + state.Scopes = *properties.Scopes + } + + if properties.Severity != nil { + state.Severity = *properties.Severity + } + + if properties.SkipQueryValidation != nil { + state.SkipQueryValidation = *properties.SkipQueryValidation + } + + if properties.TargetResourceTypes != nil { + state.TargetResourceTypes = *properties.TargetResourceTypes + } + + if properties.WindowSize != nil { + state.WindowSize = *properties.WindowSize + } + if model.Tags != nil { + state.Tags = *model.Tags + } + + return metadata.Encode(&state) + }, + } +} + +func (r ScheduledQueryRulesAlertV2Resource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Monitor.ScheduledQueryRulesV2Client + + id, err := scheduledqueryrules.ParseScheduledQueryRuleID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} + +func expandScheduledQueryRulesAlertV2ActionsModel(inputList []ScheduledQueryRulesAlertV2ActionsModel) (*scheduledqueryrules.Actions, error) { + if len(inputList) == 0 { + return nil, nil + } + + input := &inputList[0] + output := scheduledqueryrules.Actions{ + ActionGroups: &input.ActionGroups, + CustomProperties: &input.CustomProperties, + } + + return &output, nil +} + +func expandScheduledQueryRulesAlertV2CriteriaModel(inputList []ScheduledQueryRulesAlertV2CriteriaModel) (*scheduledqueryrules.ScheduledQueryRuleCriteria, error) { + output := scheduledqueryrules.ScheduledQueryRuleCriteria{} + var outputList []scheduledqueryrules.Condition + for _, v := range inputList { + input := v + condition := scheduledqueryrules.Condition{ + Operator: &input.Operator, + Threshold: &input.Threshold, + TimeAggregation: &input.TimeAggregation, + } + + dimensionsValue, err := expandScheduledQueryRulesAlertV2DimensionModel(input.Dimensions) + if err != nil { + return nil, err + } + + condition.Dimensions = dimensionsValue + + failingPeriodsValue, err := expandScheduledQueryRulesAlertV2FailingPeriodsModel(input.FailingPeriods) + if err != nil { + return nil, err + } + + condition.FailingPeriods = failingPeriodsValue + + if input.MetricMeasureColumn != "" { + condition.MetricMeasureColumn = &input.MetricMeasureColumn + } + + if input.Query != "" { + condition.Query = &input.Query + } + + if input.ResourceIdColumn != "" { + condition.ResourceIdColumn = &input.ResourceIdColumn + } + + outputList = append(outputList, condition) + } + output.AllOf = &outputList + return &output, nil +} + +func expandScheduledQueryRulesAlertV2DimensionModel(inputList []ScheduledQueryRulesAlertV2DimensionModel) (*[]scheduledqueryrules.Dimension, error) { + var outputList []scheduledqueryrules.Dimension + for _, v := range inputList { + input := v + output := scheduledqueryrules.Dimension{ + Name: input.Name, + Operator: input.Operator, + Values: input.Values, + } + + outputList = append(outputList, output) + } + + return &outputList, nil +} + +func expandScheduledQueryRulesAlertV2FailingPeriodsModel(inputList []ScheduledQueryRulesAlertV2FailingPeriodsModel) (*scheduledqueryrules.ConditionFailingPeriods, error) { + if len(inputList) == 0 { + return nil, nil + } + + input := &inputList[0] + output := scheduledqueryrules.ConditionFailingPeriods{ + MinFailingPeriodsToAlert: &input.MinFailingPeriodsToAlert, + NumberOfEvaluationPeriods: &input.NumberOfEvaluationPeriods, + } + + return &output, nil +} + +func flattenScheduledQueryRulesAlertV2ActionsModel(input *scheduledqueryrules.Actions) ([]ScheduledQueryRulesAlertV2ActionsModel, error) { + var outputList []ScheduledQueryRulesAlertV2ActionsModel + if input == nil { + return outputList, nil + } + + output := ScheduledQueryRulesAlertV2ActionsModel{} + + if input.ActionGroups != nil { + output.ActionGroups = *input.ActionGroups + } + + if input.CustomProperties != nil { + output.CustomProperties = *input.CustomProperties + } + + return append(outputList, output), nil +} + +func flattenScheduledQueryRulesAlertV2CriteriaModel(input *scheduledqueryrules.ScheduledQueryRuleCriteria) ([]ScheduledQueryRulesAlertV2CriteriaModel, error) { + var outputList []ScheduledQueryRulesAlertV2CriteriaModel + if input == nil { + return outputList, nil + } + + inputList := input.AllOf + if inputList == nil { + return outputList, nil + } + + for _, v := range *inputList { + output := ScheduledQueryRulesAlertV2CriteriaModel{} + + dimensionsValue, err := flattenScheduledQueryRulesAlertV2DimensionModel(v.Dimensions) + if err != nil { + return nil, err + } + + output.Dimensions = dimensionsValue + + failingPeriodsValue, err := flattenScheduledQueryRulesAlertV2FailingPeriodsModel(v.FailingPeriods) + if err != nil { + return nil, err + } + + output.FailingPeriods = failingPeriodsValue + + if v.MetricMeasureColumn != nil { + output.MetricMeasureColumn = *v.MetricMeasureColumn + } + + if v.Operator != nil { + output.Operator = *v.Operator + } + + if v.Query != nil { + output.Query = *v.Query + } + + if v.ResourceIdColumn != nil { + output.ResourceIdColumn = *v.ResourceIdColumn + } + + if v.Threshold != nil { + output.Threshold = *v.Threshold + } + + if v.TimeAggregation != nil { + output.TimeAggregation = *v.TimeAggregation + } + + outputList = append(outputList, output) + } + + return outputList, nil +} + +func flattenScheduledQueryRulesAlertV2DimensionModel(inputList *[]scheduledqueryrules.Dimension) ([]ScheduledQueryRulesAlertV2DimensionModel, error) { + var outputList []ScheduledQueryRulesAlertV2DimensionModel + if inputList == nil { + return outputList, nil + } + + for _, input := range *inputList { + output := ScheduledQueryRulesAlertV2DimensionModel{ + Name: input.Name, + Operator: input.Operator, + Values: input.Values, + } + + outputList = append(outputList, output) + } + + return outputList, nil +} + +func flattenScheduledQueryRulesAlertV2FailingPeriodsModel(input *scheduledqueryrules.ConditionFailingPeriods) ([]ScheduledQueryRulesAlertV2FailingPeriodsModel, error) { + var outputList []ScheduledQueryRulesAlertV2FailingPeriodsModel + if input == nil { + return outputList, nil + } + + output := ScheduledQueryRulesAlertV2FailingPeriodsModel{} + + if input.MinFailingPeriodsToAlert != nil { + output.MinFailingPeriodsToAlert = *input.MinFailingPeriodsToAlert + } + + if input.NumberOfEvaluationPeriods != nil { + output.NumberOfEvaluationPeriods = *input.NumberOfEvaluationPeriods + } + + return append(outputList, output), nil +} diff --git a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go new file mode 100644 index 000000000000..4d120a67d6c7 --- /dev/null +++ b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go @@ -0,0 +1,296 @@ +package monitor_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2021-08-01/scheduledqueryrules" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type MonitorScheduledQueryRulesAlertV2Resource struct{} + +func TestAccMonitorScheduledQueryRulesAlertV2_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorScheduledQueryRulesAlertV2_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccMonitorScheduledQueryRulesAlertV2_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorScheduledQueryRulesAlertV2_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := scheduledqueryrules.ParseScheduledQueryRuleID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Monitor.ScheduledQueryRulesV2Client + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(resp.Model != nil), nil +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%d" + location = "%s" +} + +resource "azurerm_application_insights" "test" { + name = "acctest-ai-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_monitor_action_group" "test" { + name = "acctest-mag-%[1]d" + resource_group_name = azurerm_resource_group.test.name + short_name = "test mag" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { + name = "acctest-isqr-%d" + resource_group_name = azurerm_resource_group.test.name + location = "%s" + evaluation_frequency = "PT5M" + window_duration = "PT5M" + scopes = [azurerm_application_insights.test.id] + severity = 3 + criteria { + query = <<-QUERY + requests + | summarize CountByCountry=count() by client_CountryOrRegion + QUERY + time_aggregation_method = "Count" + threshold = 5.0 + operator = "GreaterThan" + } +} +`, template, data.RandomInteger, data.Locations.Primary) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "import" { + name = azurerm_monitor_scheduled_query_rules_alert_v2.test.name + resource_group_name = azurerm_resource_group.test.name + location = "%s" + evaluation_frequency = azurerm_monitor_scheduled_query_rules_alert_v2.test.evaluation_frequency + window_duration = azurerm_monitor_scheduled_query_rules_alert_v2.test.window_duration + scopes = azurerm_monitor_scheduled_query_rules_alert_v2.test.scopes + severity = azurerm_monitor_scheduled_query_rules_alert_v2.test.severity + criteria { + query = azurerm_monitor_scheduled_query_rules_alert_v2.test.criteria.0.query + time_aggregation_method = azurerm_monitor_scheduled_query_rules_alert_v2.test.criteria.0.time_aggregation_method + threshold = azurerm_monitor_scheduled_query_rules_alert_v2.test.criteria.0.threshold + operator = azurerm_monitor_scheduled_query_rules_alert_v2.test.criteria.0.operator + } +} +`, config, data.Locations.Primary) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { + name = "acctest-isqr-%d" + resource_group_name = azurerm_resource_group.test.name + location = "%s" + + evaluation_frequency = "PT5M" + window_duration = "PT5M" + scopes = [azurerm_application_insights.test.id] + severity = 3 + criteria { + query = <<-QUERY + requests + | summarize CountByCountry=count() by client_CountryOrRegion + QUERY + time_aggregation_method = "Count" + threshold = 5.0 + operator = "GreaterThan" + + resource_id_column = "client_CountryOrRegion" + dimension { + name = "client_CountryOrRegion" + operator = "Include" + values = ["*"] + } + failing_periods { + minimum_failing_periods_to_trigger_alert = 1 + number_of_evaluation_periods = 1 + } + } + + auto_mitigation_enabled = false + workspace_alerts_storage_enabled = false + description = "test sqr" + display_name = "acctest-sqr" + enabled = false + mute_actions_after_alert_duration = "PT10M" + query_time_range_override = "PT10M" + skip_query_validation = false + target_resource_types = ["microsoft.insights/components"] + action { + action_groups = [azurerm_monitor_action_group.test.id] + custom_properties = { + key = "value" + } + } + + tags = { + key = "value" + } +} +`, template, data.RandomInteger, data.Locations.Primary) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { + name = "acctest-isqr-%d" + resource_group_name = azurerm_resource_group.test.name + location = "%s" + + evaluation_frequency = "PT10M" + window_duration = "PT10M" + scopes = [azurerm_application_insights.test.id] + severity = 4 + criteria { + query = <<-QUERY + requests + | summarize CountByCountry=count() by client_CountryOrRegion + QUERY + time_aggregation_method = "Maximum" + threshold = 17.5 + operator = "LessThan" + + resource_id_column = "client_CountryOrRegion" + metric_measure_column = "CountByCountry" + dimension { + name = "client_CountryOrRegion" + operator = "Exclude" + values = ["123"] + } + failing_periods { + minimum_failing_periods_to_trigger_alert = 1 + number_of_evaluation_periods = 1 + } + } + + auto_mitigation_enabled = true + workspace_alerts_storage_enabled = false + description = "test sqr" + display_name = "acctest-sqr" + enabled = true + query_time_range_override = "PT1H" + skip_query_validation = true + action { + action_groups = [azurerm_monitor_action_group.test.id] + custom_properties = { + key = "value" + key2 = "value2" + } + } + + tags = { + key = "value" + key2 = "value2" + } +} +`, template, data.RandomInteger, data.Locations.Primary) +} diff --git a/internal/services/monitor/registration.go b/internal/services/monitor/registration.go index f908adc25252..bae5889b4feb 100644 --- a/internal/services/monitor/registration.go +++ b/internal/services/monitor/registration.go @@ -7,12 +7,29 @@ import ( type Registration struct{} -var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +var ( + _ sdk.TypedServiceRegistration = Registration{} + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +) func (r Registration) AssociatedGitHubLabel() string { return "service/monitor" } +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{ + DataCollectionEndpointDataSource{}, + } +} + +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + DataCollectionEndpointResource{}, + DataCollectionRuleResource{}, + ScheduledQueryRulesAlertV2Resource{}, + } +} + // Name is the name of this Service func (r Registration) Name() string { return "Monitor" diff --git a/internal/services/msi/client/client.go b/internal/services/msi/client/client.go index 587e7303a216..83207868b8b5 100644 --- a/internal/services/msi/client/client.go +++ b/internal/services/msi/client/client.go @@ -1,16 +1,16 @@ package client import ( - "github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2018-11-30/managedidentity" + "github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2018-11-30/managedidentities" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - UserAssignedIdentitiesClient *managedidentity.ManagedIdentityClient + UserAssignedIdentitiesClient *managedidentities.ManagedIdentitiesClient } func NewClient(o *common.ClientOptions) *Client { - userAssignedIdentitiesClient := managedidentity.NewManagedIdentityClientWithBaseURI(o.ResourceManagerEndpoint) + userAssignedIdentitiesClient := managedidentities.NewManagedIdentitiesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&userAssignedIdentitiesClient.Client, o.ResourceManagerAuthorizer) return &Client{ diff --git a/internal/services/msi/user_assigned_identity_resource.go b/internal/services/msi/user_assigned_identity_resource.go index 33dd68ee36c0..1b05a86330a2 100644 --- a/internal/services/msi/user_assigned_identity_resource.go +++ b/internal/services/msi/user_assigned_identity_resource.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" - "github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2018-11-30/managedidentity" + "github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2018-11-30/managedidentities" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/msi/migration" @@ -99,7 +99,7 @@ func resourceArmUserAssignedIdentityCreateUpdate(d *pluginsdk.ResourceData, meta } } - identity := managedidentity.Identity{ + identity := managedidentities.Identity{ Name: utils.String(resourceId.ResourceName), Location: location.Normalize(d.Get("location").(string)), Tags: tags.Expand(t), diff --git a/internal/services/mssql/mssql_elasticpool_resource.go b/internal/services/mssql/mssql_elasticpool_resource.go index c1bf7087513a..9d0e298890f2 100644 --- a/internal/services/mssql/mssql_elasticpool_resource.go +++ b/internal/services/mssql/mssql_elasticpool_resource.go @@ -8,6 +8,7 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v5.0/sql" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/publicmaintenanceconfigurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -114,6 +115,20 @@ func resourceMsSqlElasticPool() *pluginsdk.Resource { }, }, + "maintenance_configuration_name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "SQL_Default", + ValidateFunc: validation.StringInSlice([]string{"SQL_Default", "SQL_EastUS_DB_1", "SQL_EastUS2_DB_1", "SQL_SoutheastAsia_DB_1", "SQL_AustraliaEast_DB_1", "SQL_NorthEurope_DB_1", "SQL_SouthCentralUS_DB_1", + "SQL_WestUS2_DB_1", "SQL_UKSouth_DB_1", "SQL_WestEurope_DB_1", "SQL_EastUS_DB_2", "SQL_EastUS2_DB_2", "SQL_WestUS2_DB_2", "SQL_SoutheastAsia_DB_2", "SQL_AustraliaEast_DB_2", "SQL_NorthEurope_DB_2", + "SQL_SouthCentralUS_DB_2", "SQL_UKSouth_DB_2", "SQL_WestEurope_DB_2", "SQL_AustraliaSoutheast_DB_1", "SQL_BrazilSouth_DB_1", "SQL_CanadaCentral_DB_1", "SQL_CanadaEast_DB_1", "SQL_CentralUS_DB_1", + "SQL_EastAsia_DB_1", "SQL_FranceCentral_DB_1", "SQL_GermanyWestCentral_DB_1", "SQL_CentralIndia_DB_1", "SQL_SouthIndia_DB_1", "SQL_JapanEast_DB_1", "SQL_JapanWest_DB_1", "SQL_NorthCentralUS_DB_1", + "SQL_UKWest_DB_1", "SQL_WestUS_DB_1", "SQL_AustraliaSoutheast_DB_2", "SQL_BrazilSouth_DB_2", "SQL_CanadaCentral_DB_2", "SQL_CanadaEast_DB_2", "SQL_CentralUS_DB_2", "SQL_EastAsia_DB_2", + "SQL_FranceCentral_DB_2", "SQL_GermanyWestCentral_DB_2", "SQL_CentralIndia_DB_2", "SQL_SouthIndia_DB_2", "SQL_JapanEast_DB_2", "SQL_JapanWest_DB_2", "SQL_NorthCentralUS_DB_2", "SQL_UKWest_DB_2", + "SQL_WestUS_DB_2", "SQL_WestCentralUS_DB_1", "SQL_FranceSouth_DB_1", "SQL_WestCentralUS_DB_2", "SQL_FranceSouth_DB_2", "SQL_SwitzerlandNorth_DB_1", "SQL_SwitzerlandNorth_DB_2", "SQL_BrazilSoutheast_DB_1", + "SQL_UAENorth_DB_1", "SQL_BrazilSoutheast_DB_2", "SQL_UAENorth_DB_2"}, false), + }, + "per_database_settings": { Type: pluginsdk.TypeList, Required: true, @@ -206,15 +221,17 @@ func resourceMsSqlElasticPoolCreateUpdate(d *pluginsdk.ResourceData, meta interf sku := expandMsSqlElasticPoolSku(d) t := d.Get("tags").(map[string]interface{}) + maintenanceConfigId := publicmaintenanceconfigurations.NewPublicMaintenanceConfigurationID(subscriptionId, d.Get("maintenance_configuration_name").(string)) elasticPool := sql.ElasticPool{ Name: &id.Name, Location: &location, Sku: sku, Tags: tags.Expand(t), ElasticPoolProperties: &sql.ElasticPoolProperties{ - LicenseType: sql.ElasticPoolLicenseType(d.Get("license_type").(string)), - PerDatabaseSettings: expandMsSqlElasticPoolPerDatabaseSettings(d), - ZoneRedundant: utils.Bool(d.Get("zone_redundant").(bool)), + LicenseType: sql.ElasticPoolLicenseType(d.Get("license_type").(string)), + PerDatabaseSettings: expandMsSqlElasticPoolPerDatabaseSettings(d), + ZoneRedundant: utils.Bool(d.Get("zone_redundant").(bool)), + MaintenanceConfigurationID: utils.String(maintenanceConfigId.ID()), }, } @@ -288,6 +305,12 @@ func resourceMsSqlElasticPoolRead(d *pluginsdk.ResourceData, meta interface{}) e if err := d.Set("per_database_settings", flattenMsSqlElasticPoolPerDatabaseSettings(properties.PerDatabaseSettings)); err != nil { return fmt.Errorf("setting `per_database_settings`: %+v", err) } + + maintenanceConfigId, err := publicmaintenanceconfigurations.ParsePublicMaintenanceConfigurationID(*properties.MaintenanceConfigurationID) + if err != nil { + return err + } + d.Set("maintenance_configuration_name", maintenanceConfigId.ResourceName) } return tags.FlattenAndSet(d, resp.Tags) diff --git a/internal/services/mssql/mssql_elasticpool_resource_test.go b/internal/services/mssql/mssql_elasticpool_resource_test.go index 510d768e76d1..bd2fe7069088 100644 --- a/internal/services/mssql/mssql_elasticpool_resource_test.go +++ b/internal/services/mssql/mssql_elasticpool_resource_test.go @@ -342,6 +342,8 @@ resource "azurerm_mssql_elasticpool" "test" { max_size_gb = %.7[6]f zone_redundant = %[9]t + maintenance_configuration_name = "%[4]s" != "Basic" && azurerm_resource_group.test.location == "westeurope" ? "SQL_WestEurope_DB_2" : "SQL_Default" + sku { name = "%[3]s" tier = "%[4]s" diff --git a/internal/services/mssql/mssql_managed_instance_resource.go b/internal/services/mssql/mssql_managed_instance_resource.go index 5f8f1d96c0d2..b818c23bb825 100644 --- a/internal/services/mssql/mssql_managed_instance_resource.go +++ b/internal/services/mssql/mssql_managed_instance_resource.go @@ -11,10 +11,10 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2021-05-01/publicmaintenanceconfigurations" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - maintenanceParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/maintenance/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/validate" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/sql/parse" @@ -120,7 +120,7 @@ func (r MsSqlManagedInstanceResource) Arguments() map[string]*pluginsdk.Schema { "storage_size_in_gb": { Type: schema.TypeInt, Required: true, - ValidateFunc: validation.IntBetween(32, 8192), + ValidateFunc: validation.IntBetween(32, 16384), }, "subnet_id": { @@ -293,7 +293,7 @@ func (r MsSqlManagedInstanceResource) Create() sdk.ResourceFunc { return fmt.Errorf("expanding `sku_name` for SQL Managed Instance Server %q: %v", id.ID(), err) } - maintenanceConfigId := maintenanceParse.NewPublicMaintenanceConfigurationID(subscriptionId, model.MaintenanceConfigurationName) + maintenanceConfigId := publicmaintenanceconfigurations.NewPublicMaintenanceConfigurationID(subscriptionId, model.MaintenanceConfigurationName) parameters := sql.ManagedInstance{ Sku: sku, @@ -380,7 +380,7 @@ func (r MsSqlManagedInstanceResource) Update() sdk.ResourceFunc { } if metadata.ResourceData.HasChange("maintenance_configuration_name") { - maintenanceConfigId := maintenanceParse.NewPublicMaintenanceConfigurationID(id.SubscriptionId, state.MaintenanceConfigurationName) + maintenanceConfigId := publicmaintenanceconfigurations.NewPublicMaintenanceConfigurationID(id.SubscriptionId, state.MaintenanceConfigurationName) properties.MaintenanceConfigurationID = utils.String(maintenanceConfigId.ID()) } @@ -461,11 +461,11 @@ func (r MsSqlManagedInstanceResource) Read() sdk.ResourceFunc { model.Fqdn = *props.FullyQualifiedDomainName } if props.MaintenanceConfigurationID != nil { - maintenanceConfigId, err := maintenanceParse.PublicMaintenanceConfigurationID(*props.MaintenanceConfigurationID) + maintenanceConfigId, err := publicmaintenanceconfigurations.ParsePublicMaintenanceConfigurationID(*props.MaintenanceConfigurationID) if err != nil { return err } - model.MaintenanceConfigurationName = maintenanceConfigId.Name + model.MaintenanceConfigurationName = maintenanceConfigId.ResourceName } if props.MinimalTLSVersion != nil { model.MinimumTlsVersion = *props.MinimalTLSVersion diff --git a/internal/services/mssql/mssql_server_dns_alias_resource.go b/internal/services/mssql/mssql_server_dns_alias_resource.go index 62a3f8a20f15..d8b9dbd4d4e6 100644 --- a/internal/services/mssql/mssql_server_dns_alias_resource.go +++ b/internal/services/mssql/mssql_server_dns_alias_resource.go @@ -131,9 +131,14 @@ func (m ServerDNSAliasResource) Delete() sdk.ResourceFunc { } metadata.Logger.Infof("deleting %s", id) client := metadata.Client.MSSQL.ServerDNSAliasClient - if _, err = client.Delete(ctx, id.ResourceGroup, id.ServerName, id.DnsAliaseName); err != nil { + future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.DnsAliaseName) + if err != nil { return fmt.Errorf("deleting %s: %v", id, err) } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of %q: %+v", id, err) + } + return nil }, } diff --git a/internal/services/mssql/mssql_server_resource.go b/internal/services/mssql/mssql_server_resource.go index 088b50a53e9e..de679242eae2 100644 --- a/internal/services/mssql/mssql_server_resource.go +++ b/internal/services/mssql/mssql_server_resource.go @@ -149,6 +149,7 @@ func resourceMsSqlServer() *pluginsdk.Resource { "1.0", "1.1", "1.2", + "Disabled", }, false), }, @@ -256,7 +257,7 @@ func resourceMsSqlServerCreate(d *pluginsdk.ResourceData, meta interface{}) erro props.ServerProperties.RestrictOutboundNetworkAccess = sql.ServerNetworkAccessFlagEnabled } - if v := d.Get("minimum_tls_version"); v.(string) != "" { + if v := d.Get("minimum_tls_version"); v.(string) != "Disabled" { props.ServerProperties.MinimalTLSVersion = utils.String(v.(string)) } @@ -346,7 +347,7 @@ func resourceMsSqlServerUpdate(d *pluginsdk.ResourceData, meta interface{}) erro props.ServerProperties.AdministratorLoginPassword = utils.String(adminPassword) } - if v := d.Get("minimum_tls_version"); v.(string) != "" { + if v := d.Get("minimum_tls_version"); v.(string) != "Disabled" { props.ServerProperties.MinimalTLSVersion = utils.String(v.(string)) } @@ -466,7 +467,11 @@ func resourceMsSqlServerRead(d *pluginsdk.ResourceData, meta interface{}) error d.Set("version", props.Version) d.Set("administrator_login", props.AdministratorLogin) d.Set("fully_qualified_domain_name", props.FullyQualifiedDomainName) - d.Set("minimum_tls_version", props.MinimalTLSVersion) + if v := props.MinimalTLSVersion; v == nil { + d.Set("minimum_tls_version", "Disabled") + } else { + d.Set("minimum_tls_version", props.MinimalTLSVersion) + } d.Set("public_network_access_enabled", props.PublicNetworkAccess == sql.ServerNetworkAccessFlagEnabled) d.Set("outbound_network_restriction_enabled", props.RestrictOutboundNetworkAccess == sql.ServerNetworkAccessFlagEnabled) primaryUserAssignedIdentityID := "" @@ -681,7 +686,7 @@ func flattenSqlServerRestorableDatabases(resp sql.RestorableDroppedDatabaseListR func msSqlMinimumTLSVersionDiff(ctx context.Context, d *pluginsdk.ResourceDiff, _ interface{}) (err error) { old, new := d.GetChange("minimum_tls_version") - if old != "" && new == "" { + if old != "" && old != "Disabled" && new == "Disabled" { err = fmt.Errorf("`minimum_tls_version` cannot be removed once set, please set a valid value for this property") } return diff --git a/internal/services/mssql/mssql_server_resource_test.go b/internal/services/mssql/mssql_server_resource_test.go index 6a4de734a52c..e998e03b7f38 100644 --- a/internal/services/mssql/mssql_server_resource_test.go +++ b/internal/services/mssql/mssql_server_resource_test.go @@ -45,6 +45,21 @@ func TestAccMsSqlServer_complete(t *testing.T) { }) } +func TestAccMsSqlServer_minimumTLSVersionDisabled(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_server", "test") + r := MsSqlServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicWithMinimumTLSVersionDisabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + }) +} + func TestAccMsSqlServer_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mssql_server", "test") r := MsSqlServerResource{} @@ -255,6 +270,33 @@ resource "azurerm_mssql_server" "test" { `, data.RandomInteger, data.Locations.Primary) } +func (MsSqlServerResource) basicWithMinimumTLSVersionDisabled(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-mssql-%[1]d" + location = "%[2]s" +} + +resource "azurerm_mssql_server" "test" { + name = "acctestsqlserver%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + version = "12.0" + administrator_login = "missadministrator" + administrator_login_password = "thisIsKat11" + minimum_tls_version = "Disabled" + + identity { + type = "SystemAssigned" + } +} +`, data.RandomInteger, data.Locations.Primary) +} + func (MsSqlServerResource) basicWithMinimumTLSVersion(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/mssql/mssql_virtual_machine_resource.go b/internal/services/mssql/mssql_virtual_machine_resource.go index a87ba5565bcb..19c25b180b14 100644 --- a/internal/services/mssql/mssql_virtual_machine_resource.go +++ b/internal/services/mssql/mssql_virtual_machine_resource.go @@ -409,6 +409,29 @@ func resourceMsSqlVirtualMachineCreateUpdate(d *pluginsdk.ResourceData, meta int } } + // Wait for the auto patching settings to take effect + // See: https://github.com/Azure/azure-rest-api-specs/issues/12818 + if autoPatching := d.Get("auto_patching"); (d.IsNewResource() && len(autoPatching.([]interface{})) > 0) || (!d.IsNewResource() && d.HasChange("auto_patching")) { + log.Printf("[DEBUG] Waiting for SQL Virtual Machine %q AutoPatchingSettings to take effect", d.Id()) + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Retry", "Pending"}, + Target: []string{"Updated"}, + Refresh: resourceMsSqlVirtualMachineAutoPatchingSettingsRefreshFunc(ctx, client, d), + MinTimeout: 1 * time.Minute, + ContinuousTargetOccurence: 2, + } + + if d.IsNewResource() { + stateConf.Timeout = d.Timeout(pluginsdk.TimeoutCreate) + } else { + stateConf.Timeout = d.Timeout(pluginsdk.TimeoutUpdate) + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for SQL Virtual Machine %q AutoPatchingSettings to take effect: %+v", d.Id(), err) + } + } + return resourceMsSqlVirtualMachineRead(d, meta) } @@ -688,6 +711,48 @@ func flattenSqlVirtualMachineAutoBackup(autoBackup *sqlvirtualmachines.AutoBacku } } +func resourceMsSqlVirtualMachineAutoPatchingSettingsRefreshFunc(ctx context.Context, client *sqlvirtualmachines.SqlVirtualMachinesClient, d *pluginsdk.ResourceData) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + id, err := sqlvirtualmachines.ParseSqlVirtualMachineID(d.Id()) + if err != nil { + return nil, "Error", err + } + + resp, err := client.Get(ctx, *id, sqlvirtualmachines.GetOperationOptions{Expand: utils.String("*")}) + if err != nil { + return nil, "Retry", err + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + autoPatchingSettings := flattenSqlVirtualMachineAutoPatching(props.AutoPatchingSettings) + + if len(autoPatchingSettings) == 0 { + if v, ok := d.GetOk("auto_patching"); !ok || len(v.([]interface{})) == 0 { + return resp, "Updated", nil + } + return resp, "Pending", nil + } + + if v, ok := d.GetOk("auto_patching"); !ok || len(v.([]interface{})) == 0 { + return resp, "Pending", nil + } + + for prop, val := range autoPatchingSettings[0].(map[string]interface{}) { + v := d.Get(fmt.Sprintf("auto_patching.0.%s", prop)) + if v != val { + return resp, "Pending", nil + } + } + + return resp, "Updated", nil + } + } + + return resp, "Retry", nil + } +} + func expandSqlVirtualMachineAutoPatchingSettings(input []interface{}) *sqlvirtualmachines.AutoPatchingSettings { if len(input) == 0 { return nil @@ -709,14 +774,14 @@ func flattenSqlVirtualMachineAutoPatching(autoPatching *sqlvirtualmachines.AutoP return []interface{}{} } - var startHour int64 + var startHour int if autoPatching.MaintenanceWindowStartingHour != nil { - startHour = *autoPatching.MaintenanceWindowStartingHour + startHour = int(*autoPatching.MaintenanceWindowStartingHour) } - var duration int64 + var duration int if autoPatching.MaintenanceWindowDuration != nil { - duration = *autoPatching.MaintenanceWindowDuration + duration = int(*autoPatching.MaintenanceWindowDuration) } var dayOfWeek string diff --git a/internal/services/netapp/netapp_snapshot_resource.go b/internal/services/netapp/netapp_snapshot_resource.go index 8ae77671d270..1e0e016b620f 100644 --- a/internal/services/netapp/netapp_snapshot_resource.go +++ b/internal/services/netapp/netapp_snapshot_resource.go @@ -146,7 +146,7 @@ func resourceNetAppSnapshotDelete(d *pluginsdk.ResourceData, meta interface{}) e return err } - if _, err = client.Delete(ctx, *id); err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } diff --git a/internal/services/netapp/netapp_volume_resource.go b/internal/services/netapp/netapp_volume_resource.go index 1dbe6bec3111..9bba80e9535c 100644 --- a/internal/services/netapp/netapp_volume_resource.go +++ b/internal/services/netapp/netapp_volume_resource.go @@ -1084,8 +1084,8 @@ func flattenNetAppVolumeMountIPAddresses(input *[]volumes.MountTargetProperties) } for _, item := range *input { - if item.IpAddress != nil { - results = append(results, item.IpAddress) + if item.IPAddress != nil { + results = append(results, item.IPAddress) } } diff --git a/internal/services/network/application_gateway_resource.go b/internal/services/network/application_gateway_resource.go index 47ab875139f9..4ddbe76d7648 100644 --- a/internal/services/network/application_gateway_resource.go +++ b/internal/services/network/application_gateway_resource.go @@ -325,6 +325,7 @@ func resourceApplicationGateway() *pluginsdk.Resource { "private_ip_address": { Type: pluginsdk.TypeString, Optional: true, + Computed: true, }, "public_ip_address_id": { @@ -409,6 +410,24 @@ func resourceApplicationGateway() *pluginsdk.Resource { }, }, + "global": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "request_buffering_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + "response_buffering_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + }, + }, + }, + //lintignore:S016,S023 "http_listener": { Type: pluginsdk.TypeSet, @@ -966,7 +985,7 @@ func resourceApplicationGateway() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "body": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, }, "status_code": { @@ -1092,6 +1111,17 @@ func resourceApplicationGateway() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, }, + + "components": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "path_only", + "query_string_only", + }, false), + }, + "reroute": { Type: pluginsdk.TypeBool, Optional: true, @@ -1561,6 +1591,11 @@ func resourceApplicationGatewayCreate(d *pluginsdk.ResourceData, meta interface{ gatewayIPConfigurations, stopApplicationGateway := expandApplicationGatewayIPConfigurations(d) + globalConfiguration, err := expandApplicationGatewayGlobalConfiguration(d.Get("global").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `global`: %+v", err) + } + httpListeners, err := expandApplicationGatewayHTTPListeners(d, id.ID()) if err != nil { return fmt.Errorf("fail to expand `http_listener`: %+v", err) @@ -1585,6 +1620,7 @@ func resourceApplicationGatewayCreate(d *pluginsdk.ResourceData, meta interface{ FrontendIPConfigurations: expandApplicationGatewayFrontendIPConfigurations(d, id.ID()), FrontendPorts: expandApplicationGatewayFrontendPorts(d), GatewayIPConfigurations: gatewayIPConfigurations, + GlobalConfiguration: globalConfiguration, HTTPListeners: httpListeners, PrivateLinkConfigurations: expandApplicationGatewayPrivateLinkConfigurations(d), Probes: expandApplicationGatewayProbes(d), @@ -1793,6 +1829,15 @@ func resourceApplicationGatewayUpdate(d *pluginsdk.ResourceData, meta interface{ applicationGateway.ApplicationGatewayPropertiesFormat.GatewayIPConfigurations = gatewayIPConfigurations } + if d.HasChange("global") { + globalConfiguration, err := expandApplicationGatewayGlobalConfiguration(d.Get("global").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `global`: %+v", err) + } + + applicationGateway.ApplicationGatewayPropertiesFormat.GlobalConfiguration = globalConfiguration + } + if d.HasChange("http_listener") { httpListeners, err := expandApplicationGatewayHTTPListeners(d, id.ID()) if err != nil { @@ -2052,6 +2097,10 @@ func resourceApplicationGatewayRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("setting `gateway_ip_configuration`: %+v", setErr) } + if setErr := d.Set("global", flattenApplicationGatewayGlobalConfiguration(props.GlobalConfiguration)); setErr != nil { + return fmt.Errorf("setting `global`: %+v", setErr) + } + if setErr := d.Set("private_endpoint_connection", flattenApplicationGatewayPrivateEndpoints(props.PrivateEndpointConnections)); setErr != nil { return fmt.Errorf("setting `private_endpoint_connection`: %+v", setErr) } @@ -2966,6 +3015,36 @@ func flattenApplicationGatewayIPConfigurations(input *[]network.ApplicationGatew return results } +func expandApplicationGatewayGlobalConfiguration(input []interface{}) (*network.ApplicationGatewayGlobalConfiguration, error) { + if len(input) == 0 { + return nil, nil + } + + v := input[0].(map[string]interface{}) + return &network.ApplicationGatewayGlobalConfiguration{ + EnableRequestBuffering: utils.Bool(v["request_buffering_enabled"].(bool)), + EnableResponseBuffering: utils.Bool(v["response_buffering_enabled"].(bool)), + }, nil +} + +func flattenApplicationGatewayGlobalConfiguration(input *network.ApplicationGatewayGlobalConfiguration) []interface{} { + if input == nil { + return nil + } + + output := make(map[string]interface{}) + + if input.EnableRequestBuffering != nil { + output["request_buffering_enabled"] = *input.EnableRequestBuffering + } + + if input.EnableResponseBuffering != nil { + output["response_buffering_enabled"] = *input.EnableResponseBuffering + } + + return []interface{}{output} +} + func expandApplicationGatewayFrontendPorts(d *pluginsdk.ResourceData) *[]network.ApplicationGatewayFrontendPort { vs := d.Get("frontend_port").(*pluginsdk.Set).List() results := make([]network.ApplicationGatewayFrontendPort, 0) @@ -3599,10 +3678,14 @@ func expandApplicationGatewayRewriteRuleSets(d *pluginsdk.ResourceData) (*[]netw if c["path"] == nil && c["query_string"] == nil { return nil, fmt.Errorf("At least one of `path` or `query_string` must be set") } - if c["path"] != nil { + components := "" + if c["components"] != nil { + components = c["components"].(string) + } + if c["path"] != nil && components != "query_string_only" { urlConfiguration.ModifiedPath = utils.String(c["path"].(string)) } - if c["query_string"] != nil { + if c["query_string"] != nil && components != "path_only" { urlConfiguration.ModifiedQueryString = utils.String(c["query_string"].(string)) } if c["reroute"] != nil { @@ -3733,20 +3816,36 @@ func flattenApplicationGatewayRewriteRuleSets(input *[]network.ApplicationGatewa if actionSet.URLConfiguration != nil { config := *actionSet.URLConfiguration - urlConfig := map[string]interface{}{} - + components := "" + path := "" if config.ModifiedPath != nil { - urlConfig["path"] = *config.ModifiedPath + path = *config.ModifiedPath } + queryString := "" if config.ModifiedQueryString != nil { - urlConfig["query_string"] = *config.ModifiedQueryString + queryString = *config.ModifiedQueryString + } + + if path != queryString { + if path != "" && queryString == "" { + components = "path_only" + } else if queryString != "" && path == "" { + components = "query_string_only" + } } + reroute := false if config.Reroute != nil { - urlConfig["reroute"] = *config.Reroute + reroute = *config.Reroute } - urlConfigs = append(urlConfigs, urlConfig) + + urlConfigs = append(urlConfigs, map[string]interface{}{ + "components": components, + "query_string": queryString, + "path": path, + "reroute": reroute, + }) } } ruleOutput["request_header_configuration"] = requestConfigs diff --git a/internal/services/network/application_gateway_resource_test.go b/internal/services/network/application_gateway_resource_test.go index 68f1e833d7fd..2945740a1695 100644 --- a/internal/services/network/application_gateway_resource_test.go +++ b/internal/services/network/application_gateway_resource_test.go @@ -89,6 +89,36 @@ func TestAccApplicationGateway_autoscaleConfigurationNoMaxCapacity(t *testing.T) }) } +func TestAccApplicationGateway_globalConfiguration(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_gateway", "test") + r := ApplicationGatewayResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.createGlobalConfiguration(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku.0.name").HasValue("Standard_v2"), + check.That(data.ResourceName).Key("sku.0.tier").HasValue("Standard_v2"), + check.That(data.ResourceName).Key("global.0.request_buffering_enabled").HasValue("true"), + check.That(data.ResourceName).Key("global.0.response_buffering_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + { + Config: r.updateGlobalConfiguration(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku.0.name").HasValue("Standard_v2"), + check.That(data.ResourceName).Key("sku.0.tier").HasValue("Standard_v2"), + check.That(data.ResourceName).Key("global.0.request_buffering_enabled").HasValue("false"), + check.That(data.ResourceName).Key("global.0.response_buffering_enabled").HasValue("false"), + ), + }, + data.ImportStep(), + }) +} + func TestAccApplicationGateway_zones(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_application_gateway", "test") r := ApplicationGatewayResource{} @@ -406,6 +436,11 @@ func TestAccApplicationGateway_rewriteRuleSets_rewriteUrl(t *testing.T) { Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("rewrite_rule_set.0.name").Exists(), + check.That(data.ResourceName).Key("rewrite_rule_set.#").HasValue("2"), + check.That(data.ResourceName).Key("rewrite_rule_set.0.rewrite_rule.0.url.0.components").HasValue(""), + check.That(data.ResourceName).Key("rewrite_rule_set.1.rewrite_rule.#").HasValue("2"), + check.That(data.ResourceName).Key("rewrite_rule_set.1.rewrite_rule.0.url.0.components").HasValue("path_only"), + check.That(data.ResourceName).Key("rewrite_rule_set.1.rewrite_rule.1.url.0.components").HasValue("query_string_only"), ), }, data.ImportStep(), @@ -1380,6 +1415,174 @@ resource "azurerm_application_gateway" "test" { `, r.template(data), data.RandomInteger, data.RandomInteger) } +func (r ApplicationGatewayResource) createGlobalConfiguration(data acceptance.TestData) string { + 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_public_ip" "test_standard" { + name = "acctest-pubip-standard-%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" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + sku { + name = "Standard_v2" + tier = "Standard_v2" + 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 + } + + global { + request_buffering_enabled = true + response_buffering_enabled = true + } + + 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 + priority = 10 + } +} +`, r.template(data), data.RandomInteger, data.RandomInteger) +} + +func (r ApplicationGatewayResource) updateGlobalConfiguration(data acceptance.TestData) string { + 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_public_ip" "test_standard" { + name = "acctest-pubip-standard-%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" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + sku { + name = "Standard_v2" + tier = "Standard_v2" + 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 + } + + global { + request_buffering_enabled = false + response_buffering_enabled = false + } + + 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 + priority = 10 + } +} +`, r.template(data), data.RandomInteger, data.RandomInteger) +} + func (r ApplicationGatewayResource) UserDefinedIdentity(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -6056,6 +6259,40 @@ resource "azurerm_application_gateway" "test" { } } + rewrite_rule_set { + name = "${local.rewrite_rule_set_name}_1" + + rewrite_rule { + name = "${local.rewrite_rule_name}_1" + rule_sequence = 1 + + condition { + variable = "var_uri_path" + pattern = ".*article/(.*)/(.*)" + } + + url { + path = "/article.aspx" + components = "path_only" + } + } + + rewrite_rule { + name = "${local.rewrite_rule_name}_2" + rule_sequence = 2 + + condition { + variable = "var_uri_path" + pattern = ".*article2/(.*)/(.*)" + } + + url { + query_string = "id={var_uri_path_1}&title={var_uri_path_2}" + components = "query_string_only" + } + } + } + redirect_configuration { name = local.redirect_configuration_name redirect_type = "Temporary" diff --git a/internal/services/network/express_route_circuit_peering_resource.go b/internal/services/network/express_route_circuit_peering_resource.go index 250c9b10e0e9..e29705151b27 100644 --- a/internal/services/network/express_route_circuit_peering_resource.go +++ b/internal/services/network/express_route_circuit_peering_resource.go @@ -58,12 +58,24 @@ func resourceExpressRouteCircuitPeering() *pluginsdk.Resource { "primary_peer_address_prefix": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, + RequiredWith: []string{ + "secondary_peer_address_prefix", + }, }, "secondary_peer_address_prefix": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, + RequiredWith: []string{ + "primary_peer_address_prefix", + }, + }, + + "ipv4_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, }, "vlan_id": { @@ -119,7 +131,7 @@ func resourceExpressRouteCircuitPeering() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "microsoft_peering": { Type: pluginsdk.TypeList, - Required: true, + Optional: true, MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ @@ -159,6 +171,12 @@ func resourceExpressRouteCircuitPeering() *pluginsdk.Resource { Required: true, }, + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + "route_filter_id": { Type: pluginsdk.TypeString, Optional: true, @@ -188,6 +206,11 @@ func resourceExpressRouteCircuitPeering() *pluginsdk.Resource { Optional: true, ValidateFunc: azure.ValidateResourceID, }, + + "gateway_manager_etag": { + Type: pluginsdk.TypeString, + Computed: true, + }, }, } } @@ -228,20 +251,38 @@ func resourceExpressRouteCircuitPeeringCreateUpdate(d *pluginsdk.ResourceData, m parameters := network.ExpressRouteCircuitPeering{ ExpressRouteCircuitPeeringPropertiesFormat: &network.ExpressRouteCircuitPeeringPropertiesFormat{ - PeeringType: network.ExpressRoutePeeringType(id.PeeringName), - SharedKey: utils.String(sharedKey), - PrimaryPeerAddressPrefix: utils.String(primaryPeerAddressPrefix), - SecondaryPeerAddressPrefix: utils.String(secondaryPeerAddressPrefix), - AzureASN: utils.Int32(int32(azureASN)), - PeerASN: utils.Int64(int64(peerASN)), - VlanID: utils.Int32(int32(vlanId)), + PeeringType: network.ExpressRoutePeeringType(id.PeeringName), + SharedKey: utils.String(sharedKey), + AzureASN: utils.Int32(int32(azureASN)), + PeerASN: utils.Int64(int64(peerASN)), + VlanID: utils.Int32(int32(vlanId)), + GatewayManagerEtag: utils.String(d.Get("gateway_manager_etag").(string)), }, } + ipv4Enabled := d.Get("ipv4_enabled").(bool) + if ipv4Enabled { + parameters.ExpressRouteCircuitPeeringPropertiesFormat.State = network.ExpressRoutePeeringStateEnabled + } else { + parameters.ExpressRouteCircuitPeeringPropertiesFormat.State = network.ExpressRoutePeeringStateDisabled + } + + if !strings.EqualFold(primaryPeerAddressPrefix, "") { + parameters.PrimaryPeerAddressPrefix = utils.String(primaryPeerAddressPrefix) + } + + if !strings.EqualFold(secondaryPeerAddressPrefix, "") { + parameters.SecondaryPeerAddressPrefix = utils.String(secondaryPeerAddressPrefix) + } + if strings.EqualFold(id.PeeringName, string(network.ExpressRoutePeeringTypeMicrosoftPeering)) { peerings := d.Get("microsoft_peering_config").([]interface{}) - if len(peerings) == 0 { - return fmt.Errorf("`microsoft_peering_config` must be specified when `peering_type` is set to `MicrosoftPeering`") + if len(peerings) == 0 && primaryPeerAddressPrefix != "" { + return fmt.Errorf("`microsoft_peering_config` must be specified when config for Ipv4 and `peering_type` is set to `MicrosoftPeering`") + } + + if len(peerings) != 0 && (primaryPeerAddressPrefix == "" || secondaryPeerAddressPrefix == "") { + return fmt.Errorf("`primary_peer_address_prefix, secondary_peer_address_prefix` must be specified when config for Ipv4") } peeringConfig := expandExpressRouteCircuitPeeringMicrosoftConfig(peerings) @@ -252,23 +293,20 @@ func resourceExpressRouteCircuitPeeringCreateUpdate(d *pluginsdk.ResourceData, m ID: utils.String(route_filter_id), } } + } else if route_filter_id != "" { + return fmt.Errorf("`route_filter_id` may only be specified when `peering_type` is set to `MicrosoftPeering`") + } - ipv6Peering := d.Get("ipv6").([]interface{}) - ipv6PeeringConfig, err := expandExpressRouteCircuitIpv6PeeringConfig(ipv6Peering) - if err != nil { - return err - } - parameters.ExpressRouteCircuitPeeringPropertiesFormat.Ipv6PeeringConfig = ipv6PeeringConfig - } else { - if route_filter_id != "" { - return fmt.Errorf("`route_filter_id` may only be specified when `peering_type` is set to `MicrosoftPeering`") - } + ipv6Peering := d.Get("ipv6").([]interface{}) + if len(ipv6Peering) != 0 && id.PeeringName == string(network.ExpressRoutePeeringTypeAzurePublicPeering) { + return fmt.Errorf("`ipv6` may only be specified when `peering_type` is `MicrosoftPeering` or `AzurePrivatePeering`") + } - ipv6Peering := d.Get("ipv6").([]interface{}) - if len(ipv6Peering) != 0 { - return fmt.Errorf("`ipv6` may only be specified when `peering_type` is set to `MicrosoftPeering`") - } + ipv6PeeringConfig, err := expandExpressRouteCircuitIpv6PeeringConfig(ipv6Peering) + if err != nil { + return err } + parameters.ExpressRouteCircuitPeeringPropertiesFormat.Ipv6PeeringConfig = ipv6PeeringConfig future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ExpressRouteCircuitName, id.PeeringName, parameters) if err != nil { @@ -315,6 +353,8 @@ func resourceExpressRouteCircuitPeeringRead(d *pluginsdk.ResourceData, meta inte d.Set("primary_peer_address_prefix", props.PrimaryPeerAddressPrefix) d.Set("secondary_peer_address_prefix", props.SecondaryPeerAddressPrefix) d.Set("vlan_id", props.VlanID) + d.Set("gateway_manager_etag", props.GatewayManagerEtag) + d.Set("ipv4_enabled", props.State == network.ExpressRoutePeeringStateEnabled) routeFilterId := "" if props.RouteFilter != nil && props.RouteFilter.ID != nil { @@ -388,10 +428,24 @@ func expandExpressRouteCircuitIpv6PeeringConfig(input []interface{}) (*network.I v := input[0].(map[string]interface{}) peeringConfig := network.Ipv6ExpressRouteCircuitPeeringConfig{ - PrimaryPeerAddressPrefix: utils.String(v["primary_peer_address_prefix"].(string)), - SecondaryPeerAddressPrefix: utils.String(v["secondary_peer_address_prefix"].(string)), - MicrosoftPeeringConfig: expandExpressRouteCircuitPeeringMicrosoftConfig(v["microsoft_peering"].([]interface{})), + MicrosoftPeeringConfig: expandExpressRouteCircuitPeeringMicrosoftConfig(v["microsoft_peering"].([]interface{})), + State: network.ExpressRouteCircuitPeeringStateEnabled, } + + primaryPeerAddressPrefix := v["primary_peer_address_prefix"].(string) + secondaryPeerAddressPrefix := v["secondary_peer_address_prefix"].(string) + if !strings.EqualFold(primaryPeerAddressPrefix, "") { + peeringConfig.PrimaryPeerAddressPrefix = utils.String(primaryPeerAddressPrefix) + } + if !strings.EqualFold(secondaryPeerAddressPrefix, "") { + peeringConfig.SecondaryPeerAddressPrefix = utils.String(secondaryPeerAddressPrefix) + } + + ipv6Enabled := v["enabled"].(bool) + if !ipv6Enabled { + peeringConfig.State = network.ExpressRouteCircuitPeeringStateDisabled + } + routeFilterId := v["route_filter_id"].(string) if routeFilterId != "" { if _, err := parse.RouteFilterID(routeFilterId); err != nil { @@ -448,6 +502,7 @@ func flattenExpressRouteCircuitIpv6PeeringConfig(input *network.Ipv6ExpressRoute "primary_peer_address_prefix": primaryPeerAddressPrefix, "secondary_peer_address_prefix": secondaryPeerAddressPrefix, "route_filter_id": routeFilterId, + "enabled": input.State == network.ExpressRouteCircuitPeeringStateEnabled, }, } } diff --git a/internal/services/network/network_connection_monitor_resource.go b/internal/services/network/network_connection_monitor_resource.go index 7e6ae97a5d91..e0f5e2fd80e0 100644 --- a/internal/services/network/network_connection_monitor_resource.go +++ b/internal/services/network/network_connection_monitor_resource.go @@ -6,12 +6,12 @@ import ( "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" computeValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" - logAnalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" @@ -167,7 +167,7 @@ func resourceNetworkConnectionMonitorSchema() map[string]*pluginsdk.Schema { Computed: true, ValidateFunc: validation.Any( computeValidate.VirtualMachineID, - logAnalyticsValidate.LogAnalyticsWorkspaceID, + workspaces.ValidateWorkspaceID, networkValidate.SubnetID, networkValidate.VirtualNetworkID, ), @@ -422,7 +422,7 @@ func resourceNetworkConnectionMonitorSchema() map[string]*pluginsdk.Schema { ConfigMode: pluginsdk.SchemaConfigModeAttr, Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, - ValidateFunc: logAnalyticsValidate.LogAnalyticsWorkspaceID, + ValidateFunc: workspaces.ValidateWorkspaceID, }, }, diff --git a/internal/services/network/parse/virtual_machine_scale_set_public_ip_address.go b/internal/services/network/parse/virtual_machine_scale_set_public_ip_address.go new file mode 100644 index 000000000000..9b4765ffbed5 --- /dev/null +++ b/internal/services/network/parse/virtual_machine_scale_set_public_ip_address.go @@ -0,0 +1,93 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type VirtualMachineScaleSetPublicIPAddressId struct { + SubscriptionId string + ResourceGroup string + VirtualMachineScaleSetName string + VirtualMachineName string + NetworkInterfaceName string + IpConfigurationName string + PublicIPAddressName string +} + +func NewVirtualMachineScaleSetPublicIPAddressID(subscriptionId, resourceGroup, virtualMachineScaleSetName, virtualMachineName, networkInterfaceName, ipConfigurationName, publicIPAddressName string) VirtualMachineScaleSetPublicIPAddressId { + return VirtualMachineScaleSetPublicIPAddressId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + VirtualMachineScaleSetName: virtualMachineScaleSetName, + VirtualMachineName: virtualMachineName, + NetworkInterfaceName: networkInterfaceName, + IpConfigurationName: ipConfigurationName, + PublicIPAddressName: publicIPAddressName, + } +} + +func (id VirtualMachineScaleSetPublicIPAddressId) String() string { + segments := []string{ + fmt.Sprintf("Public I P Address Name %q", id.PublicIPAddressName), + fmt.Sprintf("Ip Configuration Name %q", id.IpConfigurationName), + fmt.Sprintf("Network Interface Name %q", id.NetworkInterfaceName), + fmt.Sprintf("Virtual Machine Name %q", id.VirtualMachineName), + fmt.Sprintf("Virtual Machine Scale Set Name %q", id.VirtualMachineScaleSetName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Virtual Machine Scale Set Public I P Address", segmentsStr) +} + +func (id VirtualMachineScaleSetPublicIPAddressId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s/virtualMachines/%s/networkInterfaces/%s/ipConfigurations/%s/publicIPAddresses/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.VirtualMachineScaleSetName, id.VirtualMachineName, id.NetworkInterfaceName, id.IpConfigurationName, id.PublicIPAddressName) +} + +// VirtualMachineScaleSetPublicIPAddressID parses a VirtualMachineScaleSetPublicIPAddress ID into an VirtualMachineScaleSetPublicIPAddressId struct +func VirtualMachineScaleSetPublicIPAddressID(input string) (*VirtualMachineScaleSetPublicIPAddressId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := VirtualMachineScaleSetPublicIPAddressId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.VirtualMachineScaleSetName, err = id.PopSegment("virtualMachineScaleSets"); err != nil { + return nil, err + } + if resourceId.VirtualMachineName, err = id.PopSegment("virtualMachines"); err != nil { + return nil, err + } + if resourceId.NetworkInterfaceName, err = id.PopSegment("networkInterfaces"); err != nil { + return nil, err + } + if resourceId.IpConfigurationName, err = id.PopSegment("ipConfigurations"); err != nil { + return nil, err + } + if resourceId.PublicIPAddressName, err = id.PopSegment("publicIPAddresses"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/network/parse/virtual_machine_scale_set_public_ip_address_test.go b/internal/services/network/parse/virtual_machine_scale_set_public_ip_address_test.go new file mode 100644 index 000000000000..0a75f5252697 --- /dev/null +++ b/internal/services/network/parse/virtual_machine_scale_set_public_ip_address_test.go @@ -0,0 +1,176 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = VirtualMachineScaleSetPublicIPAddressId{} + +func TestVirtualMachineScaleSetPublicIPAddressIDFormatter(t *testing.T) { + actual := NewVirtualMachineScaleSetPublicIPAddressID("12345678-1234-9876-4563-123456789012", "resGroup1", "scaleSet1", "virtualMachine1", "networkInterface1", "ipConfiguration1", "publicIpAddress1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/publicIpAddress1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestVirtualMachineScaleSetPublicIPAddressID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *VirtualMachineScaleSetPublicIPAddressId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing VirtualMachineScaleSetName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", + Error: true, + }, + + { + // missing value for VirtualMachineScaleSetName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/", + Error: true, + }, + + { + // missing VirtualMachineName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/", + Error: true, + }, + + { + // missing value for VirtualMachineName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/", + Error: true, + }, + + { + // missing NetworkInterfaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/", + Error: true, + }, + + { + // missing value for NetworkInterfaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/", + Error: true, + }, + + { + // missing IpConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/", + Error: true, + }, + + { + // missing value for IpConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/", + Error: true, + }, + + { + // missing PublicIPAddressName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/", + Error: true, + }, + + { + // missing value for PublicIPAddressName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/publicIpAddress1", + Expected: &VirtualMachineScaleSetPublicIPAddressId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + VirtualMachineScaleSetName: "scaleSet1", + VirtualMachineName: "virtualMachine1", + NetworkInterfaceName: "networkInterface1", + IpConfigurationName: "ipConfiguration1", + PublicIPAddressName: "publicIpAddress1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/VIRTUALMACHINESCALESETS/SCALESET1/VIRTUALMACHINES/VIRTUALMACHINE1/NETWORKINTERFACES/NETWORKINTERFACE1/IPCONFIGURATIONS/IPCONFIGURATION1/PUBLICIPADDRESSES/PUBLICIPADDRESS1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := VirtualMachineScaleSetPublicIPAddressID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.VirtualMachineScaleSetName != v.Expected.VirtualMachineScaleSetName { + t.Fatalf("Expected %q but got %q for VirtualMachineScaleSetName", v.Expected.VirtualMachineScaleSetName, actual.VirtualMachineScaleSetName) + } + if actual.VirtualMachineName != v.Expected.VirtualMachineName { + t.Fatalf("Expected %q but got %q for VirtualMachineName", v.Expected.VirtualMachineName, actual.VirtualMachineName) + } + if actual.NetworkInterfaceName != v.Expected.NetworkInterfaceName { + t.Fatalf("Expected %q but got %q for NetworkInterfaceName", v.Expected.NetworkInterfaceName, actual.NetworkInterfaceName) + } + if actual.IpConfigurationName != v.Expected.IpConfigurationName { + t.Fatalf("Expected %q but got %q for IpConfigurationName", v.Expected.IpConfigurationName, actual.IpConfigurationName) + } + if actual.PublicIPAddressName != v.Expected.PublicIPAddressName { + t.Fatalf("Expected %q but got %q for PublicIPAddressName", v.Expected.PublicIPAddressName, actual.PublicIPAddressName) + } + } +} diff --git a/internal/services/network/private_endpoint_resource.go b/internal/services/network/private_endpoint_resource.go index ff406428168d..fbfe33c0a231 100644 --- a/internal/services/network/private_endpoint_resource.go +++ b/internal/services/network/private_endpoint_resource.go @@ -11,18 +11,19 @@ import ( "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + mariaDB "github.com/hashicorp/go-azure-sdk/resource-manager/mariadb/2018-06-01/servers" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/privatezones" "github.com/hashicorp/go-azure-sdk/resource-manager/signalr/2022-02-01/signalr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" cosmosParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/cosmos/parse" - mariaDBParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" mysqlParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/mysql/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" - postgresqlParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -164,6 +165,34 @@ func resourcePrivateEndpoint() *pluginsdk.Resource { }, }, + "ip_configuration": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.PrivateLinkName, + }, + "private_ip_address": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "subresource_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + "custom_dns_configs": { Type: pluginsdk.TypeList, Computed: true, @@ -268,6 +297,7 @@ func resourcePrivateEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) location := azure.NormalizeLocation(d.Get("location").(string)) privateDnsZoneGroup := d.Get("private_dns_zone_group").([]interface{}) privateServiceConnections := d.Get("private_service_connection").([]interface{}) + ipConfigurations := d.Get("ip_configuration").([]interface{}) subnetId := d.Get("subnet_id").(string) parameters := network.PrivateEndpoint{ @@ -278,10 +308,20 @@ func resourcePrivateEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) Subnet: &network.Subnet{ ID: utils.String(subnetId), }, + IPConfigurations: expandPrivateEndpointIPConfigurations(ipConfigurations), }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + err = validatePrivateLinkServiceId(*parameters.PrivateEndpointProperties.PrivateLinkServiceConnections) + if err != nil { + return err + } + err = validatePrivateLinkServiceId(*parameters.PrivateEndpointProperties.ManualPrivateLinkServiceConnections) + if err != nil { + return err + } + cosmosDbResIds := getCosmosDbResIdInPrivateServiceConnections(parameters.PrivateEndpointProperties) for _, cosmosDbResId := range cosmosDbResIds { log.Printf("[DEBUG] Add Lock For Private Endpoint %q, lock name: %q", id.Name, cosmosDbResId) @@ -289,17 +329,45 @@ func resourcePrivateEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) //goland:noinspection GoDeferInLoop defer locks.UnlockByName(cosmosDbResId, "azurerm_private_endpoint") } + locks.ByName(subnetId, "azurerm_private_endpoint") + defer locks.UnlockByName(subnetId, "azurerm_private_endpoint") - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters) - if err != nil { - if strings.EqualFold(err.Error(), "is missing required parameter 'group Id'") { - return fmt.Errorf("creating Private Endpoint %q (Resource Group %q) due to missing 'group Id', ensure that the 'subresource_names' type is populated: %+v", id.Name, id.ResourceGroup, err) - } else { - return fmt.Errorf("creating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *resource.RetryError { + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters) + if err != nil { + switch { + case strings.EqualFold(err.Error(), "is missing required parameter 'group Id'"): + { + return &resource.RetryError{ + Err: fmt.Errorf("creating Private Endpoint %q (Resource Group %q) due to missing 'group Id', ensure that the 'subresource_names' type is populated: %+v", id.Name, id.ResourceGroup, err), + Retryable: false, + } + } + case strings.Contains(err.Error(), "PrivateLinkServiceId Invalid private link service id"): + { + return &resource.RetryError{ + Err: fmt.Errorf("creating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + Retryable: true, + } + } + default: + return &resource.RetryError{ + Err: fmt.Errorf("creating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + Retryable: false, + } + } } - } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return &resource.RetryError{ + Err: fmt.Errorf("waiting for creation of Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + Retryable: false, + } + } + return nil + }) + if err != nil { + return err } d.SetId(id.ID()) @@ -317,6 +385,20 @@ func resourcePrivateEndpointCreate(d *pluginsdk.ResourceData, meta interface{}) return resourcePrivateEndpointRead(d, meta) } +func validatePrivateLinkServiceId(endpoints []network.PrivateLinkServiceConnection) error { + for _, connection := range endpoints { + _, errors := azure.ValidateResourceID(*connection.PrivateLinkServiceID, "PrivateLinkServiceID") + if len(errors) == 0 { + continue + } + _, errors = validate.PrivateConnectionResourceAlias(*connection.PrivateLinkServiceID, "PrivateLinkServiceID") + if len(errors) != 0 { + return fmt.Errorf("PrivateLinkServiceId Invalid: %q", *connection.PrivateLinkServiceID) + } + } + return nil +} + func getCosmosDbResIdInPrivateServiceConnections(p *network.PrivateEndpointProperties) []string { var ids []string exists := make(map[string]struct{}) @@ -364,6 +446,7 @@ func resourcePrivateEndpointUpdate(d *pluginsdk.ResourceData, meta interface{}) location := azure.NormalizeLocation(d.Get("location").(string)) privateDnsZoneGroup := d.Get("private_dns_zone_group").([]interface{}) privateServiceConnections := d.Get("private_service_connection").([]interface{}) + ipConfigurations := d.Get("ip_configuration").([]interface{}) subnetId := d.Get("subnet_id").(string) // TODO: in future it'd be nice to support conditional updates here, but one problem at a time @@ -375,20 +458,58 @@ func resourcePrivateEndpointUpdate(d *pluginsdk.ResourceData, meta interface{}) Subnet: &network.Subnet{ ID: utils.String(subnetId), }, + IPConfigurations: expandPrivateEndpointIPConfigurations(ipConfigurations), }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters) + err = validatePrivateLinkServiceId(*parameters.PrivateEndpointProperties.PrivateLinkServiceConnections) if err != nil { - if strings.EqualFold(err.Error(), "is missing required parameter 'group Id'") { - return fmt.Errorf("updating Private Endpoint %q (Resource Group %q) due to missing 'group Id', ensure that the 'subresource_names' type is populated: %+v", id.Name, id.ResourceGroup, err) - } else { - return fmt.Errorf("updating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) - } + return err } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + err = validatePrivateLinkServiceId(*parameters.PrivateEndpointProperties.ManualPrivateLinkServiceConnections) + if err != nil { + return err + } + + locks.ByName(subnetId, "azurerm_private_endpoint") + defer locks.UnlockByName(subnetId, "azurerm_private_endpoint") + + err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *resource.RetryError { + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters) + if err != nil { + switch { + case strings.EqualFold(err.Error(), "is missing required parameter 'group Id'"): + { + return &resource.RetryError{ + Err: fmt.Errorf("updating Private Endpoint %q (Resource Group %q) due to missing 'group Id', ensure that the 'subresource_names' type is populated: %+v", id.Name, id.ResourceGroup, err), + Retryable: false, + } + } + case strings.Contains(err.Error(), "PrivateLinkServiceId Invalid private link service id"): + { + return &resource.RetryError{ + Err: fmt.Errorf("creating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + Retryable: true, + } + } + default: + return &resource.RetryError{ + Err: fmt.Errorf("updating Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + } + } + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return &resource.RetryError{ + Err: fmt.Errorf("waiting for update of Private Endpoint %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err), + Retryable: false, + } + } + return nil + }) + if err != nil { + return err } // 1 Private Endpoint can have 1 Private DNS Zone Group - so to update we need to Delete & Recreate @@ -495,6 +616,11 @@ func resourcePrivateEndpointRead(d *pluginsdk.ResourceData, meta interface{}) er return fmt.Errorf("setting `private_service_connection`: %+v", err) } + flattenedipconfiguration := flattenPrivateEndpointIPConfigurations(props.IPConfigurations) + if err := d.Set("ip_configuration", flattenedipconfiguration); err != nil { + return fmt.Errorf("setting `ip_configuration`: %+v", err) + } + subnetId := "" if props.Subnet != nil && props.Subnet.ID != nil { subnetId = *props.Subnet.ID @@ -547,6 +673,7 @@ func resourcePrivateEndpointDelete(d *pluginsdk.ResourceData, meta interface{}) } log.Printf("[DEBUG] Deleted the Private DNS Zone Group associated with Private Endpoint %q / Resource Group %q.", id.Name, id.ResourceGroup) + subnetId := d.Get("subnet_id").(string) privateServiceConnections := d.Get("private_service_connection").([]interface{}) parameters := network.PrivateEndpoint{ PrivateEndpointProperties: &network.PrivateEndpointProperties{ @@ -560,6 +687,8 @@ func resourcePrivateEndpointDelete(d *pluginsdk.ResourceData, meta interface{}) //goland:noinspection GoDeferInLoop defer locks.UnlockByName(cosmosDbResId, "azurerm_private_endpoint") } + locks.ByName(subnetId, "azurerm_private_endpoint") + defer locks.UnlockByName(subnetId, "azurerm_private_endpoint") log.Printf("[DEBUG] Deleting the Private Endpoint %q / Resource Group %q..", id.Name, id.ResourceGroup) future, err := client.Delete(ctx, id.ResourceGroup, id.Name) @@ -611,6 +740,45 @@ func expandPrivateLinkEndpointServiceConnection(input []interface{}, parseManual return &results } +func expandPrivateEndpointIPConfigurations(input []interface{}) *[]network.PrivateEndpointIPConfiguration { + results := make([]network.PrivateEndpointIPConfiguration, 0) + + for _, item := range input { + v := item.(map[string]interface{}) + privateIPAddress := v["private_ip_address"].(string) + subResourceName := v["subresource_name"].(string) + name := v["name"].(string) + result := network.PrivateEndpointIPConfiguration{ + Name: utils.String(name), + PrivateEndpointIPConfigurationProperties: &network.PrivateEndpointIPConfigurationProperties{ + PrivateIPAddress: utils.String(privateIPAddress), + GroupID: utils.String(subResourceName), + MemberName: utils.String(subResourceName), + }, + } + results = append(results, result) + } + + return &results +} + +func flattenPrivateEndpointIPConfigurations(ipConfigurations *[]network.PrivateEndpointIPConfiguration) []interface{} { + results := make([]interface{}, 0) + if ipConfigurations == nil { + return results + } + + for _, item := range *ipConfigurations { + results = append(results, map[string]interface{}{ + "name": item.Name, + "private_ip_address": item.PrivateIPAddress, + "subresource_name": item.GroupID, + }) + } + + return results +} + func flattenCustomDnsConfigs(customDnsConfigs *[]network.CustomDNSConfigPropertiesFormat) []interface{} { results := make([]interface{}, 0) if customDnsConfigs == nil { @@ -663,7 +831,7 @@ func flattenPrivateLinkEndpointServiceConnection(serviceConnections *[]network.P // There is a bug from service, the PE created from portal could be with the connection id for postgresql server "Microsoft.DBForPostgreSQL" instead of "Microsoft.DBforPostgreSQL" // and for Mysql and MariaDB if strings.Contains(strings.ToLower(privateConnectionId), "microsoft.dbforpostgresql") { - if serverId, err := postgresqlParse.ServerID(privateConnectionId); err == nil { + if serverId, err := servers.ParseServerID(privateConnectionId); err == nil { privateConnectionId = serverId.ID() } } @@ -673,7 +841,7 @@ func flattenPrivateLinkEndpointServiceConnection(serviceConnections *[]network.P } } if strings.Contains(strings.ToLower(privateConnectionId), "microsoft.dbformariadb") { - if serverId, err := mariaDBParse.ServerID(privateConnectionId); err == nil { + if serverId, err := mariaDB.ParseServerID(privateConnectionId); err == nil { privateConnectionId = serverId.ID() } } @@ -725,7 +893,7 @@ func flattenPrivateLinkEndpointServiceConnection(serviceConnections *[]network.P // There is a bug from service, the PE created from portal could be with the connection id for postgresql server "Microsoft.DBForPostgreSQL" instead of "Microsoft.DBforPostgreSQL" // and for Mysql and MariaDB if strings.Contains(strings.ToLower(privateConnectionId), "microsoft.dbforpostgresql") { - if serverId, err := postgresqlParse.ServerID(privateConnectionId); err == nil { + if serverId, err := servers.ParseServerID(privateConnectionId); err == nil { privateConnectionId = serverId.ID() } } @@ -735,7 +903,7 @@ func flattenPrivateLinkEndpointServiceConnection(serviceConnections *[]network.P } } if strings.Contains(strings.ToLower(privateConnectionId), "microsoft.dbformariadb") { - if serverId, err := mariaDBParse.ServerID(privateConnectionId); err == nil { + if serverId, err := mariaDB.ParseServerID(privateConnectionId); err == nil { privateConnectionId = serverId.ID() } } diff --git a/internal/services/network/private_endpoint_resource_test.go b/internal/services/network/private_endpoint_resource_test.go index 56bd2a674ce1..e32e8b8fdbc0 100644 --- a/internal/services/network/private_endpoint_resource_test.go +++ b/internal/services/network/private_endpoint_resource_test.go @@ -215,7 +215,7 @@ func TestAccPrivateEndpoint_privateConnectionAlias(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.privateConnectionAlias(data), + Config: r.privateConnectionAlias(data, false), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("subnet_id").Exists(), @@ -228,6 +228,22 @@ func TestAccPrivateEndpoint_privateConnectionAlias(t *testing.T) { }) } +func TestAccPrivateEndpoint_updateToPrivateConnectionAlias(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_private_endpoint", "test") + r := PrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateConnectionAlias(data, false), + }, + data.ImportStep(), + { + Config: r.privateConnectionAlias(data, true), + }, + data.ImportStep(), + }) +} + func (t PrivateEndpointResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.PrivateEndpointID(state.ID) if err != nil { @@ -242,6 +258,25 @@ func (t PrivateEndpointResource) Exists(ctx context.Context, clients *clients.Cl return utils.Bool(resp.ID != nil), nil } +func TestAccPrivateEndpoint_multipleInstances(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_private_endpoint", "test") + r := PrivateEndpointResource{} + + instanceCount := 5 + var checks []pluginsdk.TestCheckFunc + for i := 0; i < instanceCount; i++ { + checks = append(checks, check.That(fmt.Sprintf("%s.%d", data.ResourceName, i)).ExistsInAzure(r)) + } + + config := r.multipleInstances(data, instanceCount) + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: config, + Check: acceptance.ComposeTestCheckFunc(checks...), + }, + }) +} + func (PrivateEndpointResource) template(data acceptance.TestData, seviceCfg string) string { return fmt.Sprintf(` provider "azurerm" { @@ -251,7 +286,7 @@ provider "azurerm" { data "azurerm_subscription" "current" {} resource "azurerm_resource_group" "test" { - name = "acctestRG-privatelink-%d" + name = "zjhe-acctestRG-privatelink-%d" location = "%s" } @@ -416,7 +451,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-privatelink-%d" + name = "zjhe-acctestRG-privatelink-%d" location = "%s" } @@ -496,7 +531,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-privatelink-%d" + name = "zjhe-acctestRG-privatelink-%d" location = "%s" } @@ -571,7 +606,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-privatelink-%d" + name = "zjhe-acctestRG-privatelink-%d" location = "%s" } @@ -656,7 +691,7 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-privatelink-%d" + name = "zjhe-acctestRG-privatelink-%d" location = "%s" } @@ -729,7 +764,15 @@ resource "azurerm_private_endpoint" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -func (r PrivateEndpointResource) privateConnectionAlias(data acceptance.TestData) string { +func (r PrivateEndpointResource) privateConnectionAlias(data acceptance.TestData, withTags bool) string { + tags := ` + tags = { + env = "TEST" + } +` + if !withTags { + tags = "" + } return fmt.Sprintf(` %s @@ -745,6 +788,27 @@ resource "azurerm_private_endpoint" "test" { private_connection_resource_alias = azurerm_private_link_service.test.alias request_message = "test" } +%s } -`, r.template(data, r.serviceAutoApprove(data)), data.RandomInteger) +`, r.template(data, r.serviceAutoApprove(data)), data.RandomInteger, tags) +} + +func (r PrivateEndpointResource) multipleInstances(data acceptance.TestData, count int) string { + return fmt.Sprintf(` +%s + +resource "azurerm_private_endpoint" "test" { + count = %d + name = "acctest-privatelink-%d-${count.index}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + subnet_id = azurerm_subnet.endpoint.id + + private_service_connection { + name = azurerm_private_link_service.test.name + is_manual_connection = false + private_connection_resource_id = azurerm_private_link_service.test.id + } +} +`, r.template(data, r.serviceAutoApprove(data)), count, data.RandomInteger) } diff --git a/internal/services/network/registration.go b/internal/services/network/registration.go index 9980984609b9..87082130a4a8 100644 --- a/internal/services/network/registration.go +++ b/internal/services/network/registration.go @@ -93,6 +93,8 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_route_filter": resourceRouteFilter(), "azurerm_route_table": resourceRouteTable(), "azurerm_route": resourceRoute(), + "azurerm_route_server": resourceRouteServer(), + "azurerm_route_server_bgp_connection": resourceRouteServerBgpConnection(), "azurerm_virtual_hub_security_partner_provider": resourceVirtualHubSecurityPartnerProvider(), "azurerm_subnet_service_endpoint_storage_policy": resourceSubnetServiceEndpointStoragePolicy(), "azurerm_subnet_network_security_group_association": resourceSubnetNetworkSecurityGroupAssociation(), diff --git a/internal/services/network/resourceids.go b/internal/services/network/resourceids.go index bcd1ae1ebdd9..793ef0434518 100644 --- a/internal/services/network/resourceids.go +++ b/internal/services/network/resourceids.go @@ -105,3 +105,6 @@ package network // Load balancer //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=InboundNatRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/loadBalancers/loadBalancer1/inboundNatRules/natrule1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LoadBalancerBackendAddressPool -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/loadBalancers/loadBalancer1/backendAddressPools/backendAddressPool1 + +// Virtual Machine Scale Set +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=VirtualMachineScaleSetPublicIPAddress -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/publicIpAddress1 diff --git a/internal/services/network/route_server_bgp_connection_resource.go b/internal/services/network/route_server_bgp_connection_resource.go new file mode 100644 index 000000000000..e88667a5b8d7 --- /dev/null +++ b/internal/services/network/route_server_bgp_connection_resource.go @@ -0,0 +1,170 @@ +package network + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/locks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceRouteServerBgpConnection() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceRouteServerBgpConnectionCreate, + Read: resourceRouteServerBgpConnectionRead, + Delete: resourceRouteServerBgpConnectionDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.BgpConnectionID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "route_server_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.VirtualHubID, + }, + + "peer_asn": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(0), + }, + + "peer_ip": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + } +} + +func resourceRouteServerBgpConnectionCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.VirtualHubBgpConnectionClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + routerServerId, err := parse.VirtualHubID(d.Get("route_server_id").(string)) + if err != nil { + return err + } + + locks.ByName(routerServerId.Name, "azurerm_route_server") + defer locks.UnlockByName(routerServerId.Name, "azurerm_route_server") + + id := parse.NewBgpConnectionID(routerServerId.SubscriptionId, routerServerId.ResourceGroup, routerServerId.Name, d.Get("name").(string)) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.VirtualHubName, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_route_server_bgp_connection", id.ID()) + } + } + parameters := network.BgpConnection{ + Name: utils.String(d.Get("name").(string)), + BgpConnectionProperties: &network.BgpConnectionProperties{ + PeerAsn: utils.Int64(int64(d.Get("peer_asn").(int))), + PeerIP: utils.String(d.Get("peer_ip").(string)), + }, + } + + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.VirtualHubName, id.Name, parameters) + if err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting on creation/update future for %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceRouteServerBgpConnectionRead(d, meta) +} + +func resourceRouteServerBgpConnectionRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.VirtualHubBgpConnectionClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.BgpConnectionID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.VirtualHubName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] BGP Connection %s does not exists", id) + d.SetId("") + return nil + } + + return fmt.Errorf("retrieving Route Server BGP Connection %s: %+v", id, err) + } + + d.Set("name", id.Name) + d.Set("route_server_id", parse.NewVirtualHubID(id.SubscriptionId, id.ResourceGroup, id.VirtualHubName).ID()) + + if props := resp.BgpConnectionProperties; props != nil { + if props.PeerAsn != nil { + d.Set("peer_asn", props.PeerAsn) + } + if props.PeerIP != nil { + d.Set("peer_ip", props.PeerIP) + } + } + return nil +} + +func resourceRouteServerBgpConnectionDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.VirtualHubBgpConnectionClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.BgpConnectionID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.VirtualHubName, id.Name) + if err != nil { + return fmt.Errorf("deleting BGP Connection %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting on deletion future for BGP Connection %s: %+v", id, err) + } + return nil +} diff --git a/internal/services/network/route_server_bgp_connection_resource_test.go b/internal/services/network/route_server_bgp_connection_resource_test.go new file mode 100644 index 000000000000..d65b0343378f --- /dev/null +++ b/internal/services/network/route_server_bgp_connection_resource_test.go @@ -0,0 +1,83 @@ +package network_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type RouteServerBGPConnectionResource struct{} + +func TestAccRouteServerBgpConnection_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server_bgp_connection", "test") + r := RouteServerBGPConnectionResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccRouteServerBgpConnection_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server_bgp_connection", "test") + r := RouteServerBGPConnectionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (r RouteServerBGPConnectionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.BgpConnectionID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Network.VirtualHubBgpConnectionClient.Get(ctx, id.ResourceGroup, id.VirtualHubName, id.Name) + if err != nil { + return nil, fmt.Errorf("reading route server bgp connection %s: %+v", id, err) + } + return utils.Bool(resp.ID != nil), nil +} + +func (r RouteServerBGPConnectionResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_route_server_bgp_connection" "test" { + name = "acctest-rs-bgp-%d" + route_server_id = azurerm_route_server.test.id + peer_asn = 65501 + peer_ip = "169.254.21.5" + +} +`, RouteServerResource{}.basic(data), data.RandomInteger) +} + +func (r RouteServerBGPConnectionResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_route_server_bgp_connection" "import" { + name = azurerm_route_server_bgp_connection.test.name + route_server_id = azurerm_route_server_bgp_connection.test.route_server_id + peer_asn = azurerm_route_server_bgp_connection.test.peer_asn + peer_ip = azurerm_route_server_bgp_connection.test.peer_ip +} +`, r.basic(data)) +} diff --git a/internal/services/network/route_server_resource.go b/internal/services/network/route_server_resource.go new file mode 100644 index 000000000000..01dd1855cb87 --- /dev/null +++ b/internal/services/network/route_server_resource.go @@ -0,0 +1,343 @@ +package network + +import ( + "context" + "fmt" + "log" + "strconv" + "time" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/locks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceRouteServer() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceRouteServerCreateUpdate, + Read: resourceRouteServerRead, + Update: resourceRouteServerCreateUpdate, + Delete: resourceRouteServerDelete, + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.VirtualHubID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(60 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(60 * time.Minute), + Delete: pluginsdk.DefaultTimeout(60 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.RouteServerName(), + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "sku": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"Standard"}, false), + }, + + "public_ip_address_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.PublicIpAddressID, + }, + + "subnet_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.SubnetID, + }, + + "branch_to_branch_traffic_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "virtual_router_ips": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "routing_state": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "virtual_router_asn": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "tags": commonschema.Tags(), + }, + } +} + +func resourceRouteServerCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + serverClient := meta.(*clients.Client).Network.VirtualHubClient + ipClient := meta.(*clients.Client).Network.VirtualHubIPClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id := parse.NewVirtualHubID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + + locks.ByName(id.Name, "azurerm_route_server") + defer locks.UnlockByName(id.Name, "azurerm_route_server") + + if d.IsNewResource() { + existing, err := serverClient.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for present of existing Route Server %q (Resource Group Name %q): %+v", id.Name, id.ResourceGroup, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_route_server", id.ID()) + } + } + + location := location.Normalize(d.Get("location").(string)) + t := tags.Expand(d.Get("tags").(map[string]interface{})) + + parameters := network.VirtualHub{ + Location: utils.String(location), + VirtualHubProperties: &network.VirtualHubProperties{ + Sku: utils.String(d.Get("sku").(string)), + AllowBranchToBranchTraffic: utils.Bool(d.Get("branch_to_branch_traffic_enabled").(bool)), + }, + Tags: t, + } + + if _, err := serverClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, parameters); err != nil { + return fmt.Errorf("creating Route Server %q (Resource Group Name %q): %+v", id.Name, id.ResourceGroup, err) + } + + timeout, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Provisioning", "Updating"}, + Target: []string{"Succeeded", "Provisioned"}, + Refresh: routeServerCreateRefreshFunc(ctx, serverClient, id), + PollInterval: 15 * time.Second, + ContinuousTargetOccurence: 5, + Timeout: time.Until(timeout), + } + _, err := stateConf.WaitForStateContext(ctx) + if err != nil { + return fmt.Errorf("waiting for creation/update of Route Server %q (Resource Group Name %q): %+v", id.Name, id.ResourceGroup, err) + } + + ipConfigName := "ipConfig1" + ipConfigs := network.HubIPConfiguration{ + Name: utils.String(ipConfigName), + HubIPConfigurationPropertiesFormat: &network.HubIPConfigurationPropertiesFormat{ + PublicIPAddress: &network.PublicIPAddress{ + ID: utils.String(d.Get("public_ip_address_id").(string)), + }, + Subnet: &network.Subnet{ + ID: utils.String(d.Get("subnet_id").(string)), + }, + }, + } + + future, err := ipClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, ipConfigName, ipConfigs) + if err != nil { + return fmt.Errorf("creating/updating IP Configuration %q of Route Server %q (Resource Group Name %q): %+v", ipConfigName, id.Name, id.ResourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, ipClient.Client); err != nil { + return fmt.Errorf("waiting on creation/update for IP Configuration %q of Route Server %q (Resource Group Name %q): %+v", ipConfigName, id.Name, id.ResourceGroup, err) + } + d.SetId(id.ID()) + + return resourceRouteServerRead(d, meta) +} + +func resourceRouteServerRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.VirtualHubClient + ipClient := meta.(*clients.Client).Network.VirtualHubIPClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.VirtualHubID(d.Id()) + if err != nil { + return err + } + + routeServer, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(routeServer.Response) { + log.Printf("[INFO] Route Server %s does not exists - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("reading Route Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + d.Set("name", id.Name) + d.Set("resource_group_name", id.ResourceGroup) + if location := routeServer.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + if props := routeServer.VirtualHubProperties; props != nil { + d.Set("sku", props.Sku) + var virtualRouterIps *[]string + if props.VirtualRouterIps != nil { + virtualRouterIps = props.VirtualRouterIps + } + d.Set("virtual_router_ips", virtualRouterIps) + if props.AllowBranchToBranchTraffic != nil { + d.Set("branch_to_branch_traffic_enabled", props.AllowBranchToBranchTraffic) + } + if props.VirtualRouterAsn != nil { + d.Set("virtual_router_asn", props.VirtualRouterAsn) + } + d.Set("routing_state", string(props.RoutingState)) + } + + ipConfig, err := ipClient.List(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("retrieving IP Config for Router Server %q (Resource Group Name %q): %+v", id.Name, id.ResourceGroup, err) + } + + for _, setting := range ipConfig.Values() { + if ipProps := setting.HubIPConfigurationPropertiesFormat; ipProps != nil { + if ipProps.PublicIPAddress != nil { + d.Set("public_ip_address_id", ipProps.PublicIPAddress.ID) + } + if ipProps.Subnet != nil { + d.Set("subnet_id", ipProps.Subnet.ID) + } + } + } + return tags.FlattenAndSet(d, routeServer.Tags) +} + +func resourceRouteServerDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.VirtualHubClient + ipClient := meta.(*clients.Client).Network.VirtualHubIPClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + routeServerId, err := parse.VirtualHubID(d.Id()) + if err != nil { + return err + } + + ipConfig, err := ipClient.List(ctx, routeServerId.ResourceGroup, routeServerId.Name) + if err != nil { + return fmt.Errorf("retrieving IP Config for Router Server %q (Resource Group Name %q): %+v", routeServerId.Name, routeServerId.ResourceGroup, err) + } + var ipName string + for _, setting := range ipConfig.Values() { + if setting.Name != nil { + ipName = *setting.Name + } + } + ipConfigId := parse.NewVirtualHubIpConfigurationID(routeServerId.SubscriptionId, routeServerId.ResourceGroup, routeServerId.Name, ipName) + + if ipConfig.Values() != nil { + if err := deleteRouteServerIpConfiguration(ctx, ipClient, ipConfigId); err != nil { + return err + } + } + + future, err := client.Delete(ctx, routeServerId.ResourceGroup, routeServerId.Name) + if err != nil { + return fmt.Errorf("deleting Route Server %q (Resource Group Name %q): %+v", routeServerId.Name, routeServerId.ResourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("waiting for Route Server %q (Resource Group Name %q): %+v", routeServerId.Name, routeServerId.ResourceGroup, err) + } + } + + return nil +} + +func deleteRouteServerIpConfiguration(ctx context.Context, client *network.VirtualHubIPConfigurationClient, id parse.VirtualHubIpConfigurationId) error { + future, err := client.Delete(ctx, id.ResourceGroup, id.VirtualHubName, id.IpConfigurationName) + if err != nil { + return fmt.Errorf("deleting Router Server IP Config %s for Route Server %q (Resource Group Name %q): %+v", id.IpConfigurationName, id.VirtualHubName, id.ResourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("waiting for deletion of Route Server IP Config %s: %+v", id.IpConfigurationName, err) + } + } + timeout, _ := ctx.Deadline() + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"200"}, + Target: []string{"404"}, + Refresh: ipConfigStateRefreshFunc(ctx, client, id), + Timeout: time.Until(timeout), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for Router Server IP Config %s for Route Server %q (Resource Group Name %q): %+v", id.IpConfigurationName, id.VirtualHubName, id.ResourceGroup, err) + } + return nil +} + +func routeServerCreateRefreshFunc(ctx context.Context, client *network.VirtualHubsClient, id parse.VirtualHubId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(res.Response) { + return nil, "", fmt.Errorf("Route Server %q (Resource Group Name %q) does not exists", id.Name, id.ResourceGroup) + } + return nil, "", fmt.Errorf("retrieving Route Server %q (Resource Group Name %q) error", id.Name, id.ResourceGroup) + } + + if res.VirtualHubProperties != nil { + return res, string(res.VirtualHubProperties.ProvisioningState), nil + } + return nil, "", fmt.Errorf("unable to read the provisioning state of this Route Server %q (Resource Group Name %q)", id.Name, id.ResourceGroup) + } +} + +func ipConfigStateRefreshFunc(ctx context.Context, client *network.VirtualHubIPConfigurationClient, id parse.VirtualHubIpConfigurationId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id.ResourceGroup, id.VirtualHubName, id.IpConfigurationName) + if err != nil { + if utils.ResponseWasNotFound(res.Response) { + return res, strconv.Itoa(res.StatusCode), nil + } + return nil, "", fmt.Errorf("polling for the status of route server ip config %s: %+v", id, err) + } + return res, strconv.Itoa(res.StatusCode), nil + } +} diff --git a/internal/services/network/route_server_resource_test.go b/internal/services/network/route_server_resource_test.go new file mode 100644 index 000000000000..2ac78d7597e3 --- /dev/null +++ b/internal/services/network/route_server_resource_test.go @@ -0,0 +1,183 @@ +package network_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type RouteServerResource struct{} + +func TestAccRouteServer_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server", "test") + r := RouteServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccRouteServer_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server", "test") + r := RouteServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + { + Config: r.requiresImport(data), + ExpectError: acceptance.RequiresImportError("azurerm_route_server"), + }, + }) +} + +func TestAccRouteServer_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server", "test") + r := RouteServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccRouteServer_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_route_server", "test") + r := RouteServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} +func (r RouteServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + routeServerId, err := parse.VirtualHubID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Network.VirtualHubClient.Get(ctx, routeServerId.ResourceGroup, routeServerId.Name) + if err != nil { + return nil, fmt.Errorf("reading Route Server %s: %+v", routeServerId, err) + } + + ipConfig, err := clients.Network.VirtualHubIPClient.List(ctx, routeServerId.ResourceGroup, routeServerId.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Ip Config for Route Server %s: %+v", routeServerId, err) + } + if ipConfig.Values() == nil { + return nil, fmt.Errorf("no IP Config is set for the Route Server %s: %+v", routeServerId, err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (r RouteServerResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_route_server" "test" { + name = "acctestrs-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Standard" + public_ip_address_id = azurerm_public_ip.test.id + subnet_id = azurerm_subnet.test.id +} +`, r.template(data), data.RandomInteger) +} + +func (r RouteServerResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_route_server" "import" { + name = azurerm_route_server.test.name + resource_group_name = azurerm_route_server.test.resource_group_name + location = azurerm_route_server.test.location + sku = azurerm_route_server.test.sku + public_ip_address_id = azurerm_route_server.test.public_ip_address_id + subnet_id = azurerm_route_server.test.subnet_id +} +`, r.basic(data)) +} + +func (r RouteServerResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_route_server" "test" { + name = "acctestrs-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Standard" + public_ip_address_id = azurerm_public_ip.test.id + subnet_id = azurerm_subnet.test.id + branch_to_branch_traffic_enabled = true +} +`, r.template(data), data.RandomInteger) +} + +func (r RouteServerResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvn-%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 = "RouteServerSubnet" + virtual_network_name = azurerm_virtual_network.test.name + resource_group_name = azurerm_resource_group.test.name + address_prefixes = ["10.0.0.0/24"] +} + +resource "azurerm_public_ip" "test" { + name = "acctestpip-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + allocation_method = "Static" + sku = "Standard" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/network/subnet_data_source.go b/internal/services/network/subnet_data_source.go index 09fc30a119bc..41dfe63549d3 100644 --- a/internal/services/network/subnet_data_source.go +++ b/internal/services/network/subnet_data_source.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -14,7 +15,7 @@ import ( ) func dataSourceSubnet() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Read: dataSourceSubnetRead, Timeouts: &pluginsdk.ResourceTimeout{ @@ -64,18 +65,31 @@ func dataSourceSubnet() *pluginsdk.Resource { Type: pluginsdk.TypeString, }, }, - - "enforce_private_link_endpoint_network_policies": { + "private_endpoint_network_policies_enabled": { Type: pluginsdk.TypeBool, Computed: true, }, - "enforce_private_link_service_network_policies": { + "private_link_service_network_policies_enabled": { Type: pluginsdk.TypeBool, Computed: true, }, }, } + + if !features.FourPointOhBeta() { + resource.Schema["enforce_private_link_endpoint_network_policies"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Computed: true, + } + + resource.Schema["enforce_private_link_service_network_policies"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Computed: true, + } + } + + return resource } func dataSourceSubnetRead(d *pluginsdk.ResourceData, meta interface{}) error { @@ -88,7 +102,7 @@ func dataSourceSubnetRead(d *pluginsdk.ResourceData, meta interface{}) error { resp, err := client.Get(ctx, id.ResourceGroup, id.VirtualNetworkName, id.Name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: %s was not found", id) + return fmt.Errorf("%s was not found", id) } return fmt.Errorf("retrieving %s: %+v", id, err) } @@ -110,8 +124,13 @@ func dataSourceSubnetRead(d *pluginsdk.ResourceData, meta interface{}) error { d.Set("address_prefixes", utils.FlattenStringSlice(props.AddressPrefixes)) } - d.Set("enforce_private_link_endpoint_network_policies", flattenSubnetPrivateLinkNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) - d.Set("enforce_private_link_service_network_policies", flattenSubnetPrivateLinkNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) + if !features.FourPointOhBeta() { + d.Set("enforce_private_link_endpoint_network_policies", flattenEnforceSubnetNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) + d.Set("enforce_private_link_service_network_policies", flattenEnforceSubnetNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) + } + + d.Set("private_endpoint_network_policies_enabled", flattenSubnetNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) + d.Set("private_link_service_network_policies_enabled", flattenSubnetNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) networkSecurityGroupId := "" if props.NetworkSecurityGroup != nil && props.NetworkSecurityGroup.ID != nil { diff --git a/internal/services/network/subnet_resource.go b/internal/services/network/subnet_resource.go index 4b59b47d7608..634857b0f371 100644 --- a/internal/services/network/subnet_resource.go +++ b/internal/services/network/subnet_resource.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" @@ -24,7 +25,7 @@ import ( var SubnetResourceName = "azurerm_subnet" func resourceSubnet() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Create: resourceSubnetCreate, Read: resourceSubnetRead, Update: resourceSubnetUpdate, @@ -131,6 +132,9 @@ func resourceSubnet() *pluginsdk.Resource { "Microsoft.Synapse/workspaces", "Microsoft.Web/hostingEnvironments", "Microsoft.Web/serverFarms", + "Microsoft.Orbital/orbitalGateways", + "NGINX.NGINXPLUS/nginxDeployments", + "PaloAltoNetworks.Cloudngfw/firewalls", }, false), }, @@ -157,19 +161,67 @@ func resourceSubnet() *pluginsdk.Resource { }, }, - "enforce_private_link_endpoint_network_policies": { - Type: pluginsdk.TypeBool, + "private_endpoint_network_policies_enabled": { + Type: pluginsdk.TypeBool, + Computed: func() bool { + return !features.FourPointOh() + }(), Optional: true, - Default: false, + Default: func() interface{} { + if !features.FourPointOh() { + return nil + } + return !features.FourPointOh() + }(), + ConflictsWith: func() []string { + if !features.FourPointOh() { + return []string{"enforce_private_link_endpoint_network_policies"} + } + return []string{} + }(), }, - "enforce_private_link_service_network_policies": { - Type: pluginsdk.TypeBool, + "private_link_service_network_policies_enabled": { + Type: pluginsdk.TypeBool, + Computed: func() bool { + return !features.FourPointOh() + }(), Optional: true, - Default: false, + Default: func() interface{} { + if !features.FourPointOh() { + return nil + } + return features.FourPointOh() + }(), + ConflictsWith: func() []string { + if !features.FourPointOh() { + return []string{"enforce_private_link_service_network_policies"} + } + return []string{} + }(), }, }, } + + if !features.FourPointOhBeta() { + resource.Schema["enforce_private_link_endpoint_network_policies"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Computed: true, + Optional: true, + Deprecated: "`enforce_private_link_endpoint_network_policies` will be removed in favour of the property `private_endpoint_network_policies_enabled` in version 4.0 of the AzureRM Provider", + ConflictsWith: []string{"private_endpoint_network_policies_enabled"}, + } + + resource.Schema["enforce_private_link_service_network_policies"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Computed: true, + Optional: true, + Deprecated: "`enforce_private_link_service_network_policies` will be removed in favour of the property `private_link_service_network_policies_enabled` in version 4.0 of the AzureRM Provider", + ConflictsWith: []string{"private_link_service_network_policies_enabled"}, + } + } + + return resource } // TODO: refactor the create/flatten functions @@ -212,10 +264,72 @@ func resourceSubnetCreate(d *pluginsdk.ResourceData, meta interface{}) error { // To enable private endpoints you must disable the network policies for the subnet because // Network policies like network security groups are not supported by private endpoints. - privateEndpointNetworkPolicies := d.Get("enforce_private_link_endpoint_network_policies").(bool) - privateLinkServiceNetworkPolicies := d.Get("enforce_private_link_service_network_policies").(bool) - properties.PrivateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetPrivateLinkNetworkPolicy(privateEndpointNetworkPolicies)) - properties.PrivateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetPrivateLinkNetworkPolicy(privateLinkServiceNetworkPolicies)) + var privateEndpointNetworkPolicies network.VirtualNetworkPrivateEndpointNetworkPolicies + var privateLinkServiceNetworkPolicies network.VirtualNetworkPrivateLinkServiceNetworkPolicies + + if features.FourPointOhBeta() { + privateEndpointNetworkPoliciesRaw := d.Get("private_endpoint_network_policies_enabled").(bool) + privateLinkServiceNetworkPoliciesRaw := d.Get("private_link_service_network_policies_enabled").(bool) + + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetNetworkPolicy(privateEndpointNetworkPoliciesRaw)) + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetNetworkPolicy(privateLinkServiceNetworkPoliciesRaw)) + } else { + var enforceOk bool + var enforceServiceOk bool + var enableOk bool + var enableServiceOk bool + var enforcePrivateEndpointNetworkPoliciesRaw bool + var enforcePrivateLinkServiceNetworkPoliciesRaw bool + var privateEndpointNetworkPoliciesRaw bool + var privateLinkServiceNetworkPoliciesRaw bool + + // Set the legacy default value since they are now computed optional + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPoliciesEnabled + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPoliciesEnabled + + // This is the only way I was able to figure out if the fields are actually in the config or not, + // which is needed here because these are all now optional computed fields... + if !pluginsdk.IsExplicitlyNullInConfig(d, "enforce_private_link_endpoint_network_policies") { + enforceOk = true + enforcePrivateEndpointNetworkPoliciesRaw = d.Get("enforce_private_link_endpoint_network_policies").(bool) + } + + if !pluginsdk.IsExplicitlyNullInConfig(d, "enforce_private_link_service_network_policies") { + enforceServiceOk = true + enforcePrivateLinkServiceNetworkPoliciesRaw = d.Get("enforce_private_link_service_network_policies").(bool) + } + + if !pluginsdk.IsExplicitlyNullInConfig(d, "private_endpoint_network_policies_enabled") { + enableOk = true + privateEndpointNetworkPoliciesRaw = d.Get("private_endpoint_network_policies_enabled").(bool) + } + + if !pluginsdk.IsExplicitlyNullInConfig(d, "private_link_service_network_policies_enabled") { + enableServiceOk = true + privateLinkServiceNetworkPoliciesRaw = d.Get("private_link_service_network_policies_enabled").(bool) + } + + // Only one of these values can be set since they conflict with each other + // if neither of them are set use the default values + if enforceOk || enableOk { + if enforceOk { + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandEnforceSubnetNetworkPolicy(enforcePrivateEndpointNetworkPoliciesRaw)) + } else if enableOk { + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetNetworkPolicy(privateEndpointNetworkPoliciesRaw)) + } + } + + if enforceServiceOk || enableServiceOk { + if enforceServiceOk { + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandEnforceSubnetNetworkPolicy(enforcePrivateLinkServiceNetworkPoliciesRaw)) + } else if enableServiceOk { + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetNetworkPolicy(privateLinkServiceNetworkPoliciesRaw)) + } + } + } + + properties.PrivateEndpointNetworkPolicies = privateEndpointNetworkPolicies + properties.PrivateLinkServiceNetworkPolicies = privateLinkServiceNetworkPolicies serviceEndpointPoliciesRaw := d.Get("service_endpoint_policy_ids").(*pluginsdk.Set).List() properties.ServiceEndpointPolicies = expandSubnetServiceEndpointPolicies(serviceEndpointPoliciesRaw) @@ -321,14 +435,50 @@ func resourceSubnetUpdate(d *pluginsdk.ResourceData, meta interface{}) error { props.Delegations = expandSubnetDelegation(delegationsRaw) } - if d.HasChange("enforce_private_link_endpoint_network_policies") { - v := d.Get("enforce_private_link_endpoint_network_policies").(bool) - props.PrivateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetPrivateLinkNetworkPolicy(v)) - } + if features.FourPointOhBeta() { + if d.HasChange("private_endpoint_network_policies_enabled") { + v := d.Get("private_endpoint_network_policies_enabled").(bool) + props.PrivateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetNetworkPolicy(v)) + } + + if d.HasChange("private_link_service_network_policies_enabled") { + v := d.Get("private_link_service_network_policies_enabled").(bool) + props.PrivateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetNetworkPolicy(v)) + } + } else { + // This is the best case we can do in this state since they are computed optional fields now + // If you remove the fields from the config they will just persist as they are, if you change + // one it will update it to the value that was changed and in the read the other value will be + // updated as well to reflect the new value so it is safe to toggle between which field you want + // to use to define this behavior... + var privateEndpointNetworkPolicies network.VirtualNetworkPrivateEndpointNetworkPolicies + var privateLinkServiceNetworkPolicies network.VirtualNetworkPrivateLinkServiceNetworkPolicies + + if d.HasChange("enforce_private_link_endpoint_network_policies") || d.HasChange("private_endpoint_network_policies_enabled") { + enforcePrivateEndpointNetworkPoliciesRaw := d.Get("enforce_private_link_endpoint_network_policies").(bool) + privateEndpointNetworkPoliciesRaw := d.Get("private_endpoint_network_policies_enabled").(bool) + + if d.HasChange("enforce_private_link_endpoint_network_policies") { + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandEnforceSubnetNetworkPolicy(enforcePrivateEndpointNetworkPoliciesRaw)) + } else if d.HasChange("private_endpoint_network_policies_enabled") { + privateEndpointNetworkPolicies = network.VirtualNetworkPrivateEndpointNetworkPolicies(expandSubnetNetworkPolicy(privateEndpointNetworkPoliciesRaw)) + } + + props.PrivateEndpointNetworkPolicies = privateEndpointNetworkPolicies + } - if d.HasChange("enforce_private_link_service_network_policies") { - v := d.Get("enforce_private_link_service_network_policies").(bool) - props.PrivateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetPrivateLinkNetworkPolicy(v)) + if d.HasChange("enforce_private_link_service_network_policies") || d.HasChange("private_link_service_network_policies_enabled") { + enforcePrivateLinkServiceNetworkPoliciesRaw := d.Get("enforce_private_link_service_network_policies").(bool) + privateLinkServiceNetworkPoliciesRaw := d.Get("private_link_service_network_policies_enabled").(bool) + + if d.HasChange("enforce_private_link_service_network_policies") { + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandEnforceSubnetNetworkPolicy(enforcePrivateLinkServiceNetworkPoliciesRaw)) + } else if d.HasChange("private_link_service_network_policies_enabled") { + privateLinkServiceNetworkPolicies = network.VirtualNetworkPrivateLinkServiceNetworkPolicies(expandSubnetNetworkPolicy(privateLinkServiceNetworkPoliciesRaw)) + } + + props.PrivateLinkServiceNetworkPolicies = privateLinkServiceNetworkPolicies + } } if d.HasChange("service_endpoints") { @@ -376,6 +526,7 @@ func resourceSubnetUpdate(d *pluginsdk.ResourceData, meta interface{}) error { MinTimeout: 1 * time.Minute, Timeout: time.Until(timeout), } + if _, err = vnetStateConf.WaitForStateContext(ctx); err != nil { return fmt.Errorf("waiting for provisioning state of virtual network for %s: %+v", id, err) } @@ -422,8 +573,13 @@ func resourceSubnetRead(d *pluginsdk.ResourceData, meta interface{}) error { return fmt.Errorf("flattening `delegation`: %+v", err) } - d.Set("enforce_private_link_endpoint_network_policies", flattenSubnetPrivateLinkNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) - d.Set("enforce_private_link_service_network_policies", flattenSubnetPrivateLinkNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) + if !features.FourPointOhBeta() { + d.Set("enforce_private_link_endpoint_network_policies", flattenEnforceSubnetNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) + d.Set("enforce_private_link_service_network_policies", flattenEnforceSubnetNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) + } + + d.Set("private_endpoint_network_policies_enabled", flattenSubnetNetworkPolicy(string(props.PrivateEndpointNetworkPolicies))) + d.Set("private_link_service_network_policies_enabled", flattenSubnetNetworkPolicy(string(props.PrivateLinkServiceNetworkPolicies))) serviceEndpoints := flattenSubnetServiceEndpoints(props.ServiceEndpoints) if err := d.Set("service_endpoints", serviceEndpoints); err != nil { @@ -566,24 +722,36 @@ func flattenSubnetDelegation(delegations *[]network.Delegation) []interface{} { return retDeles } -// TODO: confirm this logic below - -func expandSubnetPrivateLinkNetworkPolicy(enabled bool) string { +// TODO 4.0: Remove expandEnforceSubnetPrivateLinkNetworkPolicy function +func expandEnforceSubnetNetworkPolicy(enabled bool) string { // This is strange logic, but to get the schema to make sense for the end user // I exposed it with the same name that the Azure CLI does to be consistent // between the tool sets, which means true == Disabled. if enabled { - return "Disabled" + return string(network.VirtualNetworkPrivateEndpointNetworkPoliciesDisabled) + } + + return string(network.VirtualNetworkPrivateEndpointNetworkPoliciesEnabled) +} + +func expandSubnetNetworkPolicy(enabled bool) string { + if enabled { + return string(network.VirtualNetworkPrivateEndpointNetworkPoliciesEnabled) } - return "Enabled" + return string(network.VirtualNetworkPrivateEndpointNetworkPoliciesDisabled) } -func flattenSubnetPrivateLinkNetworkPolicy(input string) bool { +// TODO 4.0: Remove flattenEnforceSubnetPrivateLinkNetworkPolicy function +func flattenEnforceSubnetNetworkPolicy(input string) bool { // This is strange logic, but to get the schema to make sense for the end user // I exposed it with the same name that the Azure CLI does to be consistent // between the tool sets, which means true == Disabled. - return strings.EqualFold(input, "Disabled") + return strings.EqualFold(input, string(network.VirtualNetworkPrivateEndpointNetworkPoliciesDisabled)) +} + +func flattenSubnetNetworkPolicy(input string) bool { + return strings.EqualFold(input, string(network.VirtualNetworkPrivateEndpointNetworkPoliciesEnabled)) } func expandSubnetServiceEndpointPolicies(input []interface{}) *[]network.ServiceEndpointPolicy { diff --git a/internal/services/network/subnet_resource_test.go b/internal/services/network/subnet_resource_test.go index 234ca72420fa..14e6a773dec1 100644 --- a/internal/services/network/subnet_resource_test.go +++ b/internal/services/network/subnet_resource_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" @@ -155,27 +156,27 @@ func TestAccSubnet_delegation(t *testing.T) { }) } -func TestAccSubnet_enforcePrivateLinkEndpointNetworkPolicies(t *testing.T) { +func TestAccSubnet_enablePrivateEndpointNetworkPolicies(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_subnet", "test") r := SubnetResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, true), + Config: r.enablePrivateEndpointNetworkPolicies(data, true), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, false), + Config: r.enablePrivateEndpointNetworkPolicies(data, false), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, true), + Config: r.enablePrivateEndpointNetworkPolicies(data, true), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -184,27 +185,27 @@ func TestAccSubnet_enforcePrivateLinkEndpointNetworkPolicies(t *testing.T) { }) } -func TestAccSubnet_enforcePrivateLinkServiceNetworkPolicies(t *testing.T) { +func TestAccSubnet_enablePrivateLinkServiceNetworkPolicies(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_subnet", "test") r := SubnetResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.enforcePrivateLinkServiceNetworkPolicies(data, true), + Config: r.enablePrivateLinkServiceNetworkPolicies(data, true), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.enforcePrivateLinkServiceNetworkPolicies(data, false), + Config: r.enablePrivateLinkServiceNetworkPolicies(data, false), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.enforcePrivateLinkServiceNetworkPolicies(data, true), + Config: r.enablePrivateLinkServiceNetworkPolicies(data, true), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -213,6 +214,188 @@ func TestAccSubnet_enforcePrivateLinkServiceNetworkPolicies(t *testing.T) { }) } +// TODO 4.0: Remove test +func TestAccSubnet_enforcePrivateLinkEndpointNetworkPolicies(t *testing.T) { + if !features.FourPointOhBeta() { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) + } else { + t.Skip("@WodansSon: skipping due to deprecation of the 'enforce_private_link_endpoint_network_policies' field in 4.0") + } +} + +// TODO 4.0: Remove test +func TestAccSubnet_enforcePrivateLinkServiceNetworkPolicies(t *testing.T) { + if !features.FourPointOhBeta() { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.enforcePrivateLinkServiceNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkServiceNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkServiceNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) + } else { + t.Skip("@WodansSon: skipping due to deprecation of the 'enforce_private_link_service_network_policies' field in 4.0") + } +} + +// TODO 4.0: Remove test +func TestAccSubnet_PrivateLinkPoliciesToggleWithEnforceFirst(t *testing.T) { + if !features.FourPointOhBeta() { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("true"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("false"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + { + Config: r.enablePrivateEndpointNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkServiceNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("true"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("false"), + ), + }, + data.ImportStep(), + { + Config: r.enablePrivateLinkServiceNetworkPolicies(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) + } else { + t.Skip("@WodansSon: skipping due to deprecation of the 'enforce_private_link_endpoint_network_policies' and 'enforce_private_link_service_network_policies' fields in 4.0") + } +} + +// TODO 4.0: Remove test +func TestAccSubnet_PrivateLinkPoliciesToggleWithEnabledFirst(t *testing.T) { + if !features.FourPointOhBeta() { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.enablePrivateEndpointNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("true"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("false"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkEndpointNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + { + Config: r.enablePrivateLinkServiceNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("true"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("false"), + ), + }, + data.ImportStep(), + { + Config: r.enforcePrivateLinkServiceNetworkPolicies(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) + } else { + t.Skip("@WodansSon: skipping due to deprecation of the 'enforce_private_link_endpoint_network_policies' and 'enforce_private_link_service_network_policies' fields in 4.0") + } +} + func TestAccSubnet_serviceEndpoints(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_subnet", "test") r := SubnetResource{} @@ -300,6 +483,64 @@ func TestAccSubnet_updateAddressPrefix(t *testing.T) { }) } +func TestAccSubnet_privateLinkEndpointNetworkPoliciesValidateDefaultValues(t *testing.T) { + if features.FourPointOhBeta() { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkEndpointNetworkPoliciesDefaults(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("false"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) + } else { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.privateLinkEndpointNetworkPoliciesDefaults(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("enforce_private_link_endpoint_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("enforce_private_link_service_network_policies").HasValue("false"), + check.That(data.ResourceName).Key("private_endpoint_network_policies_enabled").HasValue("true"), + check.That(data.ResourceName).Key("private_link_service_network_policies_enabled").HasValue("true"), + ), + }, + data.ImportStep(), + }) + } +} + +func TestAccSubnet_updateServiceDelegation(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_subnet", "test") + r := SubnetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.updateServiceDelegation(data, "NGINX.NGINXPLUS/nginxDeployments"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateServiceDelegation(data, "PaloAltoNetworks.Cloudngfw/firewalls"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (t SubnetResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.SubnetID(state.ID) if err != nil { @@ -481,6 +722,37 @@ resource "azurerm_subnet" "test" { `, r.template(data)) } +func (r SubnetResource) enablePrivateEndpointNetworkPolicies(data acceptance.TestData, enabled bool) string { + return fmt.Sprintf(` +%s + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + private_endpoint_network_policies_enabled = %t +} +`, r.template(data), enabled) +} + +func (r SubnetResource) enablePrivateLinkServiceNetworkPolicies(data acceptance.TestData, enabled bool) string { + return fmt.Sprintf(` +%s + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + private_link_service_network_policies_enabled = %t +} +`, r.template(data), enabled) +} + +// TODO 4.0: Remove test func (r SubnetResource) enforcePrivateLinkEndpointNetworkPolicies(data acceptance.TestData, enabled bool) string { return fmt.Sprintf(` %s @@ -496,6 +768,7 @@ resource "azurerm_subnet" "test" { `, r.template(data), enabled) } +// TODO 4.0: Remove test func (r SubnetResource) enforcePrivateLinkServiceNetworkPolicies(data acceptance.TestData, enabled bool) string { return fmt.Sprintf(` %s @@ -511,6 +784,19 @@ resource "azurerm_subnet" "test" { `, r.template(data), enabled) } +func (r SubnetResource) privateLinkEndpointNetworkPoliciesDefaults(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] +} +`, r.template(data)) +} + func (SubnetResource) basic_addressPrefixes(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -663,6 +949,31 @@ resource "azurerm_subnet" "test" { `, r.template(data)) } +func (r SubnetResource) updateServiceDelegation(data acceptance.TestData, serviceName string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "first" + + service_delegation { + name = "%s" + + actions = [ + "Microsoft.Network/virtualNetworks/subnets/join/action", + ] + } + } +} +`, r.template(data), serviceName) +} + func (SubnetResource) template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/network/validate/route_server_name.go b/internal/services/network/validate/route_server_name.go new file mode 100644 index 000000000000..6a26e46ebcc6 --- /dev/null +++ b/internal/services/network/validate/route_server_name.go @@ -0,0 +1,19 @@ +package validate + +import ( + "regexp" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +func RouteServerName() pluginsdk.SchemaValidateFunc { + return validation.All( + validation.StringIsNotEmpty, + validation.StringLenBetween(1, 80), + validation.StringMatch( + regexp.MustCompile(`^[A-Za-z\d][A-Za-z\d.\-_]*[A-Za-z\d_]$`), + "The name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens.", + ), + ) +} diff --git a/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id.go b/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id.go new file mode 100644 index 000000000000..58a4f394a115 --- /dev/null +++ b/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" +) + +func VirtualMachineScaleSetPublicIPAddressID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.VirtualMachineScaleSetPublicIPAddressID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id_test.go b/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id_test.go new file mode 100644 index 000000000000..047140c8e474 --- /dev/null +++ b/internal/services/network/validate/virtual_machine_scale_set_public_ip_address_id_test.go @@ -0,0 +1,124 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestVirtualMachineScaleSetPublicIPAddressID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing VirtualMachineScaleSetName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/", + Valid: false, + }, + + { + // missing value for VirtualMachineScaleSetName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/", + Valid: false, + }, + + { + // missing VirtualMachineName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/", + Valid: false, + }, + + { + // missing value for VirtualMachineName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/", + Valid: false, + }, + + { + // missing NetworkInterfaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/", + Valid: false, + }, + + { + // missing value for NetworkInterfaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/", + Valid: false, + }, + + { + // missing IpConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/", + Valid: false, + }, + + { + // missing value for IpConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/", + Valid: false, + }, + + { + // missing PublicIPAddressName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/", + Valid: false, + }, + + { + // missing value for PublicIPAddressName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleSet1/virtualMachines/virtualMachine1/networkInterfaces/networkInterface1/ipConfigurations/ipConfiguration1/publicIPAddresses/publicIpAddress1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.COMPUTE/VIRTUALMACHINESCALESETS/SCALESET1/VIRTUALMACHINES/VIRTUALMACHINE1/NETWORKINTERFACES/NETWORKINTERFACE1/IPCONFIGURATIONS/IPCONFIGURATION1/PUBLICIPADDRESSES/PUBLICIPADDRESS1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := VirtualMachineScaleSetPublicIPAddressID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/network/validate/web_application_firewall_policy.go b/internal/services/network/validate/web_application_firewall_policy.go index 56dfd253ab35..6606ca2d9623 100644 --- a/internal/services/network/validate/web_application_firewall_policy.go +++ b/internal/services/network/validate/web_application_firewall_policy.go @@ -44,3 +44,11 @@ var ValidateWebApplicationFirewallPolicyRuleSetType = validation.StringInSlice([ "OWASP", "Microsoft_BotManagerRuleSet", }, false) + +var ValidateWebApplicationFirewallPolicyExclusionRuleSetVersion = validation.StringInSlice([]string{ + "3.2", +}, false) + +var ValidateWebApplicationFirewallPolicyExclusionRuleSetType = validation.StringInSlice([]string{ + "OWASP", +}, false) diff --git a/internal/services/network/web_application_firewall_policy_resource.go b/internal/services/network/web_application_firewall_policy_resource.go index 610c741618ae..3ca900514a61 100644 --- a/internal/services/network/web_application_firewall_policy_resource.go +++ b/internal/services/network/web_application_firewall_policy_resource.go @@ -195,6 +195,47 @@ func resourceWebApplicationFirewallPolicy() *pluginsdk.Resource { string(network.OwaspCrsExclusionEntrySelectorMatchOperatorStartsWith), }, false), }, + "excluded_rule_set": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "type": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "OWASP", + ValidateFunc: validate.ValidateWebApplicationFirewallPolicyExclusionRuleSetType, + }, + "version": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "3.2", + ValidateFunc: validate.ValidateWebApplicationFirewallPolicyExclusionRuleSetVersion, + }, + "rule_group": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "rule_group_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ValidateWebApplicationFirewallPolicyRuleGroupName, + }, + "excluded_rules": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -475,6 +516,62 @@ func expandWebApplicationFirewallPolicyManagedRulesDefinition(input []interface{ } } +func expandWebApplicationFirewallPolicyExclusionManagedRules(input []interface{}) *[]network.ExclusionManagedRule { + results := make([]network.ExclusionManagedRule, 0) + for _, item := range input { + ruleID := item.(string) + + result := network.ExclusionManagedRule{ + RuleID: utils.String(ruleID), + } + + results = append(results, result) + } + return &results +} + +func expandWebApplicationFirewallPolicyExclusionManagedRuleGroup(input []interface{}) *[]network.ExclusionManagedRuleGroup { + results := make([]network.ExclusionManagedRuleGroup, 0) + for _, item := range input { + v := item.(map[string]interface{}) + + ruleGroupName := v["rule_group_name"].(string) + + result := network.ExclusionManagedRuleGroup{ + RuleGroupName: utils.String(ruleGroupName), + } + + if excludedRules := v["excluded_rules"].([]interface{}); len(excludedRules) > 0 { + result.Rules = expandWebApplicationFirewallPolicyExclusionManagedRules(excludedRules) + } + + results = append(results, result) + } + return &results +} + +func expandWebApplicationFirewallPolicyExclusionManagedRuleSet(input []interface{}) *[]network.ExclusionManagedRuleSet { + results := make([]network.ExclusionManagedRuleSet, 0) + for _, item := range input { + v := item.(map[string]interface{}) + + ruleSetType := v["type"].(string) + ruleSetVersion := v["version"].(string) + ruleGroups := make([]interface{}, 0) + if value, exists := v["rule_group"]; exists { + ruleGroups = value.([]interface{}) + } + result := network.ExclusionManagedRuleSet{ + RuleSetType: utils.String(ruleSetType), + RuleSetVersion: utils.String(ruleSetVersion), + RuleGroups: expandWebApplicationFirewallPolicyExclusionManagedRuleGroup(ruleGroups), + } + + results = append(results, result) + } + return &results +} + func expandWebApplicationFirewallPolicyExclusions(input []interface{}) *[]network.OwaspCrsExclusionEntry { results := make([]network.OwaspCrsExclusionEntry, 0) for _, item := range input { @@ -483,11 +580,13 @@ func expandWebApplicationFirewallPolicyExclusions(input []interface{}) *[]networ matchVariable := v["match_variable"].(string) selectorMatchOperator := v["selector_match_operator"].(string) selector := v["selector"].(string) + exclusionManagedRuleSets := v["excluded_rule_set"].([]interface{}) result := network.OwaspCrsExclusionEntry{ - MatchVariable: network.OwaspCrsExclusionEntryMatchVariable(matchVariable), - SelectorMatchOperator: network.OwaspCrsExclusionEntrySelectorMatchOperator(selectorMatchOperator), - Selector: utils.String(selector), + MatchVariable: network.OwaspCrsExclusionEntryMatchVariable(matchVariable), + SelectorMatchOperator: network.OwaspCrsExclusionEntrySelectorMatchOperator(selectorMatchOperator), + Selector: utils.String(selector), + ExclusionManagedRuleSets: expandWebApplicationFirewallPolicyExclusionManagedRuleSet(exclusionManagedRuleSets), } results = append(results, result) @@ -653,6 +752,57 @@ func flattenWebApplicationFirewallPolicyManagedRulesDefinition(input *network.Ma return results } +func flattenWebApplicationFirewallPolicyExclusionManagedRules(input *[]network.ExclusionManagedRule) []string { + results := make([]string, 0) + if input == nil || len(*input) == 0 { + return results + } + + for _, item := range *input { + if item.RuleID != nil { + v := *item.RuleID + results = append(results, v) + } + } + + return results +} + +func flattenWebApplicationFirewallPolicyExclusionManagedRuleGroups(input *[]network.ExclusionManagedRuleGroup) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range *input { + v := make(map[string]interface{}) + + v["rule_group_name"] = item.RuleGroupName + v["excluded_rules"] = flattenWebApplicationFirewallPolicyExclusionManagedRules(item.Rules) + + results = append(results, v) + } + return results +} + +func flattenWebApplicationFirewallPolicyExclusionManagedRuleSets(input *[]network.ExclusionManagedRuleSet) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range *input { + v := make(map[string]interface{}) + + v["type"] = item.RuleSetType + v["version"] = item.RuleSetVersion + v["rule_group"] = flattenWebApplicationFirewallPolicyExclusionManagedRuleGroups(item.RuleGroups) + + results = append(results, v) + } + return results +} + func flattenWebApplicationFirewallPolicyExclusions(input *[]network.OwaspCrsExclusionEntry) []interface{} { results := make([]interface{}, 0) if input == nil { @@ -668,7 +818,9 @@ func flattenWebApplicationFirewallPolicyExclusions(input *[]network.OwaspCrsExcl if selector != nil { v["selector"] = *selector } + v["selector_match_operator"] = string(item.SelectorMatchOperator) + v["excluded_rule_set"] = flattenWebApplicationFirewallPolicyExclusionManagedRuleSets(item.ExclusionManagedRuleSets) results = append(results, v) } diff --git a/internal/services/network/web_application_firewall_policy_resource_test.go b/internal/services/network/web_application_firewall_policy_resource_test.go index 18b52b5aa307..b917bbe5f461 100644 --- a/internal/services/network/web_application_firewall_policy_resource_test.go +++ b/internal/services/network/web_application_firewall_policy_resource_test.go @@ -216,6 +216,28 @@ func TestAccWebApplicationFirewallPolicy_knownCVEs(t *testing.T) { }) } +func TestAccWebApplicationFirewallPolicy_excludedRules(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_web_application_firewall_policy", "test") + r := WebApplicationFirewallResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.excludedRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateExcludedRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (t WebApplicationFirewallResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.ApplicationGatewayWebApplicationFirewallPolicyID(state.ID) if err != nil { @@ -512,3 +534,225 @@ resource "azurerm_web_application_firewall_policy" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } + +func (WebApplicationFirewallResource) excludedRules(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_web_application_firewall_policy" "test" { + name = "acctestwafpolicy-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + tags = { + env = "test" + } + + custom_rules { + name = "Rule1" + priority = 1 + rule_type = "MatchRule" + + match_conditions { + match_variables { + variable_name = "RemoteAddr" + } + + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24", "10.0.0.0/24"] + } + + action = "Block" + } + + custom_rules { + name = "Rule2" + priority = 2 + rule_type = "MatchRule" + + match_conditions { + match_variables { + variable_name = "RemoteAddr" + } + + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24"] + } + + match_conditions { + match_variables { + variable_name = "RequestHeaders" + selector = "UserAgent" + } + + operator = "Contains" + negation_condition = false + match_values = ["windows"] + transforms = ["Lowercase"] + } + + action = "Block" + } + + managed_rules { + exclusion { + match_variable = "RequestHeaderNames" + selector = "x-shared-secret" + selector_match_operator = "Equals" + + excluded_rule_set { + rule_group { + rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" + excluded_rules = [ + "920100", + "920120", + ] + } + } + } + + managed_rule_set { + type = "OWASP" + version = "3.2" + + rule_group_override { + rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" + disabled_rules = [ + "920300", + "920440", + ] + } + } + } + + policy_settings { + enabled = true + mode = "Prevention" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (WebApplicationFirewallResource) updateExcludedRules(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_web_application_firewall_policy" "test" { + name = "acctestwafpolicy-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + tags = { + env = "test" + } + + custom_rules { + name = "Rule1" + priority = 1 + rule_type = "MatchRule" + + match_conditions { + match_variables { + variable_name = "RemoteAddr" + } + + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24", "10.0.0.0/24"] + } + + action = "Block" + } + + custom_rules { + name = "Rule2" + priority = 2 + rule_type = "MatchRule" + + match_conditions { + match_variables { + variable_name = "RemoteAddr" + } + + operator = "IPMatch" + negation_condition = false + match_values = ["192.168.1.0/24"] + } + + match_conditions { + match_variables { + variable_name = "RequestHeaders" + selector = "UserAgent" + } + + operator = "Contains" + negation_condition = false + match_values = ["windows"] + transforms = ["Lowercase"] + } + + action = "Block" + } + + managed_rules { + exclusion { + match_variable = "RequestHeaderNames" + selector = "x-shared-secret" + selector_match_operator = "Equals" + + excluded_rule_set { + rule_group { + rule_group_name = "REQUEST-913-SCANNER-DETECTION" + excluded_rules = [ + "913100", + "913101", + ] + } + + rule_group { + rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" + excluded_rules = [ + "920100", + "920120", + ] + } + } + } + + managed_rule_set { + type = "OWASP" + version = "3.2" + + rule_group_override { + rule_group_name = "REQUEST-920-PROTOCOL-ENFORCEMENT" + disabled_rules = [ + "920300", + "920440", + ] + } + } + } + + policy_settings { + enabled = true + mode = "Prevention" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/internal/services/policy/client/client.go b/internal/services/policy/client/client.go index e4656fcb0212..2d57570ae7f3 100644 --- a/internal/services/policy/client/client.go +++ b/internal/services/policy/client/client.go @@ -4,7 +4,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/guestconfiguration/mgmt/2020-06-25/guestconfiguration" "github.com/Azure/azure-sdk-for-go/services/preview/resources/mgmt/2021-06-01-preview/policy" policyPreview "github.com/Azure/azure-sdk-for-go/services/preview/resources/mgmt/2021-06-01-preview/policy" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) @@ -13,7 +13,7 @@ type Client struct { DefinitionsClient *policy.DefinitionsClient ExemptionsClient *policyPreview.ExemptionsClient SetDefinitionsClient *policy.SetDefinitionsClient - PolicyInsightsClient *policyinsights.PolicyInsightsClient + RemediationsClient *remediations.RemediationsClient GuestConfigurationAssignmentsClient *guestconfiguration.AssignmentsClient } @@ -30,8 +30,8 @@ func NewClient(o *common.ClientOptions) *Client { setDefinitionsClient := policy.NewSetDefinitionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&setDefinitionsClient.Client, o.ResourceManagerAuthorizer) - policyInsightsClient := policyinsights.NewPolicyInsightsClientWithBaseURI(o.ResourceManagerEndpoint) - o.ConfigureClient(&policyInsightsClient.Client, o.ResourceManagerAuthorizer) + remediationsClient := remediations.NewRemediationsClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&remediationsClient.Client, o.ResourceManagerAuthorizer) guestConfigurationAssignmentsClient := guestconfiguration.NewAssignmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&guestConfigurationAssignmentsClient.Client, o.ResourceManagerAuthorizer) @@ -41,7 +41,7 @@ func NewClient(o *common.ClientOptions) *Client { DefinitionsClient: &definitionsClient, ExemptionsClient: &exemptionsClient, SetDefinitionsClient: &setDefinitionsClient, - PolicyInsightsClient: &policyInsightsClient, + RemediationsClient: &remediationsClient, GuestConfigurationAssignmentsClient: &guestConfigurationAssignmentsClient, } } diff --git a/internal/services/policy/remediation_management_group.go b/internal/services/policy/remediation_management_group.go index 7221236fbcdc..63a4d34ba0d8 100644 --- a/internal/services/policy/remediation_management_group.go +++ b/internal/services/policy/remediation_management_group.go @@ -8,9 +8,11 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + validate2 "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" managmentGroupParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/managementgroup/parse" managmentGroupValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/managementgroup/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/parse" @@ -19,11 +21,10 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmManagementGroupPolicyRemediation() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Create: resourceArmManagementGroupPolicyRemediationCreateUpdate, Read: resourceArmManagementGroupPolicyRemediationRead, Update: resourceArmManagementGroupPolicyRemediationCreateUpdate, @@ -64,6 +65,24 @@ func resourceArmManagementGroupPolicyRemediation() *pluginsdk.Resource { ValidateFunc: validate.PolicyAssignmentID, }, + "failure_percentage": { + Type: pluginsdk.TypeFloat, + Optional: true, + ValidateFunc: validate2.FloatInRange(0, 1.0), + }, + + "parallel_deployments": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + + "resource_count": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + "location_filters": { Type: pluginsdk.TypeList, Optional: true, @@ -80,22 +99,26 @@ func resourceArmManagementGroupPolicyRemediation() *pluginsdk.Resource { DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validate.PolicyDefinitionID, }, - - "resource_discovery_mode": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), - ValidateFunc: validation.StringInSlice([]string{ - string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), - string(policyinsights.ResourceDiscoveryModeReEvaluateCompliance), - }, false), - }, }, } + + if !features.FourPointOhBeta() { + resource.Schema["resource_discovery_mode"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Default: string(remediations.ResourceDiscoveryModeExistingNonCompliant), + ValidateFunc: validation.StringInSlice([]string{ + string(remediations.ResourceDiscoveryModeExistingNonCompliant), + string(remediations.ResourceDiscoveryModeReEvaluateCompliance), + }, false), + Deprecated: "`resource_discovery_mode` will be removed in version 4.0 of the AzureRM Provider as evaluating compliance before remediation is only supported at subscription scope and below.", + } + } + return resource } func resourceArmManagementGroupPolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -103,7 +126,7 @@ func resourceArmManagementGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resour if err != nil { return err } - id := policyinsights.NewProviders2RemediationID(managementID.Name, d.Get("name").(string)) + id := remediations.NewProviders2RemediationID(managementID.Name, d.Get("name").(string)) if d.IsNewResource() { existing, err := client.RemediationsGetAtManagementGroup(ctx, id) @@ -117,17 +140,9 @@ func resourceArmManagementGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resour } } - parameters := policyinsights.Remediation{ - Properties: &policyinsights.RemediationProperties{ - Filters: &policyinsights.RemediationFilters{ - Locations: utils.ExpandStringSlice(d.Get("location_filters").([]interface{})), - }, - PolicyAssignmentId: utils.String(d.Get("policy_assignment_id").(string)), - PolicyDefinitionReferenceId: utils.String(d.Get("policy_definition_id").(string)), - }, + parameters := remediations.Remediation{ + Properties: readRemediationProperties(d), } - mode := policyinsights.ResourceDiscoveryMode(d.Get("resource_discovery_mode").(string)) - parameters.Properties.ResourceDiscoveryMode = &mode if _, err := client.RemediationsCreateOrUpdateAtManagementGroup(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating %s: %+v", id.ID(), err) @@ -139,11 +154,11 @@ func resourceArmManagementGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resour } func resourceArmManagementGroupPolicyRemediationRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseProviders2RemediationID(d.Id()) + id, err := remediations.ParseProviders2RemediationID(d.Id()) if err != nil { return fmt.Errorf("reading Policy Remediation: %+v", err) } @@ -162,29 +177,15 @@ func resourceArmManagementGroupPolicyRemediationRead(d *pluginsdk.ResourceData, managementGroupID := managmentGroupParse.NewManagementGroupId(id.ManagementGroupId) d.Set("management_group_id", managementGroupID.ID()) - if props := resp.Model.Properties; props != nil { - locations := []interface{}{} - if filters := props.Filters; filters != nil { - locations = utils.FlattenStringSlice(filters.Locations) - } - if err := d.Set("location_filters", locations); err != nil { - return fmt.Errorf("setting `location_filters`: %+v", err) - } - - d.Set("policy_assignment_id", props.PolicyAssignmentId) - d.Set("policy_definition_id", props.PolicyDefinitionReferenceId) - d.Set("resource_discovery_mode", props.ResourceDiscoveryMode) - } - - return nil + return setRemediationProperties(d, resp.Model.Properties) } func resourceArmManagementGroupPolicyRemediationDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseProviders2RemediationID(d.Id()) + id, err := remediations.ParseProviders2RemediationID(d.Id()) if err != nil { return err } @@ -215,7 +216,7 @@ func resourceArmManagementGroupPolicyRemediationDelete(d *pluginsdk.ResourceData } func managementGroupPolicyRemediationCancellationRefreshFunc(ctx context.Context, - client *policyinsights.PolicyInsightsClient, id policyinsights.Providers2RemediationId) pluginsdk.StateRefreshFunc { + client *remediations.RemediationsClient, id remediations.Providers2RemediationId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.RemediationsGetAtManagementGroup(ctx, id) diff --git a/internal/services/policy/remediation_management_group_test.go b/internal/services/policy/remediation_management_group_test.go index 99a447864838..d8299ae65dea 100644 --- a/internal/services/policy/remediation_management_group_test.go +++ b/internal/services/policy/remediation_management_group_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -47,12 +47,12 @@ func TestAccAzureRMManagementGroupPolicyRemediation_complete(t *testing.T) { } func (r ManagementGroupPolicyRemediationResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := policyinsights.ParseProviders2RemediationID(state.ID) + id, err := remediations.ParseProviders2RemediationID(state.ID) if err != nil { return nil, err } - resp, err := client.Policy.PolicyInsightsClient.RemediationsGetAtManagementGroup(ctx, *id) + resp, err := client.Policy.RemediationsClient.RemediationsGetAtManagementGroup(ctx, *id) if err != nil || resp.Model == nil { if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil @@ -78,7 +78,7 @@ data "azurerm_policy_definition" "test" { } resource "azurerm_management_group_policy_assignment" "test" { - name = "acctestpol-%[2]s" + name = "acctestpa-mg-%[2]s" management_group_id = azurerm_management_group.test.id policy_definition_id = data.azurerm_policy_definition.test.id parameters = jsonencode({ diff --git a/internal/services/policy/remediation_resource.go b/internal/services/policy/remediation_resource.go index efe4bb667e29..4aa114ea95ef 100644 --- a/internal/services/policy/remediation_resource.go +++ b/internal/services/policy/remediation_resource.go @@ -8,9 +8,10 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + validate2 "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/validate" @@ -63,6 +64,24 @@ func resourceArmResourcePolicyRemediation() *pluginsdk.Resource { ValidateFunc: validate.PolicyAssignmentID, }, + "failure_percentage": { + Type: pluginsdk.TypeFloat, + Optional: true, + ValidateFunc: validate2.FloatInRange(0, 1.0), + }, + + "parallel_deployments": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + + "resource_count": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + "location_filters": { Type: pluginsdk.TypeList, Optional: true, @@ -83,10 +102,10 @@ func resourceArmResourcePolicyRemediation() *pluginsdk.Resource { "resource_discovery_mode": { Type: pluginsdk.TypeString, Optional: true, - Default: string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), + Default: string(remediations.ResourceDiscoveryModeExistingNonCompliant), ValidateFunc: validation.StringInSlice([]string{ - string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), - string(policyinsights.ResourceDiscoveryModeReEvaluateCompliance), + string(remediations.ResourceDiscoveryModeExistingNonCompliant), + string(remediations.ResourceDiscoveryModeReEvaluateCompliance), }, false), }, }, @@ -94,13 +113,13 @@ func resourceArmResourcePolicyRemediation() *pluginsdk.Resource { } func resourceArmResourcePolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() resourceId := d.Get("resource_id").(string) - id := policyinsights.NewScopedRemediationID(resourceId, d.Get("name").(string)) + id := remediations.NewScopedRemediationID(resourceId, d.Get("name").(string)) if d.IsNewResource() { existing, err := client.RemediationsGetAtResource(ctx, id) @@ -114,17 +133,9 @@ func resourceArmResourcePolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, } } - parameters := policyinsights.Remediation{ - Properties: &policyinsights.RemediationProperties{ - Filters: &policyinsights.RemediationFilters{ - Locations: utils.ExpandStringSlice(d.Get("location_filters").([]interface{})), - }, - PolicyAssignmentId: utils.String(d.Get("policy_assignment_id").(string)), - PolicyDefinitionReferenceId: utils.String(d.Get("policy_definition_id").(string)), - }, + parameters := remediations.Remediation{ + Properties: readRemediationProperties(d), } - mode := policyinsights.ResourceDiscoveryMode(d.Get("resource_discovery_mode").(string)) - parameters.Properties.ResourceDiscoveryMode = &mode if _, err := client.RemediationsCreateOrUpdateAtResource(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating %s: %+v", id.ID(), err) @@ -136,11 +147,11 @@ func resourceArmResourcePolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, } func resourceArmResourcePolicyRemediationRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseScopedRemediationID(d.Id()) + id, err := remediations.ParseScopedRemediationID(d.Id()) if err != nil { return fmt.Errorf("parsing Policy Scoped Remediation ID: %+v", err) } @@ -157,30 +168,15 @@ func resourceArmResourcePolicyRemediationRead(d *pluginsdk.ResourceData, meta in d.Set("name", id.RemediationName) d.Set("resource_id", id.ResourceId) - if props := resp.Model.Properties; props != nil { - locations := []interface{}{} - if filters := props.Filters; filters != nil { - locations = utils.FlattenStringSlice(filters.Locations) - } - if err := d.Set("location_filters", locations); err != nil { - return fmt.Errorf("setting `location_filters`: %+v", err) - } - - d.Set("policy_assignment_id", props.PolicyAssignmentId) - d.Set("policy_definition_id", props.PolicyDefinitionReferenceId) - d.Set("resource_discovery_mode", utils.NormalizeNilableString((*string)(props.ResourceDiscoveryMode))) - - } - - return nil + return setRemediationProperties(d, resp.Model.Properties) } func resourceArmResourcePolicyRemediationDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseScopedRemediationID(d.Id()) + id, err := remediations.ParseScopedRemediationID(d.Id()) if err != nil { return fmt.Errorf("parsing Policy Scoped Remediation ID: %+v", err) } @@ -213,7 +209,7 @@ func resourceArmResourcePolicyRemediationDelete(d *pluginsdk.ResourceData, meta return err } -func resourcePolicyRemediationCancellationRefreshFunc(ctx context.Context, client *policyinsights.PolicyInsightsClient, id policyinsights.ScopedRemediationId) pluginsdk.StateRefreshFunc { +func resourcePolicyRemediationCancellationRefreshFunc(ctx context.Context, client *remediations.RemediationsClient, id remediations.ScopedRemediationId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.RemediationsGetAtResource(ctx, id) if err != nil { @@ -232,7 +228,7 @@ func resourcePolicyRemediationCancellationRefreshFunc(ctx context.Context, clien // waitRemediationToDelete waits for the remediation to a status that allow to delete func waitRemediationToDelete(ctx context.Context, - prop *policyinsights.RemediationProperties, + prop *remediations.RemediationProperties, id string, timeout time.Duration, cancelFunc func() error, @@ -241,7 +237,7 @@ func waitRemediationToDelete(ctx context.Context, if prop == nil { return nil } - if mode := prop.ResourceDiscoveryMode; mode != nil && *mode == policyinsights.ResourceDiscoveryModeReEvaluateCompliance { + if mode := prop.ResourceDiscoveryMode; mode != nil && *mode == remediations.ResourceDiscoveryModeReEvaluateCompliance { // Remediation can only be canceld when it is in "Evaluating" or "Accepted" status, otherwise, API might raise error (e.g. canceling a "Completed" remediation returns 400). if state := prop.ProvisioningState; state != nil && (*state == "Evaluating" || *state == "Accepted") { log.Printf("[DEBUG] cancelling the remediation first before deleting it when `resource_discovery_mode` is set to `ReEvaluateCompliance`") @@ -267,3 +263,53 @@ func waitRemediationToDelete(ctx context.Context, } return nil } + +// readRemediationProperties sets the properties of the remediation, useful when add new properties to the model +func readRemediationProperties(d *pluginsdk.ResourceData) (prop *remediations.RemediationProperties) { + prop = &remediations.RemediationProperties{ + Filters: &remediations.RemediationFilters{ + Locations: utils.ExpandStringSlice(d.Get("location_filters").([]interface{})), + }, + PolicyAssignmentId: utils.String(d.Get("policy_assignment_id").(string)), + PolicyDefinitionReferenceId: utils.String(d.Get("policy_definition_id").(string)), + } + mode := remediations.ResourceDiscoveryMode(d.Get("resource_discovery_mode").(string)) + prop.ResourceDiscoveryMode = &mode + if v := d.Get("failure_percentage").(float64); v != 0 { + prop.FailureThreshold = &remediations.RemediationPropertiesFailureThreshold{ + Percentage: utils.Float(v), + } + } + if v := d.Get("parallel_deployments").(int); v != 0 { + prop.ParallelDeployments = utils.Int64(int64(v)) + } + if v := d.Get("resource_count").(int); v != 0 { + prop.ResourceCount = utils.Int64(int64(v)) + } + return +} + +// setRemediationProperties sets the properties of the remediation, useful when add new properties to the model +func setRemediationProperties(d *pluginsdk.ResourceData, prop *remediations.RemediationProperties) error { + if prop == nil { + return nil + } + locations := []interface{}{} + if filters := prop.Filters; filters != nil { + locations = utils.FlattenStringSlice(filters.Locations) + } + if err := d.Set("location_filters", locations); err != nil { + return fmt.Errorf("setting `location_filters`: %+v", err) + } + + d.Set("policy_assignment_id", prop.PolicyAssignmentId) + d.Set("policy_definition_id", prop.PolicyDefinitionReferenceId) + d.Set("resource_discovery_mode", utils.NormalizeNilableString((*string)(prop.ResourceDiscoveryMode))) + + d.Set("resource_count", prop.ResourceCount) + d.Set("parallel_deployments", prop.ParallelDeployments) + if prop.FailureThreshold != nil { + d.Set("failure_percentage", prop.FailureThreshold.Percentage) + } + return nil +} diff --git a/internal/services/policy/remediation_resource_group.go b/internal/services/policy/remediation_resource_group.go index 799934922629..3b441d6b6638 100644 --- a/internal/services/policy/remediation_resource_group.go +++ b/internal/services/policy/remediation_resource_group.go @@ -8,8 +8,9 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + validate2 "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/validate" @@ -19,7 +20,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmResourceGroupPolicyRemediation() *pluginsdk.Resource { @@ -64,6 +64,24 @@ func resourceArmResourceGroupPolicyRemediation() *pluginsdk.Resource { ValidateFunc: validate.PolicyAssignmentID, }, + "failure_percentage": { + Type: pluginsdk.TypeFloat, + Optional: true, + ValidateFunc: validate2.FloatInRange(0, 1.0), + }, + + "parallel_deployments": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + + "resource_count": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + "location_filters": { Type: pluginsdk.TypeList, Optional: true, @@ -84,10 +102,10 @@ func resourceArmResourceGroupPolicyRemediation() *pluginsdk.Resource { "resource_discovery_mode": { Type: pluginsdk.TypeString, Optional: true, - Default: string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), + Default: string(remediations.ResourceDiscoveryModeExistingNonCompliant), ValidateFunc: validation.StringInSlice([]string{ - string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), - string(policyinsights.ResourceDiscoveryModeReEvaluateCompliance), + string(remediations.ResourceDiscoveryModeExistingNonCompliant), + string(remediations.ResourceDiscoveryModeReEvaluateCompliance), }, false), }, }, @@ -95,7 +113,7 @@ func resourceArmResourceGroupPolicyRemediation() *pluginsdk.Resource { } func resourceArmResourceGroupPolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -104,7 +122,7 @@ func resourceArmResourceGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resource return err } - id := policyinsights.NewProviderRemediationID(resourceGroupId.SubscriptionId, resourceGroupId.ResourceGroup, d.Get("name").(string)) + id := remediations.NewProviderRemediationID(resourceGroupId.SubscriptionId, resourceGroupId.ResourceGroup, d.Get("name").(string)) if d.IsNewResource() { existing, err := client.RemediationsGetAtResourceGroup(ctx, id) @@ -118,17 +136,9 @@ func resourceArmResourceGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resource } } - parameters := policyinsights.Remediation{ - Properties: &policyinsights.RemediationProperties{ - Filters: &policyinsights.RemediationFilters{ - Locations: utils.ExpandStringSlice(d.Get("location_filters").([]interface{})), - }, - PolicyAssignmentId: utils.String(d.Get("policy_assignment_id").(string)), - PolicyDefinitionReferenceId: utils.String(d.Get("policy_definition_id").(string)), - }, + parameters := remediations.Remediation{ + Properties: readRemediationProperties(d), } - mode := policyinsights.ResourceDiscoveryMode(d.Get("resource_discovery_mode").(string)) - parameters.Properties.ResourceDiscoveryMode = &mode if _, err = client.RemediationsCreateOrUpdateAtResourceGroup(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating %s: %+v", id.ID(), err) @@ -140,11 +150,11 @@ func resourceArmResourceGroupPolicyRemediationCreateUpdate(d *pluginsdk.Resource } func resourceArmResourceGroupPolicyRemediationRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseProviderRemediationID(d.Id()) + id, err := remediations.ParseProviderRemediationID(d.Id()) if err != nil { return fmt.Errorf("reading Policy Remediation: %+v", err) } @@ -164,30 +174,15 @@ func resourceArmResourceGroupPolicyRemediationRead(d *pluginsdk.ResourceData, me d.Set("name", id.RemediationName) d.Set("resource_group_id", resourceGroupId.ID()) - if props := resp.Model.Properties; props != nil { - locations := []interface{}{} - if filters := props.Filters; filters != nil { - locations = utils.FlattenStringSlice(filters.Locations) - } - if err := d.Set("location_filters", locations); err != nil { - return fmt.Errorf("setting `location_filters`: %+v", err) - } - - d.Set("policy_assignment_id", props.PolicyAssignmentId) - d.Set("policy_definition_id", props.PolicyDefinitionReferenceId) - d.Set("resource_discovery_mode", utils.NormalizeNilableString((*string)(props.ResourceDiscoveryMode))) - - } - - return nil + return setRemediationProperties(d, resp.Model.Properties) } func resourceArmResourceGroupPolicyRemediationDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseProviderRemediationID(d.Id()) + id, err := remediations.ParseProviderRemediationID(d.Id()) if err != nil { return err } @@ -217,7 +212,7 @@ func resourceArmResourceGroupPolicyRemediationDelete(d *pluginsdk.ResourceData, return err } -func resourceGroupPolicyRemediationCancellationRefreshFunc(ctx context.Context, client *policyinsights.PolicyInsightsClient, id policyinsights.ProviderRemediationId) pluginsdk.StateRefreshFunc { +func resourceGroupPolicyRemediationCancellationRefreshFunc(ctx context.Context, client *remediations.RemediationsClient, id remediations.ProviderRemediationId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.RemediationsGetAtResourceGroup(ctx, id) if err != nil { diff --git a/internal/services/policy/remediation_resource_group_test.go b/internal/services/policy/remediation_resource_group_test.go index 55c655a46424..6d14a23cbd3f 100644 --- a/internal/services/policy/remediation_resource_group_test.go +++ b/internal/services/policy/remediation_resource_group_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -47,12 +47,12 @@ func TestAccAzureRMResourceGroupPolicyRemediation_complete(t *testing.T) { } func (r ResourceGroupPolicyRemediationResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := policyinsights.ParseProviderRemediationID(state.ID) + id, err := remediations.ParseProviderRemediationID(state.ID) if err != nil { return nil, err } - resp, err := client.Policy.PolicyInsightsClient.RemediationsGetAtResourceGroup(ctx, *id) + resp, err := client.Policy.RemediationsClient.RemediationsGetAtResourceGroup(ctx, *id) if err != nil || resp.Model == nil { if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil @@ -109,7 +109,7 @@ PARAMETERS } resource "azurerm_resource_group_policy_assignment" "test" { - name = "acctestpa-%[1]s" + name = "acctestpa-rg-%[1]s" resource_group_id = azurerm_resource_group.test.id policy_definition_id = azurerm_policy_definition.test.id @@ -149,6 +149,9 @@ resource "azurerm_resource_group_policy_remediation" "test" { location_filters = ["westus"] policy_definition_id = azurerm_policy_definition.test.id resource_discovery_mode = "ReEvaluateCompliance" + failure_percentage = 0.5 + parallel_deployments = 3 + resource_count = 3 } `, r.template(data), data.RandomString) } diff --git a/internal/services/policy/remediation_resource_test.go b/internal/services/policy/remediation_resource_test.go index 14c1498a3d4e..387cedc960b0 100644 --- a/internal/services/policy/remediation_resource_test.go +++ b/internal/services/policy/remediation_resource_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + policyinsights "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -52,7 +52,7 @@ func (r ResourcePolicyRemediationResource) Exists(ctx context.Context, client *c return nil, err } - resp, err := client.Policy.PolicyInsightsClient.RemediationsGetAtResource(ctx, *id) + resp, err := client.Policy.RemediationsClient.RemediationsGetAtResource(ctx, *id) if err != nil || resp.Model == nil { if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil @@ -86,7 +86,7 @@ data "azurerm_policy_definition" "test" { } resource "azurerm_resource_policy_assignment" "test" { - name = "acctestpa-%[1]s" + name = "acctestpa-res-%[1]s" resource_id = azurerm_virtual_network.test.id policy_definition_id = data.azurerm_policy_definition.test.id parameters = jsonencode({ @@ -121,6 +121,9 @@ resource "azurerm_resource_policy_remediation" "test" { location_filters = ["westus"] policy_definition_id = data.azurerm_policy_definition.test.id resource_discovery_mode = "ReEvaluateCompliance" + failure_percentage = 0.5 + parallel_deployments = 3 + resource_count = 3 } `, r.template(data), data.RandomString) } diff --git a/internal/services/policy/remediation_subscription.go b/internal/services/policy/remediation_subscription.go index 9ac266d7f89a..1cafdcc78236 100644 --- a/internal/services/policy/remediation_subscription.go +++ b/internal/services/policy/remediation_subscription.go @@ -9,8 +9,9 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + validate2 "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/policy/validate" @@ -18,7 +19,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourceArmSubscriptionPolicyRemediation() *pluginsdk.Resource { @@ -63,6 +63,24 @@ func resourceArmSubscriptionPolicyRemediation() *pluginsdk.Resource { ValidateFunc: validate.PolicyAssignmentID, }, + "failure_percentage": { + Type: pluginsdk.TypeFloat, + Optional: true, + ValidateFunc: validate2.FloatInRange(0, 1.0), + }, + + "parallel_deployments": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + + "resource_count": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validate2.IntegerPositive, + }, + "location_filters": { Type: pluginsdk.TypeList, Optional: true, @@ -83,10 +101,10 @@ func resourceArmSubscriptionPolicyRemediation() *pluginsdk.Resource { "resource_discovery_mode": { Type: pluginsdk.TypeString, Optional: true, - Default: string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), + Default: string(remediations.ResourceDiscoveryModeExistingNonCompliant), ValidateFunc: validation.StringInSlice([]string{ - string(policyinsights.ResourceDiscoveryModeExistingNonCompliant), - string(policyinsights.ResourceDiscoveryModeReEvaluateCompliance), + string(remediations.ResourceDiscoveryModeExistingNonCompliant), + string(remediations.ResourceDiscoveryModeReEvaluateCompliance), }, false), }, }, @@ -94,7 +112,7 @@ func resourceArmSubscriptionPolicyRemediation() *pluginsdk.Resource { } func resourceArmSubscriptionPolicyRemediationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -103,7 +121,7 @@ func resourceArmSubscriptionPolicyRemediationCreateUpdate(d *pluginsdk.ResourceD return err } - id := policyinsights.NewRemediationID(subscriptionId.SubscriptionId, d.Get("name").(string)) + id := remediations.NewRemediationID(subscriptionId.SubscriptionId, d.Get("name").(string)) if d.IsNewResource() { existing, err := client.RemediationsGetAtSubscription(ctx, id) @@ -117,17 +135,9 @@ func resourceArmSubscriptionPolicyRemediationCreateUpdate(d *pluginsdk.ResourceD } } - parameters := policyinsights.Remediation{ - Properties: &policyinsights.RemediationProperties{ - Filters: &policyinsights.RemediationFilters{ - Locations: utils.ExpandStringSlice(d.Get("location_filters").([]interface{})), - }, - PolicyAssignmentId: utils.String(d.Get("policy_assignment_id").(string)), - PolicyDefinitionReferenceId: utils.String(d.Get("policy_definition_id").(string)), - }, + parameters := remediations.Remediation{ + Properties: readRemediationProperties(d), } - mode := policyinsights.ResourceDiscoveryMode(d.Get("resource_discovery_mode").(string)) - parameters.Properties.ResourceDiscoveryMode = &mode if _, err = client.RemediationsCreateOrUpdateAtSubscription(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating %s: %+v", id.ID(), err) @@ -139,11 +149,11 @@ func resourceArmSubscriptionPolicyRemediationCreateUpdate(d *pluginsdk.ResourceD } func resourceArmSubscriptionPolicyRemediationRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseRemediationID(d.Id()) + id, err := remediations.ParseRemediationID(d.Id()) if err != nil { return fmt.Errorf("reading Policy Remediation: %+v", err) } @@ -163,29 +173,15 @@ func resourceArmSubscriptionPolicyRemediationRead(d *pluginsdk.ResourceData, met d.Set("name", id.RemediationName) d.Set("subscription_id", subscriptionId.ID()) - if props := resp.Model.Properties; props != nil { - locations := []interface{}{} - if filters := props.Filters; filters != nil { - locations = utils.FlattenStringSlice(filters.Locations) - } - if err := d.Set("location_filters", locations); err != nil { - return fmt.Errorf("setting `location_filters`: %+v", err) - } - - d.Set("policy_assignment_id", props.PolicyAssignmentId) - d.Set("policy_definition_id", props.PolicyDefinitionReferenceId) - d.Set("resource_discovery_mode", utils.NormalizeNilableString((*string)(props.ResourceDiscoveryMode))) - } - - return nil + return setRemediationProperties(d, resp.Model.Properties) } func resourceArmSubscriptionPolicyRemediationDelete(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.PolicyInsightsClient + client := meta.(*clients.Client).Policy.RemediationsClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := policyinsights.ParseRemediationID(d.Id()) + id, err := remediations.ParseRemediationID(d.Id()) if err != nil { return err } @@ -215,7 +211,7 @@ func resourceArmSubscriptionPolicyRemediationDelete(d *pluginsdk.ResourceData, m return err } -func subscriptionPolicyRemediationCancellationRefreshFunc(ctx context.Context, client *policyinsights.PolicyInsightsClient, id policyinsights.RemediationId) pluginsdk.StateRefreshFunc { +func subscriptionPolicyRemediationCancellationRefreshFunc(ctx context.Context, client *remediations.RemediationsClient, id remediations.RemediationId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := client.RemediationsGetAtSubscription(ctx, id) if err != nil { diff --git a/internal/services/policy/remediation_subscription_test.go b/internal/services/policy/remediation_subscription_test.go index 0337874b5893..45792aa0f2e1 100644 --- a/internal/services/policy/remediation_subscription_test.go +++ b/internal/services/policy/remediation_subscription_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/response" - "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/policyinsights" + "github.com/hashicorp/go-azure-sdk/resource-manager/policyinsights/2021-10-01/remediations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" @@ -47,12 +47,12 @@ func TestAccAzureRMSubscriptionPolicyRemediation_complete(t *testing.T) { } func (r SubscriptionPolicyRemediationResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := policyinsights.ParseRemediationID(state.ID) + id, err := remediations.ParseRemediationID(state.ID) if err != nil { return nil, err } - resp, err := client.Policy.PolicyInsightsClient.RemediationsGetAtSubscription(ctx, *id) + resp, err := client.Policy.RemediationsClient.RemediationsGetAtSubscription(ctx, *id) if err != nil || resp.Model == nil { if response.WasNotFound(resp.HttpResponse) { return utils.Bool(false), nil @@ -76,16 +76,16 @@ data "azurerm_policy_definition" "test" { } resource "azurerm_subscription_policy_assignment" "test" { - name = "acctestpa-%[1]d" + name = "acctestpa-sub-%[1]d" subscription_id = data.azurerm_subscription.test.id policy_definition_id = data.azurerm_policy_definition.test.id parameters = jsonencode({ "listOfAllowedLocations" = { - "value" = ["%[2]s", "%[3]s"] + "value" = ["%[2]s", "%[3]s", "%[4]s"] } }) } -`, data.RandomInteger, data.Locations.Primary, data.Locations.Secondary) +`, data.RandomInteger, data.Locations.Primary, data.Locations.Secondary, data.Locations.Ternary) } func (r SubscriptionPolicyRemediationResource) basic(data acceptance.TestData) string { @@ -111,6 +111,9 @@ resource "azurerm_subscription_policy_remediation" "test" { location_filters = ["westus"] policy_definition_id = data.azurerm_policy_definition.test.id resource_discovery_mode = "ReEvaluateCompliance" + failure_percentage = 0.5 + parallel_deployments = 3 + resource_count = 3 } `, r.template(data), data.RandomString) } diff --git a/internal/services/postgres/client/client.go b/internal/services/postgres/client/client.go index eb9af7723fd6..c90a0be97a1b 100644 --- a/internal/services/postgres/client/client.go +++ b/internal/services/postgres/client/client.go @@ -1,65 +1,81 @@ package client import ( - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/configurations" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/databases" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/firewallrules" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/replicas" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serveradministrators" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serversecurityalertpolicies" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/virtualnetworkrules" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2020-01-01/serverkeys" + flexibleserverconfigurations "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/configurations" + flexibleserverdatabases "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/databases" + flexibleserverfirewallrules "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/firewallrules" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/serverrestart" + flexibleservers "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/common" ) type Client struct { - ConfigurationsClient *postgresql.ConfigurationsClient - DatabasesClient *postgresql.DatabasesClient - FirewallRulesClient *postgresql.FirewallRulesClient - FlexibleServersClient *postgresqlflexibleservers.ServersClient - FlexibleServersConfigurationsClient *postgresqlflexibleservers.ConfigurationsClient - FlexibleServerFirewallRuleClient *postgresqlflexibleservers.FirewallRulesClient - FlexibleServerDatabaseClient *postgresqlflexibleservers.DatabasesClient - ServersClient *postgresql.ServersClient - ServerKeysClient *postgresql.ServerKeysClient - ServerSecurityAlertPoliciesClient *postgresql.ServerSecurityAlertPoliciesClient - VirtualNetworkRulesClient *postgresql.VirtualNetworkRulesClient - ServerAdministratorsClient *postgresql.ServerAdministratorsClient - ReplicasClient *postgresql.ReplicasClient + ConfigurationsClient *configurations.ConfigurationsClient + DatabasesClient *databases.DatabasesClient + FirewallRulesClient *firewallrules.FirewallRulesClient + FlexibleServersClient *flexibleservers.ServersClient + FlexibleServersConfigurationsClient *flexibleserverconfigurations.ConfigurationsClient + FlexibleServerFirewallRuleClient *flexibleserverfirewallrules.FirewallRulesClient + FlexibleServerDatabaseClient *flexibleserverdatabases.DatabasesClient + ServersClient *servers.ServersClient + ServerRestartClient *serverrestart.ServerRestartClient + ServerKeysClient *serverkeys.ServerKeysClient + ServerSecurityAlertPoliciesClient *serversecurityalertpolicies.ServerSecurityAlertPoliciesClient + VirtualNetworkRulesClient *virtualnetworkrules.VirtualNetworkRulesClient + ServerAdministratorsClient *serveradministrators.ServerAdministratorsClient + ReplicasClient *replicas.ReplicasClient } func NewClient(o *common.ClientOptions) *Client { - configurationsClient := postgresql.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + configurationsClient := configurations.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&configurationsClient.Client, o.ResourceManagerAuthorizer) - databasesClient := postgresql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + databasesClient := databases.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&databasesClient.Client, o.ResourceManagerAuthorizer) - firewallRulesClient := postgresql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + firewallRulesClient := firewallrules.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&firewallRulesClient.Client, o.ResourceManagerAuthorizer) - serversClient := postgresql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + serversClient := servers.NewServersClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&serversClient.Client, o.ResourceManagerAuthorizer) - serverKeysClient := postgresql.NewServerKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + serverKeysClient := serverkeys.NewServerKeysClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&serverKeysClient.Client, o.ResourceManagerAuthorizer) - serverSecurityAlertPoliciesClient := postgresql.NewServerSecurityAlertPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + serverSecurityAlertPoliciesClient := serversecurityalertpolicies.NewServerSecurityAlertPoliciesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&serverSecurityAlertPoliciesClient.Client, o.ResourceManagerAuthorizer) - virtualNetworkRulesClient := postgresql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + virtualNetworkRulesClient := virtualnetworkrules.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&virtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) - serverAdministratorsClient := postgresql.NewServerAdministratorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + serverAdministratorsClient := serveradministrators.NewServerAdministratorsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&serverAdministratorsClient.Client, o.ResourceManagerAuthorizer) - replicasClient := postgresql.NewReplicasClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + replicasClient := replicas.NewReplicasClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&replicasClient.Client, o.ResourceManagerAuthorizer) - flexibleServersClient := postgresqlflexibleservers.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + flexibleServersClient := flexibleservers.NewServersClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&flexibleServersClient.Client, o.ResourceManagerAuthorizer) - flexibleServerFirewallRuleClient := postgresqlflexibleservers.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + restartServerClient := serverrestart.NewServerRestartClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&restartServerClient.Client, o.ResourceManagerAuthorizer) + + flexibleServerFirewallRuleClient := flexibleserverfirewallrules.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&flexibleServerFirewallRuleClient.Client, o.ResourceManagerAuthorizer) - flexibleServerDatabaseClient := postgresqlflexibleservers.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + flexibleServerDatabaseClient := flexibleserverdatabases.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&flexibleServerDatabaseClient.Client, o.ResourceManagerAuthorizer) - flexibleServerConfigurationsClient := postgresqlflexibleservers.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + flexibleServerConfigurationsClient := flexibleserverconfigurations.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&flexibleServerConfigurationsClient.Client, o.ResourceManagerAuthorizer) return &Client{ @@ -68,6 +84,7 @@ func NewClient(o *common.ClientOptions) *Client { FirewallRulesClient: &firewallRulesClient, FlexibleServersConfigurationsClient: &flexibleServerConfigurationsClient, FlexibleServersClient: &flexibleServersClient, + ServerRestartClient: &restartServerClient, FlexibleServerFirewallRuleClient: &flexibleServerFirewallRuleClient, FlexibleServerDatabaseClient: &flexibleServerDatabaseClient, ServersClient: &serversClient, diff --git a/internal/services/postgres/migration/postgresql_aad_administrator.go b/internal/services/postgres/migration/postgresql_aad_administrator.go new file mode 100644 index 000000000000..1f86dfff4889 --- /dev/null +++ b/internal/services/postgres/migration/postgresql_aad_administrator.go @@ -0,0 +1,69 @@ +package migration + +import ( + "context" + "log" + + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serveradministrators" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/sql/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +var _ pluginsdk.StateUpgrade = PostgresqlAADAdministratorV0ToV1{} + +type PostgresqlAADAdministratorV0ToV1 struct{} + +func (PostgresqlAADAdministratorV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "server_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "login": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.AdminUsernames, + }, + + "object_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, + }, + + "tenant_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, + }, + } +} + +func (PostgresqlAADAdministratorV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + // old + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/servers/{serverName}/administrators/activeDirectory + // new: + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/servers/{serverName} + // summary: + // Remove administrators chunk + oldId := rawState["id"].(string) + id, err := parse.AzureActiveDirectoryAdministratorID(oldId) + if err != nil { + return rawState, err + } + + newId := serveradministrators.NewServerID(id.SubscriptionId, id.ResourceGroup, id.ServerName) + log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId) + rawState["id"] = newId.ID() + + return rawState, nil + } +} diff --git a/internal/services/postgres/migration/postgresql_database.go b/internal/services/postgres/migration/postgresql_database.go new file mode 100644 index 000000000000..22a69c38a448 --- /dev/null +++ b/internal/services/postgres/migration/postgresql_database.go @@ -0,0 +1,74 @@ +package migration + +import ( + "context" + "log" + "strings" + + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/databases" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" +) + +var _ pluginsdk.StateUpgrade = PostgresqlDatabaseV0ToV1{} + +type PostgresqlDatabaseV0ToV1 struct{} + +func (PostgresqlDatabaseV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "server_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ServerName, + }, + + "charset": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + ForceNew: true, + }, + + "collation": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DatabaseCollation, + }, + } +} + +func (PostgresqlDatabaseV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + // old + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBForPostgreSQL/servers/{serverName}/databases/{databaseName} + // new: + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/servers/{serverName}/databases/{databaseName} + // summary: + // Check for `For` and swap to `for` + oldId := rawState["id"].(string) + if strings.Contains(oldId, "Microsoft.DBForPostgreSQL") { + modifiedId := strings.ReplaceAll(oldId, "Microsoft.DBForPostgreSQL", "Microsoft.DBforPostgreSQL") + + newId, err := databases.ParseDatabaseID(modifiedId) + if err != nil { + return rawState, err + } + log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId) + rawState["id"] = newId.ID() + } + + return rawState, nil + } +} diff --git a/internal/services/postgres/migration/postgresql_server.go b/internal/services/postgres/migration/postgresql_server.go new file mode 100644 index 000000000000..b7800d6d065b --- /dev/null +++ b/internal/services/postgres/migration/postgresql_server.go @@ -0,0 +1,264 @@ +package migration + +import ( + "context" + "log" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +var _ pluginsdk.StateUpgrade = PostgresqlServerV0ToV1{} + +type PostgresqlServerV0ToV1 struct{} + +func (PostgresqlServerV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ServerName, + }, + + "location": azure.SchemaLocation(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "sku_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "version": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForServerVersion(), false), + }, + + "administrator_login": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.All(validation.StringIsNotWhiteSpace, validate.AdminUsernames), + }, + + "administrator_login_password": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + }, + + "auto_grow_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "backup_retention_days": { + Type: pluginsdk.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(7, 35), + }, + + "geo_redundant_backup_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "create_mode": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(servers.CreateModeDefault), + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForCreateMode(), false), + }, + + "creation_source_server_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: servers.ValidateServerID, + }, + + "identity": commonschema.SystemAssignedIdentityOptional(), + + "infrastructure_encryption_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "restore_point_in_time": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, + + "storage_mb": { + Type: pluginsdk.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.IntBetween(5120, 16777216), + validation.IntDivisibleBy(1024), + ), + }, + + "ssl_minimal_tls_version_enforced": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(servers.MinimalTlsVersionEnumTLSOneTwo), + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForMinimalTlsVersionEnum(), false), + }, + + "ssl_enforcement_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + + "threat_detection_policy": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "disabled_alerts": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: pluginsdk.HashString, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "Sql_Injection", + "Sql_Injection_Vulnerability", + "Access_Anomaly", + "Data_Exfiltration", + "Unsafe_Action", + }, false), + }, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "email_account_admins": { + Type: pluginsdk.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "email_addresses": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + // todo email validation in code + }, + Set: pluginsdk.HashString, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "retention_days": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "storage_account_access_key": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + + "storage_endpoint": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + AtLeastOneOf: []string{ + "threat_detection_policy.0.enabled", "threat_detection_policy.0.disabled_alerts", "threat_detection_policy.0.email_account_admins", + "threat_detection_policy.0.email_addresses", "threat_detection_policy.0.retention_days", "threat_detection_policy.0.storage_account_access_key", + "threat_detection_policy.0.storage_endpoint", + }, + }, + }, + }, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.Tags(), + } +} + +func (PostgresqlServerV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + // old + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBForPostgreSQL/servers/{serverName} + // new: + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/servers/{serverName} + // summary: + // Check for `For` and swap to `for` + oldId := rawState["id"].(string) + if strings.Contains(oldId, "Microsoft.DBForPostgreSQL") { + modifiedId := strings.ReplaceAll(oldId, "Microsoft.DBForPostgreSQL", "Microsoft.DBforPostgreSQL") + + newId, err := servers.ParseServerID(modifiedId) + if err != nil { + return rawState, err + } + log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId) + rawState["id"] = newId.ID() + } + + return rawState, nil + } +} diff --git a/internal/services/postgres/parse/configuration.go b/internal/services/postgres/parse/configuration.go deleted file mode 100644 index 69dee5a0be6d..000000000000 --- a/internal/services/postgres/parse/configuration.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ConfigurationId struct { - SubscriptionId string - ResourceGroup string - ServerName string - Name string -} - -func NewConfigurationID(subscriptionId, resourceGroup, serverName, name string) ConfigurationId { - return ConfigurationId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - Name: name, - } -} - -func (id ConfigurationId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Configuration", segmentsStr) -} - -func (id ConfigurationId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s/configurations/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.Name) -} - -// ConfigurationID parses a Configuration ID into an ConfigurationId struct -func ConfigurationID(input string) (*ConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ConfigurationId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("configurations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/configuration_test.go b/internal/services/postgres/parse/configuration_test.go deleted file mode 100644 index cc5d86470471..000000000000 --- a/internal/services/postgres/parse/configuration_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ConfigurationId{} - -func TestConfigurationIDFormatter(t *testing.T) { - actual := NewConfigurationID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "configuration1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/configuration1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestConfigurationID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/configuration1", - Expected: &ConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - Name: "configuration1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/CONFIGURATIONS/CONFIGURATION1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ConfigurationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/parse/database.go b/internal/services/postgres/parse/database.go deleted file mode 100644 index 33e2dbf57aa3..000000000000 --- a/internal/services/postgres/parse/database.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type DatabaseId struct { - SubscriptionId string - ResourceGroup string - ServerName string - Name string -} - -func NewDatabaseID(subscriptionId, resourceGroup, serverName, name string) DatabaseId { - return DatabaseId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - Name: name, - } -} - -func (id DatabaseId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Database", segmentsStr) -} - -func (id DatabaseId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s/databases/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.Name) -} - -// DatabaseID parses a Database ID into an DatabaseId struct -func DatabaseID(input string) (*DatabaseId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := DatabaseId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("databases"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/database_test.go b/internal/services/postgres/parse/database_test.go deleted file mode 100644 index 944f54622b6f..000000000000 --- a/internal/services/postgres/parse/database_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = DatabaseId{} - -func TestDatabaseIDFormatter(t *testing.T) { - actual := NewDatabaseID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "database1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/database1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestDatabaseID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *DatabaseId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/database1", - Expected: &DatabaseId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - Name: "database1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/DATABASES/DATABASE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := DatabaseID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/parse/firewall_rule.go b/internal/services/postgres/parse/firewall_rule.go deleted file mode 100644 index d18754cb95dd..000000000000 --- a/internal/services/postgres/parse/firewall_rule.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type FirewallRuleId struct { - SubscriptionId string - ResourceGroup string - ServerName string - Name string -} - -func NewFirewallRuleID(subscriptionId, resourceGroup, serverName, name string) FirewallRuleId { - return FirewallRuleId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - Name: name, - } -} - -func (id FirewallRuleId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Firewall Rule", segmentsStr) -} - -func (id FirewallRuleId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s/firewallRules/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.Name) -} - -// FirewallRuleID parses a FirewallRule ID into an FirewallRuleId struct -func FirewallRuleID(input string) (*FirewallRuleId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := FirewallRuleId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("firewallRules"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/firewall_rule_test.go b/internal/services/postgres/parse/firewall_rule_test.go deleted file mode 100644 index 9f8e0caf1986..000000000000 --- a/internal/services/postgres/parse/firewall_rule_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = FirewallRuleId{} - -func TestFirewallRuleIDFormatter(t *testing.T) { - actual := NewFirewallRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "firewallRule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/firewallRule1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestFirewallRuleID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *FirewallRuleId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/firewallRule1", - Expected: &FirewallRuleId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - Name: "firewallRule1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/FIREWALLRULES/FIREWALLRULE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := FirewallRuleID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/parse/flexible_server.go b/internal/services/postgres/parse/flexible_server.go deleted file mode 100644 index ae13a5c53a30..000000000000 --- a/internal/services/postgres/parse/flexible_server.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type FlexibleServerId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewFlexibleServerID(subscriptionId, resourceGroup, name string) FlexibleServerId { - return FlexibleServerId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id FlexibleServerId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Flexible Server", segmentsStr) -} - -func (id FlexibleServerId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/flexibleServers/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// FlexibleServerID parses a FlexibleServer ID into an FlexibleServerId struct -func FlexibleServerID(input string) (*FlexibleServerId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := FlexibleServerId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("flexibleServers"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/flexible_server_configuration.go b/internal/services/postgres/parse/flexible_server_configuration.go deleted file mode 100644 index 8667dc68fa20..000000000000 --- a/internal/services/postgres/parse/flexible_server_configuration.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type FlexibleServerConfigurationId struct { - SubscriptionId string - ResourceGroup string - FlexibleServerName string - ConfigurationName string -} - -func NewFlexibleServerConfigurationID(subscriptionId, resourceGroup, flexibleServerName, configurationName string) FlexibleServerConfigurationId { - return FlexibleServerConfigurationId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - FlexibleServerName: flexibleServerName, - ConfigurationName: configurationName, - } -} - -func (id FlexibleServerConfigurationId) String() string { - segments := []string{ - fmt.Sprintf("Configuration Name %q", id.ConfigurationName), - fmt.Sprintf("Flexible Server Name %q", id.FlexibleServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Flexible Server Configuration", segmentsStr) -} - -func (id FlexibleServerConfigurationId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/flexibleServers/%s/configurations/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.FlexibleServerName, id.ConfigurationName) -} - -// FlexibleServerConfigurationID parses a FlexibleServerConfiguration ID into an FlexibleServerConfigurationId struct -func FlexibleServerConfigurationID(input string) (*FlexibleServerConfigurationId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := FlexibleServerConfigurationId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.FlexibleServerName, err = id.PopSegment("flexibleServers"); err != nil { - return nil, err - } - if resourceId.ConfigurationName, err = id.PopSegment("configurations"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/flexible_server_configuration_test.go b/internal/services/postgres/parse/flexible_server_configuration_test.go deleted file mode 100644 index bd0adcadb08d..000000000000 --- a/internal/services/postgres/parse/flexible_server_configuration_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = FlexibleServerConfigurationId{} - -func TestFlexibleServerConfigurationIDFormatter(t *testing.T) { - actual := NewFlexibleServerConfigurationID("12345678-1234-9876-4563-123456789012", "resGroup1", "flexibleServer1", "configuration1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/configuration1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestFlexibleServerConfigurationID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *FlexibleServerConfigurationId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Error: true, - }, - - { - // missing ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Error: true, - }, - - { - // missing value for ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/configuration1", - Expected: &FlexibleServerConfigurationId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - FlexibleServerName: "flexibleServer1", - ConfigurationName: "configuration1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/CONFIGURATIONS/CONFIGURATION1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := FlexibleServerConfigurationID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.FlexibleServerName != v.Expected.FlexibleServerName { - t.Fatalf("Expected %q but got %q for FlexibleServerName", v.Expected.FlexibleServerName, actual.FlexibleServerName) - } - if actual.ConfigurationName != v.Expected.ConfigurationName { - t.Fatalf("Expected %q but got %q for ConfigurationName", v.Expected.ConfigurationName, actual.ConfigurationName) - } - } -} diff --git a/internal/services/postgres/parse/flexible_server_database.go b/internal/services/postgres/parse/flexible_server_database.go deleted file mode 100644 index 35515795781f..000000000000 --- a/internal/services/postgres/parse/flexible_server_database.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type FlexibleServerDatabaseId struct { - SubscriptionId string - ResourceGroup string - FlexibleServerName string - DatabaseName string -} - -func NewFlexibleServerDatabaseID(subscriptionId, resourceGroup, flexibleServerName, databaseName string) FlexibleServerDatabaseId { - return FlexibleServerDatabaseId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - FlexibleServerName: flexibleServerName, - DatabaseName: databaseName, - } -} - -func (id FlexibleServerDatabaseId) String() string { - segments := []string{ - fmt.Sprintf("Database Name %q", id.DatabaseName), - fmt.Sprintf("Flexible Server Name %q", id.FlexibleServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Flexible Server Database", segmentsStr) -} - -func (id FlexibleServerDatabaseId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/flexibleServers/%s/databases/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.FlexibleServerName, id.DatabaseName) -} - -// FlexibleServerDatabaseID parses a FlexibleServerDatabase ID into an FlexibleServerDatabaseId struct -func FlexibleServerDatabaseID(input string) (*FlexibleServerDatabaseId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := FlexibleServerDatabaseId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.FlexibleServerName, err = id.PopSegment("flexibleServers"); err != nil { - return nil, err - } - if resourceId.DatabaseName, err = id.PopSegment("databases"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/flexible_server_database_test.go b/internal/services/postgres/parse/flexible_server_database_test.go deleted file mode 100644 index ca6f4d64eb8d..000000000000 --- a/internal/services/postgres/parse/flexible_server_database_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = FlexibleServerDatabaseId{} - -func TestFlexibleServerDatabaseIDFormatter(t *testing.T) { - actual := NewFlexibleServerDatabaseID("12345678-1234-9876-4563-123456789012", "resGroup1", "flexibleServer1", "database1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/database1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestFlexibleServerDatabaseID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *FlexibleServerDatabaseId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Error: true, - }, - - { - // missing DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Error: true, - }, - - { - // missing value for DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/database1", - Expected: &FlexibleServerDatabaseId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - FlexibleServerName: "flexibleServer1", - DatabaseName: "database1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/DATABASES/DATABASE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := FlexibleServerDatabaseID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.FlexibleServerName != v.Expected.FlexibleServerName { - t.Fatalf("Expected %q but got %q for FlexibleServerName", v.Expected.FlexibleServerName, actual.FlexibleServerName) - } - if actual.DatabaseName != v.Expected.DatabaseName { - t.Fatalf("Expected %q but got %q for DatabaseName", v.Expected.DatabaseName, actual.DatabaseName) - } - } -} diff --git a/internal/services/postgres/parse/flexible_server_firewall_rule.go b/internal/services/postgres/parse/flexible_server_firewall_rule.go deleted file mode 100644 index 7a3d935c4c56..000000000000 --- a/internal/services/postgres/parse/flexible_server_firewall_rule.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type FlexibleServerFirewallRuleId struct { - SubscriptionId string - ResourceGroup string - FlexibleServerName string - FirewallRuleName string -} - -func NewFlexibleServerFirewallRuleID(subscriptionId, resourceGroup, flexibleServerName, firewallRuleName string) FlexibleServerFirewallRuleId { - return FlexibleServerFirewallRuleId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - FlexibleServerName: flexibleServerName, - FirewallRuleName: firewallRuleName, - } -} - -func (id FlexibleServerFirewallRuleId) String() string { - segments := []string{ - fmt.Sprintf("Firewall Rule Name %q", id.FirewallRuleName), - fmt.Sprintf("Flexible Server Name %q", id.FlexibleServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Flexible Server Firewall Rule", segmentsStr) -} - -func (id FlexibleServerFirewallRuleId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/flexibleServers/%s/firewallRules/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.FlexibleServerName, id.FirewallRuleName) -} - -// FlexibleServerFirewallRuleID parses a FlexibleServerFirewallRule ID into an FlexibleServerFirewallRuleId struct -func FlexibleServerFirewallRuleID(input string) (*FlexibleServerFirewallRuleId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := FlexibleServerFirewallRuleId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.FlexibleServerName, err = id.PopSegment("flexibleServers"); err != nil { - return nil, err - } - if resourceId.FirewallRuleName, err = id.PopSegment("firewallRules"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/flexible_server_firewall_rule_test.go b/internal/services/postgres/parse/flexible_server_firewall_rule_test.go deleted file mode 100644 index 0f938c5b3074..000000000000 --- a/internal/services/postgres/parse/flexible_server_firewall_rule_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = FlexibleServerFirewallRuleId{} - -func TestFlexibleServerFirewallRuleIDFormatter(t *testing.T) { - actual := NewFlexibleServerFirewallRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "flexibleServer1", "firewallRule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/firewallRule1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestFlexibleServerFirewallRuleID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *FlexibleServerFirewallRuleId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Error: true, - }, - - { - // missing FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Error: true, - }, - - { - // missing value for FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/firewallRule1", - Expected: &FlexibleServerFirewallRuleId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - FlexibleServerName: "flexibleServer1", - FirewallRuleName: "firewallRule1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/FIREWALLRULES/FIREWALLRULE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := FlexibleServerFirewallRuleID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.FlexibleServerName != v.Expected.FlexibleServerName { - t.Fatalf("Expected %q but got %q for FlexibleServerName", v.Expected.FlexibleServerName, actual.FlexibleServerName) - } - if actual.FirewallRuleName != v.Expected.FirewallRuleName { - t.Fatalf("Expected %q but got %q for FirewallRuleName", v.Expected.FirewallRuleName, actual.FirewallRuleName) - } - } -} diff --git a/internal/services/postgres/parse/flexible_server_test.go b/internal/services/postgres/parse/flexible_server_test.go deleted file mode 100644 index 9902bf014fcc..000000000000 --- a/internal/services/postgres/parse/flexible_server_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = FlexibleServerId{} - -func TestFlexibleServerIDFormatter(t *testing.T) { - actual := NewFlexibleServerID("12345678-1234-9876-4563-123456789012", "resGroup1", "flexibleServer1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestFlexibleServerID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *FlexibleServerId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1", - Expected: &FlexibleServerId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "flexibleServer1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := FlexibleServerID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/parse/server.go b/internal/services/postgres/parse/server.go deleted file mode 100644 index 17dc8a9e70c8..000000000000 --- a/internal/services/postgres/parse/server.go +++ /dev/null @@ -1,69 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ServerId struct { - SubscriptionId string - ResourceGroup string - Name string -} - -func NewServerID(subscriptionId, resourceGroup, name string) ServerId { - return ServerId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - Name: name, - } -} - -func (id ServerId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Server", segmentsStr) -} - -func (id ServerId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) -} - -// ServerID parses a Server ID into an ServerId struct -func ServerID(input string) (*ServerId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ServerId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.Name, err = id.PopSegment("servers"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/server_key.go b/internal/services/postgres/parse/server_key.go deleted file mode 100644 index 31da5fcb0ec1..000000000000 --- a/internal/services/postgres/parse/server_key.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type ServerKeyId struct { - SubscriptionId string - ResourceGroup string - ServerName string - KeyName string -} - -func NewServerKeyID(subscriptionId, resourceGroup, serverName, keyName string) ServerKeyId { - return ServerKeyId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - KeyName: keyName, - } -} - -func (id ServerKeyId) String() string { - segments := []string{ - fmt.Sprintf("Key Name %q", id.KeyName), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Server Key", segmentsStr) -} - -func (id ServerKeyId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s/keys/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.KeyName) -} - -// ServerKeyID parses a ServerKey ID into an ServerKeyId struct -func ServerKeyID(input string) (*ServerKeyId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := ServerKeyId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.KeyName, err = id.PopSegment("keys"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/server_key_test.go b/internal/services/postgres/parse/server_key_test.go deleted file mode 100644 index 4e4a93c4695f..000000000000 --- a/internal/services/postgres/parse/server_key_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ServerKeyId{} - -func TestServerKeyIDFormatter(t *testing.T) { - actual := NewServerKeyID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "key1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/key1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestServerKeyID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ServerKeyId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // missing KeyName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Error: true, - }, - - { - // missing value for KeyName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/key1", - Expected: &ServerKeyId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - KeyName: "key1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/KEYS/KEY1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ServerKeyID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.KeyName != v.Expected.KeyName { - t.Fatalf("Expected %q but got %q for KeyName", v.Expected.KeyName, actual.KeyName) - } - } -} diff --git a/internal/services/postgres/parse/server_test.go b/internal/services/postgres/parse/server_test.go deleted file mode 100644 index 7b13b5a04921..000000000000 --- a/internal/services/postgres/parse/server_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = ServerId{} - -func TestServerIDFormatter(t *testing.T) { - actual := NewServerID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestServerID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *ServerId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1", - Expected: &ServerId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - Name: "server1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := ServerID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/parse/virtual_network_rule.go b/internal/services/postgres/parse/virtual_network_rule.go deleted file mode 100644 index f080046b235b..000000000000 --- a/internal/services/postgres/parse/virtual_network_rule.go +++ /dev/null @@ -1,75 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -type VirtualNetworkRuleId struct { - SubscriptionId string - ResourceGroup string - ServerName string - Name string -} - -func NewVirtualNetworkRuleID(subscriptionId, resourceGroup, serverName, name string) VirtualNetworkRuleId { - return VirtualNetworkRuleId{ - SubscriptionId: subscriptionId, - ResourceGroup: resourceGroup, - ServerName: serverName, - Name: name, - } -} - -func (id VirtualNetworkRuleId) String() string { - segments := []string{ - fmt.Sprintf("Name %q", id.Name), - fmt.Sprintf("Server Name %q", id.ServerName), - fmt.Sprintf("Resource Group %q", id.ResourceGroup), - } - segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Virtual Network Rule", segmentsStr) -} - -func (id VirtualNetworkRuleId) ID() string { - fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.DBforPostgreSQL/servers/%s/virtualNetworkRules/%s" - return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ServerName, id.Name) -} - -// VirtualNetworkRuleID parses a VirtualNetworkRule ID into an VirtualNetworkRuleId struct -func VirtualNetworkRuleID(input string) (*VirtualNetworkRuleId, error) { - id, err := resourceids.ParseAzureResourceID(input) - if err != nil { - return nil, err - } - - resourceId := VirtualNetworkRuleId{ - SubscriptionId: id.SubscriptionID, - ResourceGroup: id.ResourceGroup, - } - - if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") - } - - if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") - } - - if resourceId.ServerName, err = id.PopSegment("servers"); err != nil { - return nil, err - } - if resourceId.Name, err = id.PopSegment("virtualNetworkRules"); err != nil { - return nil, err - } - - if err := id.ValidateNoEmptySegments(input); err != nil { - return nil, err - } - - return &resourceId, nil -} diff --git a/internal/services/postgres/parse/virtual_network_rule_test.go b/internal/services/postgres/parse/virtual_network_rule_test.go deleted file mode 100644 index e0a631283c16..000000000000 --- a/internal/services/postgres/parse/virtual_network_rule_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package parse - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" -) - -var _ resourceids.Id = VirtualNetworkRuleId{} - -func TestVirtualNetworkRuleIDFormatter(t *testing.T) { - actual := NewVirtualNetworkRuleID("12345678-1234-9876-4563-123456789012", "resGroup1", "server1", "virtualNetworkRule1").ID() - expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/virtualNetworkRule1" - if actual != expected { - t.Fatalf("Expected %q but got %q", expected, actual) - } -} - -func TestVirtualNetworkRuleID(t *testing.T) { - testData := []struct { - Input string - Error bool - Expected *VirtualNetworkRuleId - }{ - - { - // empty - Input: "", - Error: true, - }, - - { - // missing SubscriptionId - Input: "/", - Error: true, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Error: true, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Error: true, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Error: true, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Error: true, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Error: true, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Error: true, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/", - Error: true, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/virtualNetworkRule1", - Expected: &VirtualNetworkRuleId{ - SubscriptionId: "12345678-1234-9876-4563-123456789012", - ResourceGroup: "resGroup1", - ServerName: "server1", - Name: "virtualNetworkRule1", - }, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/VIRTUALNETWORKRULES/VIRTUALNETWORKRULE1", - Error: true, - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %q", v.Input) - - actual, err := VirtualNetworkRuleID(v.Input) - if err != nil { - if v.Error { - continue - } - - t.Fatalf("Expect a value but got an error: %s", err) - } - if v.Error { - t.Fatal("Expect an error but didn't get one") - } - - if actual.SubscriptionId != v.Expected.SubscriptionId { - t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) - } - if actual.ResourceGroup != v.Expected.ResourceGroup { - t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) - } - if actual.ServerName != v.Expected.ServerName { - t.Fatalf("Expected %q but got %q for ServerName", v.Expected.ServerName, actual.ServerName) - } - if actual.Name != v.Expected.Name { - t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) - } - } -} diff --git a/internal/services/postgres/postgresql_aad_administrator_resource.go b/internal/services/postgres/postgresql_aad_administrator_resource.go index 6633b2828240..f511851b2504 100644 --- a/internal/services/postgres/postgresql_aad_administrator_resource.go +++ b/internal/services/postgres/postgresql_aad_administrator_resource.go @@ -5,17 +5,16 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" - "github.com/gofrs/uuid" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serveradministrators" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourcePostgreSQLAdministrator() *pluginsdk.Resource { @@ -25,7 +24,7 @@ func resourcePostgreSQLAdministrator() *pluginsdk.Resource { Update: resourcePostgreSQLAdministratorCreateUpdate, Delete: resourcePostgreSQLAdministratorDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.AzureActiveDirectoryAdministratorID(id) + _, err := serveradministrators.ParseServerID(id) return err }), @@ -36,6 +35,11 @@ func resourcePostgreSQLAdministrator() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, + SchemaVersion: 1, + StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{ + 0: migration.PostgresqlAADAdministratorV0ToV1{}, + }), + Schema: map[string]*pluginsdk.Schema{ "server_name": { Type: pluginsdk.TypeString, @@ -72,40 +76,32 @@ func resourcePostgreSQLAdministratorCreateUpdate(d *pluginsdk.ResourceData, meta ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - login := d.Get("login").(string) - objectId := uuid.FromStringOrNil(d.Get("object_id").(string)) - tenantId := uuid.FromStringOrNil(d.Get("tenant_id").(string)) - - id := parse.NewAzureActiveDirectoryAdministratorID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), "activeDirectory") + id := serveradministrators.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_active_directory_administrator", id.ID()) } } - parameters := postgresql.ServerAdministratorResource{ - ServerAdministratorProperties: &postgresql.ServerAdministratorProperties{ - AdministratorType: utils.String("ActiveDirectory"), - Login: utils.String(login), - Sid: &objectId, - TenantID: &tenantId, + parameters := serveradministrators.ServerAdministratorResource{ + Properties: &serveradministrators.ServerAdministratorProperties{ + AdministratorType: serveradministrators.AdministratorTypeActiveDirectory, + Login: d.Get("login").(string), + Sid: d.Get("object_id").(string), + TenantId: d.Get("tenant_id").(string), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, parameters) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %+v", id, err) - } d.SetId(id.ID()) return nil @@ -116,14 +112,14 @@ func resourcePostgreSQLAdministratorRead(d *pluginsdk.ResourceData, meta interfa ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AzureActiveDirectoryAdministratorID(d.Id()) + id, err := serveradministrators.ParseServerID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] %s was not found - removing from state", *id) d.SetId("") return nil @@ -132,23 +128,16 @@ func resourcePostgreSQLAdministratorRead(d *pluginsdk.ResourceData, meta interfa return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - if props := resp.ServerAdministratorProperties; props != nil { - d.Set("login", props.Login) - - objectId := "" - if props.Sid != nil { - objectId = props.Sid.String() + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("login", props.Login) + d.Set("object_id", props.Sid) + d.Set("tenant_id", props.TenantId) } - d.Set("object_id", objectId) - tenantId := "" - if props.TenantID != nil { - tenantId = props.TenantID.String() - } - d.Set("tenant_id", tenantId) } return nil @@ -159,18 +148,14 @@ func resourcePostgreSQLAdministratorDelete(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.AzureActiveDirectoryAdministratorID(d.Id()) + id, err := serveradministrators.ParseServerID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName) - if err != nil { + if err := client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } return nil } diff --git a/internal/services/postgres/postgresql_aad_administrator_resource_test.go b/internal/services/postgres/postgresql_aad_administrator_resource_test.go index eecf8310c1f8..f1aa60ba97ff 100644 --- a/internal/services/postgres/postgresql_aad_administrator_resource_test.go +++ b/internal/services/postgres/postgresql_aad_administrator_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serveradministrators" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -70,26 +71,26 @@ func TestAccPostgreSqlAdministrator_disappears(t *testing.T) { } func (r PostgreSqlAdministratorResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.AzureActiveDirectoryAdministratorID(state.ID) + id, err := serveradministrators.ParseServerID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.ServerAdministratorsClient.Get(ctx, id.ResourceGroup, id.ServerName) + resp, err := clients.Postgres.ServerAdministratorsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading Postgresql AAD Administrator (%s): %+v", id.String(), err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (r PostgreSqlAdministratorResource) Destroy(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.AzureActiveDirectoryAdministratorID(state.ID) + id, err := serveradministrators.ParseServerID(state.ID) if err != nil { return nil, err } - if _, err := client.Postgres.ServerAdministratorsClient.Delete(ctx, id.ResourceGroup, id.ServerName); err != nil { + if _, err := client.Postgres.ServerAdministratorsClient.Delete(ctx, *id); err != nil { return nil, fmt.Errorf("deleting Postgresql AAD Administrator (%s): %+v", id.String(), err) } diff --git a/internal/services/postgres/postgresql_configuration_resource.go b/internal/services/postgres/postgresql_configuration_resource.go index d872bfcbf28b..0b55f229df81 100644 --- a/internal/services/postgres/postgresql_configuration_resource.go +++ b/internal/services/postgres/postgresql_configuration_resource.go @@ -5,11 +5,11 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/configurations" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -22,7 +22,7 @@ func resourcePostgreSQLConfiguration() *pluginsdk.Resource { Read: resourcePostgreSQLConfigurationRead, Delete: resourcePostgreSQLConfigurationDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.ConfigurationID(id) + _, err := configurations.ParseConfigurationID(id) return err }), @@ -64,27 +64,22 @@ func resourcePostgreSQLConfigurationCreateUpdate(d *pluginsdk.ResourceData, meta ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := configurations.NewConfigurationID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) // TODO: support RequiresImport - this is possible to tell if it's the non-default value from the API (see Delete) locks.ByName(id.ServerName, postgreSQLServerResourceName) defer locks.UnlockByName(id.ServerName, postgreSQLServerResourceName) - properties := postgresql.Configuration{ - ConfigurationProperties: &postgresql.ConfigurationProperties{ + properties := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ Value: utils.String(d.Get("value").(string)), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, properties) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourcePostgreSQLConfigurationRead(d, meta) } @@ -94,14 +89,14 @@ func resourcePostgreSQLConfigurationRead(d *pluginsdk.ResourceData, meta interfa ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -110,12 +105,14 @@ func resourcePostgreSQLConfigurationRead(d *pluginsdk.ResourceData, meta interfa return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) + d.Set("name", id.ConfigurationName) d.Set("server_name", id.ServerName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) - if props := resp.ConfigurationProperties; props != nil { - d.Set("value", props.Value) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("value", props.Value) + } } return nil @@ -126,7 +123,7 @@ func resourcePostgreSQLConfigurationDelete(d *pluginsdk.ResourceData, meta inter ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } @@ -135,26 +132,26 @@ func resourcePostgreSQLConfigurationDelete(d *pluginsdk.ResourceData, meta inter defer locks.UnlockByName(id.ServerName, postgreSQLServerResourceName) // "delete" = resetting this to the default value - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - return fmt.Errorf("retrieving Postgresql Configuration '%s': %+v", id.Name, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } - properties := postgresql.Configuration{ - ConfigurationProperties: &postgresql.ConfigurationProperties{ + defaultValue := "" + if resp.Model != nil && resp.Model.Properties != nil && resp.Model.Properties.DefaultValue != nil { + defaultValue = *resp.Model.Properties.DefaultValue + } + + properties := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ // we can alternatively set `source: "system-default"` - Value: resp.DefaultValue, + Value: &defaultValue, }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, properties) - if err != nil { + if err = client.CreateOrUpdateThenPoll(ctx, *id, properties); err != nil { return fmt.Errorf("resetting %s to it's default value: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for %s to reset to it's default value: %+v", *id, err) - } - return nil } diff --git a/internal/services/postgres/postgresql_configuration_resource_test.go b/internal/services/postgres/postgresql_configuration_resource_test.go index 519d4170784b..1f51289eff4a 100644 --- a/internal/services/postgres/postgresql_configuration_resource_test.go +++ b/internal/services/postgres/postgresql_configuration_resource_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/configurations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -95,24 +96,28 @@ func TestAccPostgreSQLConfiguration_multiplePostgreSQLConfigurations(t *testing. func (r PostgreSQLConfigurationResource) checkReset(configurationName string) acceptance.ClientCheckFunc { return func(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) error { - id, err := parse.ServerID(state.Attributes["id"]) + id, err := configurations.ParseServerID(state.Attributes["id"]) if err != nil { return err } - resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.Name, configurationName) + configurationId := configurations.NewConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ServerName, configurationName) + + resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, configurationId) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: PostgreSQL Configuration %q (server %q resource group: %q) does not exist", configurationName, id.Name, id.ResourceGroup) + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s does not exist", id) } return fmt.Errorf("Bad: Get on postgresqlConfigurationsClient: %+v", err) } - actualValue := *resp.Value - defaultValue := *resp.DefaultValue + if resp.Model != nil && resp.Model.Properties != nil { + actualValue := *resp.Model.Properties.Value + defaultValue := *resp.Model.Properties.DefaultValue - if defaultValue != actualValue { - return fmt.Errorf("PostgreSQL Configuration wasn't set to the default value. Expected '%s' - got '%s': \n%+v", defaultValue, actualValue, resp) + if defaultValue != actualValue { + return fmt.Errorf("PostgreSQL Configuration wasn't set to the default value. Expected '%s' - got '%s': \n%+v", defaultValue, actualValue, resp) + } } return nil @@ -121,22 +126,26 @@ func (r PostgreSQLConfigurationResource) checkReset(configurationName string) ac func (r PostgreSQLConfigurationResource) checkValue(value string) acceptance.ClientCheckFunc { return func(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) error { - id, err := parse.ConfigurationID(state.Attributes["id"]) + id, err := configurations.ParseConfigurationID(state.Attributes["id"]) if err != nil { return err } - resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: PostgreSQL Configuration %q (server %q resource group: %q) does not exist", id.Name, id.ServerName, id.ResourceGroup) + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s does not exist", id) } return fmt.Errorf("Bad: Get on postgresqlConfigurationsClient: %+v", err) } - if *resp.Value != value { - return fmt.Errorf("PostgreSQL Configuration wasn't set. Expected '%s' - got '%s': \n%+v", value, *resp.Value, resp) + if resp.Model != nil && resp.Model.Properties != nil && resp.Model.Properties.Value != nil { + if *resp.Model.Properties.Value != value { + return fmt.Errorf("PostgreSQL Configuration wasn't set. Expected '%s' - got '%s': \n%+v", value, *resp.Model.Properties.Value, resp) + } + } else { + return fmt.Errorf("bad get on %s", id) } return nil @@ -144,17 +153,17 @@ func (r PostgreSQLConfigurationResource) checkValue(value string) acceptance.Cli } func (t PostgreSQLConfigurationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ConfigurationID(state.ID) + id, err := configurations.ParseConfigurationID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := clients.Postgres.ConfigurationsClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("reading Postgresql Configuration (%s): %+v", id.String(), err) + return nil, fmt.Errorf("reading %s: %+v", id, err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (r PostgreSQLConfigurationResource) backslashQuote(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_database_resource.go b/internal/services/postgres/postgresql_database_resource.go index 31e01422f839..ac051f4eaf7e 100644 --- a/internal/services/postgres/postgresql_database_resource.go +++ b/internal/services/postgres/postgresql_database_resource.go @@ -5,11 +5,12 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/databases" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" @@ -23,7 +24,7 @@ func resourcePostgreSQLDatabase() *pluginsdk.Resource { Read: resourcePostgreSQLDatabaseRead, Delete: resourcePostgreSQLDatabaseDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.DatabaseID(id) + _, err := databases.ParseDatabaseID(id) return err }), @@ -34,6 +35,11 @@ func resourcePostgreSQLDatabase() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(60 * time.Minute), }, + SchemaVersion: 1, + StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{ + 0: migration.PostgresqlDatabaseV0ToV1{}, + }), + Schema: map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, @@ -73,34 +79,29 @@ func resourcePostgreSQLDatabaseCreate(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewDatabaseID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + id := databases.NewDatabaseID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_database", id.ID()) } - properties := postgresql.Database{ - DatabaseProperties: &postgresql.DatabaseProperties{ + properties := databases.Database{ + Properties: &databases.DatabaseProperties{ Charset: utils.String(d.Get("charset").(string)), Collation: utils.String(d.Get("collation").(string)), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, properties) - if err != nil { + if err = client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourcePostgreSQLDatabaseRead(d, meta) } @@ -110,14 +111,14 @@ func resourcePostgreSQLDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DatabaseID(d.Id()) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -126,13 +127,15 @@ func resourcePostgreSQLDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) + d.Set("name", id.DatabaseName) d.Set("server_name", id.ServerName) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("resource_group_name", id.ResourceGroupName) - if props := resp.DatabaseProperties; props != nil { - d.Set("charset", props.Charset) - d.Set("collation", props.Collation) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("charset", props.Charset) + d.Set("collation", props.Collation) + } } return nil @@ -143,19 +146,14 @@ func resourcePostgreSQLDatabaseDelete(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.DatabaseID(d.Id()) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.Name) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } - return nil } diff --git a/internal/services/postgres/postgresql_database_resource_test.go b/internal/services/postgres/postgresql_database_resource_test.go index 4c93c28ac247..4e3a0a7ed217 100644 --- a/internal/services/postgres/postgresql_database_resource_test.go +++ b/internal/services/postgres/postgresql_database_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/databases" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -92,17 +92,17 @@ func TestAccPostgreSQLDatabase_charsetMixedcase(t *testing.T) { } func (t PostgreSQLDatabaseResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.DatabaseID(state.ID) + id, err := databases.ParseDatabaseID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.DatabasesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := clients.Postgres.DatabasesClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("reading Postgresql Database (%s): %+v", id.String(), err) + return nil, fmt.Errorf("reading %s: %+v", id, err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgreSQLDatabaseResource) basic(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_firewall_rule_resource.go b/internal/services/postgres/postgresql_firewall_rule_resource.go index 075dfb643abc..2dbc7304f92e 100644 --- a/internal/services/postgres/postgresql_firewall_rule_resource.go +++ b/internal/services/postgres/postgresql_firewall_rule_resource.go @@ -6,16 +6,15 @@ import ( "regexp" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourcePostgreSQLFirewallRule() *pluginsdk.Resource { @@ -24,7 +23,7 @@ func resourcePostgreSQLFirewallRule() *pluginsdk.Resource { Read: resourcePostgreSQLFirewallRuleRead, Delete: resourcePostgreSQLFirewallRuleDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.FirewallRuleID(id) + _, err := firewallrules.ParseFirewallRuleID(id) return err }), @@ -78,34 +77,29 @@ func resourcePostgreSQLFirewallRuleCreate(d *pluginsdk.ResourceData, meta interf ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewFirewallRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + id := firewallrules.NewFirewallRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_firewall_rule", id.ID()) } - properties := postgresql.FirewallRule{ - FirewallRuleProperties: &postgresql.FirewallRuleProperties{ - StartIPAddress: utils.String(d.Get("start_ip_address").(string)), - EndIPAddress: utils.String(d.Get("end_ip_address").(string)), + properties := firewallrules.FirewallRule{ + Properties: firewallrules.FirewallRuleProperties{ + StartIPAddress: d.Get("start_ip_address").(string), + EndIPAddress: d.Get("end_ip_address").(string), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, properties) - if err != nil { + if err = client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourcePostgreSQLFirewallRuleRead(d, meta) } @@ -115,14 +109,14 @@ func resourcePostgreSQLFirewallRuleRead(d *pluginsdk.ResourceData, meta interfac ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -131,13 +125,13 @@ func resourcePostgreSQLFirewallRuleRead(d *pluginsdk.ResourceData, meta interfac return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("name", id.FirewallRuleName) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - if props := resp.FirewallRuleProperties; props != nil { - d.Set("start_ip_address", props.StartIPAddress) - d.Set("end_ip_address", props.EndIPAddress) + if model := resp.Model; model != nil { + d.Set("start_ip_address", resp.Model.Properties.StartIPAddress) + d.Set("end_ip_address", resp.Model.Properties.EndIPAddress) } return nil @@ -148,19 +142,14 @@ func resourcePostgreSQLFirewallRuleDelete(d *pluginsdk.ResourceData, meta interf ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.Name) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } - return nil } diff --git a/internal/services/postgres/postgresql_firewall_rule_resource_test.go b/internal/services/postgres/postgresql_firewall_rule_resource_test.go index 7eddc2bdd7a1..6333570b6849 100644 --- a/internal/services/postgres/postgresql_firewall_rule_resource_test.go +++ b/internal/services/postgres/postgresql_firewall_rule_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -48,17 +48,17 @@ func TestAccPostgreSQLFirewallRule_requiresImport(t *testing.T) { } func (t PostgreSQLFirewallRuleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.FirewallRuleID(state.ID) + id, err := firewallrules.ParseFirewallRuleID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.FirewallRulesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := clients.Postgres.FirewallRulesClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading Postgresql Firewall Rule (%s): %+v", id.String(), err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgreSQLFirewallRuleResource) basic(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_flexible_server_configuration_resource.go b/internal/services/postgres/postgresql_flexible_server_configuration_resource.go index ea5c7ac9a7ef..2514a9eaf1a1 100644 --- a/internal/services/postgres/postgresql_flexible_server_configuration_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_configuration_resource.go @@ -5,11 +5,10 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/configurations" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -31,7 +30,7 @@ func resourcePostgresqlFlexibleServerConfiguration() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.FlexibleServerConfigurationID(id) + _, err := configurations.ParseConfigurationID(id) return err }), @@ -47,7 +46,7 @@ func resourcePostgresqlFlexibleServerConfiguration() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.FlexibleServerID, + ValidateFunc: configurations.ValidateFlexibleServerID, }, "value": { @@ -67,33 +66,26 @@ func resourceFlexibleServerConfigurationUpdate(d *pluginsdk.ResourceData, meta i log.Printf("[INFO] preparing arguments for Azure Postgresql Flexible Server configuration creation.") - name := d.Get("name").(string) - serverId, err := parse.FlexibleServerID(d.Get("server_id").(string)) + serverId, err := configurations.ParseFlexibleServerID(d.Get("server_id").(string)) if err != nil { return err } + id := configurations.NewConfigurationID(subscriptionId, serverId.ResourceGroupName, serverId.ServerName, d.Get("name").(string)) - id := parse.NewFlexibleServerConfigurationID(subscriptionId, serverId.ResourceGroup, serverId.Name, name) + locks.ByName(id.ServerName, postgresqlFlexibleServerResourceName) + defer locks.UnlockByName(id.ServerName, postgresqlFlexibleServerResourceName) - locks.ByName(id.FlexibleServerName, postgresqlFlexibleServerResourceName) - defer locks.UnlockByName(id.FlexibleServerName, postgresqlFlexibleServerResourceName) - - props := postgresqlflexibleservers.Configuration{ - ConfigurationProperties: &postgresqlflexibleservers.ConfigurationProperties{ + props := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ Value: utils.String(d.Get("value").(string)), Source: utils.String("user-override"), }, } - future, err := client.Update(ctx, serverId.ResourceGroup, serverId.Name, name, props) - if err != nil { + if err := client.UpdateThenPoll(ctx, id, props); err != nil { return fmt.Errorf("updating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for create/update of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourceFlexibleServerConfigurationRead(d, meta) @@ -105,14 +97,14 @@ func resourceFlexibleServerConfigurationRead(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.ConfigurationName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found, removing from state", id) d.SetId("") return nil @@ -122,10 +114,10 @@ func resourceFlexibleServerConfigurationRead(d *pluginsdk.ResourceData, meta int } d.Set("name", id.ConfigurationName) - d.Set("server_id", parse.NewFlexibleServerID(subscriptionId, id.ResourceGroup, id.FlexibleServerName).ID()) + d.Set("server_id", configurations.NewFlexibleServerID(subscriptionId, id.ResourceGroupName, id.ServerName).ID()) - if props := resp.ConfigurationProperties; props != nil { - d.Set("value", props.Value) + if resp.Model != nil && resp.Model.Properties != nil { + d.Set("value", resp.Model.Properties.Value) } return nil @@ -136,34 +128,34 @@ func resourceFlexibleServerConfigurationDelete(d *pluginsdk.ResourceData, meta i ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerConfigurationID(d.Id()) + id, err := configurations.ParseConfigurationID(d.Id()) if err != nil { return err } - locks.ByName(id.FlexibleServerName, postgresqlFlexibleServerResourceName) - defer locks.UnlockByName(id.FlexibleServerName, postgresqlFlexibleServerResourceName) + locks.ByName(id.ServerName, postgresqlFlexibleServerResourceName) + defer locks.UnlockByName(id.ServerName, postgresqlFlexibleServerResourceName) - resp, err := client.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.ConfigurationName) + resp, err := client.Get(ctx, *id) if err != nil { return fmt.Errorf("retrieving %s: %+v", id, err) } - props := postgresqlflexibleservers.Configuration{ - ConfigurationProperties: &postgresqlflexibleservers.ConfigurationProperties{ - Value: resp.DefaultValue, + defaultValue := "" + if resp.Model != nil && resp.Model.Properties != nil && resp.Model.Properties.DefaultValue != nil { + defaultValue = *resp.Model.Properties.DefaultValue + } + + props := configurations.Configuration{ + Properties: &configurations.ConfigurationProperties{ + Value: &defaultValue, Source: utils.String("user-override"), }, } - future, err := client.Update(ctx, id.ResourceGroup, id.FlexibleServerName, id.ConfigurationName, props) - if err != nil { + if err = client.UpdateThenPoll(ctx, *id, props); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", id, err) - } - return nil } diff --git a/internal/services/postgres/postgresql_flexible_server_configuration_resource_test.go b/internal/services/postgres/postgresql_flexible_server_configuration_resource_test.go index 58661d87c5c0..1ed5128bab04 100644 --- a/internal/services/postgres/postgresql_flexible_server_configuration_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_configuration_resource_test.go @@ -5,10 +5,12 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-helpers/lang/response" + + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/configurations" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -111,24 +113,32 @@ func TestAccFlexibleServerConfiguration_updateApplicationName(t *testing.T) { func (r PostgresqlFlexibleServerConfigurationResource) checkReset(configurationName string) acceptance.ClientCheckFunc { return func(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) error { - id, err := parse.FlexibleServerID(state.ID) + id, err := configurations.ParseFlexibleServerID(state.ID) if err != nil { return err } - resp, err := clients.Postgres.FlexibleServersConfigurationsClient.Get(ctx, id.ResourceGroup, id.Name, configurationName) + configurationId := configurations.NewConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ServerName, configurationName) + + resp, err := clients.Postgres.FlexibleServersConfigurationsClient.Get(ctx, configurationId) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: Azure Postgresql Flexible Server configuration %q (server %q resource group: %q) does not exist", configurationName, id.Name, id.ResourceGroup) + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s does not exist", id) } return fmt.Errorf("Bad: Get on postgresqlConfigurationsClient: %+v", err) } - actualValue := *resp.Value - defaultValue := *resp.DefaultValue + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if props.Value != nil && props.DefaultValue != nil { + actualValue := *props.Value + defaultValue := *props.DefaultValue - if defaultValue != actualValue { - return fmt.Errorf("Azure Postgresql Flexible Server configuration wasn't set to the default value. Expected '%s' - got '%s': \n%+v", defaultValue, actualValue, resp) + if defaultValue != actualValue { + return fmt.Errorf("Azure Postgresql Flexible Server configuration wasn't set to the default value. Expected '%s' - got '%s': \n%+v", defaultValue, actualValue, resp) + } + } + } } return nil @@ -152,17 +162,17 @@ func TestAccFlexibleServerConfiguration_multiplePostgresqlFlexibleServerConfigur // Helper functions for verification func (r PostgresqlFlexibleServerConfigurationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.FlexibleServerConfigurationID(state.ID) + id, err := configurations.ParseConfigurationID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.FlexibleServersConfigurationsClient.Get(ctx, id.ResourceGroup, id.FlexibleServerName, state.Attributes["name"]) + resp, err := clients.Postgres.FlexibleServersConfigurationsClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading Postgresql Configuration (%s): %+v", id.String(), err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgresqlFlexibleServerConfigurationResource) template(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_flexible_server_data_source.go b/internal/services/postgres/postgresql_flexible_server_data_source.go index c8eb4192896e..968b30c54a26 100644 --- a/internal/services/postgres/postgresql_flexible_server_data_source.go +++ b/internal/services/postgres/postgresql_flexible_server_data_source.go @@ -4,15 +4,14 @@ import ( "fmt" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourcePostgresqlFlexibleServer() *pluginsdk.Resource { @@ -73,7 +72,7 @@ func dataSourcePostgresqlFlexibleServer() *pluginsdk.Resource { Computed: true, }, - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } @@ -87,45 +86,56 @@ func dataSourceArmPostgresqlFlexibleServerRead(d *pluginsdk.ResourceData, meta i name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - id := parse.NewFlexibleServerID(subscriptionId, resourceGroup, name) + id := servers.NewFlexibleServerID(subscriptionId, resourceGroup, name) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Postgresqlflexibleservers Server %q does not exist", id.Name) + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s does not exist", id) } - return fmt.Errorf("retrieving Postgresqlflexibleservers Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } d.SetId(id.ID()) - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) + d.Set("name", id.ServerName) + d.Set("resource_group_name", id.ResourceGroupName) + + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(&model.Location)) + + if props := model.Properties; props != nil { + d.Set("administrator_login", props.AdministratorLogin) + d.Set("version", props.Version) + d.Set("fqdn", props.FullyQualifiedDomainName) + + if storage := props.Storage; storage != nil && storage.StorageSizeGB != nil { + d.Set("storage_mb", (*props.Storage.StorageSizeGB * 1024)) + } + + if backup := props.Backup; backup != nil { + d.Set("backup_retention_days", props.Backup.BackupRetentionDays) + } + + if network := props.Network; network != nil { + d.Set("delegated_subnet_id", network.DelegatedSubnetResourceId) + publicNetworkAccess := false + if network.PublicNetworkAccess != nil { + publicNetworkAccess = *network.PublicNetworkAccess == servers.ServerPublicNetworkAccessStateEnabled + } + d.Set("public_network_access_enabled", publicNetworkAccess) + } - if props := resp.ServerProperties; props != nil { - d.Set("administrator_login", props.AdministratorLogin) - d.Set("version", props.Version) - d.Set("fqdn", props.FullyQualifiedDomainName) - - if storage := props.Storage; storage != nil && storage.StorageSizeGB != nil { - d.Set("storage_mb", (*props.Storage.StorageSizeGB * 1024)) } - if backup := props.Backup; backup != nil { - d.Set("backup_retention_days", props.Backup.BackupRetentionDays) + sku, err := flattenFlexibleServerSku(model.Sku) + if err != nil { + return fmt.Errorf("flattening `sku_name`: %+v", err) } - if network := props.Network; network != nil { - d.Set("delegated_subnet_id", network.DelegatedSubnetResourceID) - d.Set("public_network_access_enabled", network.PublicNetworkAccess == postgresqlflexibleservers.ServerPublicNetworkAccessStateEnabled) - } - } + d.Set("sku_name", sku) - sku, err := flattenFlexibleServerSku(resp.Sku) - if err != nil { - return fmt.Errorf("flattening `sku_name` for PostgreSQL Flexible Server %s (Resource Group %q): %v", id.Name, id.ResourceGroup, err) + return tags.FlattenAndSet(d, model.Tags) } - d.Set("sku_name", sku) - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/postgres/postgresql_flexible_server_database_resource.go b/internal/services/postgres/postgresql_flexible_server_database_resource.go index 3d88d980233b..5e67ec8a3636 100644 --- a/internal/services/postgres/postgresql_flexible_server_database_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_database_resource.go @@ -5,10 +5,10 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/databases" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" @@ -22,7 +22,7 @@ func resourcePostgresqlFlexibleServerDatabase() *pluginsdk.Resource { Read: resourcePostgresqlFlexibleServerDatabaseRead, Delete: resourcePostgresqlFlexibleServerDatabaseDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.FlexibleServerDatabaseID(id) + _, err := databases.ParseDatabaseID(id) return err }), @@ -44,7 +44,7 @@ func resourcePostgresqlFlexibleServerDatabase() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.FlexibleServerID, + ValidateFunc: databases.ValidateFlexibleServerID, }, "charset": { @@ -73,42 +73,36 @@ func resourcePostgresqlFlexibleServerDatabaseCreate(d *pluginsdk.ResourceData, m ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - serverId, err := parse.FlexibleServerID(d.Get("server_id").(string)) + serverId, err := databases.ParseFlexibleServerID(d.Get("server_id").(string)) if err != nil { return err } - id := parse.NewFlexibleServerDatabaseID(subscriptionId, serverId.ResourceGroup, serverId.Name, name) + id := databases.NewDatabaseID(subscriptionId, serverId.ResourceGroupName, serverId.ServerName, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, serverId.ResourceGroup, serverId.Name, name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %q: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_flexible_server_database", id.ID()) } } - properties := postgresqlflexibleservers.Database{ - DatabaseProperties: &postgresqlflexibleservers.DatabaseProperties{ + properties := databases.Database{ + Properties: &databases.DatabaseProperties{ Charset: utils.String(d.Get("charset").(string)), Collation: utils.String(d.Get("collation").(string)), }, } - future, err := client.Create(ctx, id.ResourceGroup, id.FlexibleServerName, id.DatabaseName, properties) - if err != nil { + if err = client.CreateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of %s: %+v", id, err) - } - d.SetId(id.ID()) return resourcePostgresqlFlexibleServerDatabaseRead(d, meta) } @@ -119,14 +113,14 @@ func resourcePostgresqlFlexibleServerDatabaseRead(d *pluginsdk.ResourceData, met ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerDatabaseID(d.Id()) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.DatabaseName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if !response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] %s does not exist - removing from state", *id) d.SetId("") return nil @@ -134,11 +128,13 @@ func resourcePostgresqlFlexibleServerDatabaseRead(d *pluginsdk.ResourceData, met return fmt.Errorf("retrieving %s: %+v", *id, err) } d.Set("name", id.DatabaseName) - d.Set("server_id", parse.NewFlexibleServerID(subscriptionId, id.ResourceGroup, id.FlexibleServerName).ID()) + d.Set("server_id", databases.NewFlexibleServerID(subscriptionId, id.ResourceGroupName, id.ServerName).ID()) - if props := resp.DatabaseProperties; props != nil { - d.Set("charset", props.Charset) - d.Set("collation", props.Collation) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("charset", props.Charset) + d.Set("collation", props.Collation) + } } return nil @@ -149,19 +145,14 @@ func resourcePostgresqlFlexibleServerDatabaseDelete(d *pluginsdk.ResourceData, m ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerDatabaseID(d.Id()) + id, err := databases.ParseDatabaseID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.FlexibleServerName, id.DatabaseName) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deleting of %s: %+v", *id, err) - } - return nil } diff --git a/internal/services/postgres/postgresql_flexible_server_database_resource_test.go b/internal/services/postgres/postgresql_flexible_server_database_resource_test.go index 115efd5a8b01..353a0c5b3e8d 100644 --- a/internal/services/postgres/postgresql_flexible_server_database_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_database_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/databases" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -76,17 +76,17 @@ func TestAccPostgresqlFlexibleServerDatabase_withoutCharsetAndCollation(t *testi } func (PostgresqlFlexibleServerDatabaseResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.FlexibleServerDatabaseID(state.ID) + id, err := databases.ParseDatabaseID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.FlexibleServerDatabaseClient.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.DatabaseName) + resp, err := clients.Postgres.FlexibleServerDatabaseClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.DatabaseProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (r PostgresqlFlexibleServerDatabaseResource) requiresImport(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource.go b/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource.go index b051ca083bf9..6a2e282df01a 100644 --- a/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource.go @@ -5,15 +5,14 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func resourcePostgresqlFlexibleServerFirewallRule() *pluginsdk.Resource { @@ -31,7 +30,7 @@ func resourcePostgresqlFlexibleServerFirewallRule() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.FlexibleServerFirewallRuleID(id) + _, err := firewallrules.ParseFirewallRuleID(id) return err }), @@ -47,7 +46,7 @@ func resourcePostgresqlFlexibleServerFirewallRule() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.FlexibleServerID, + ValidateFunc: firewallrules.ValidateFlexibleServerID, }, "end_ip_address": { @@ -71,42 +70,36 @@ func resourcePostgresqlFlexibleServerFirewallRuleCreateUpdate(d *pluginsdk.Resou ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - serverId, err := parse.FlexibleServerID(d.Get("server_id").(string)) + serverId, err := firewallrules.ParseFlexibleServerID(d.Get("server_id").(string)) if err != nil { return err } - id := parse.NewFlexibleServerFirewallRuleID(subscriptionId, serverId.ResourceGroup, serverId.Name, name) + id := firewallrules.NewFirewallRuleID(subscriptionId, serverId.ResourceGroupName, serverId.ServerName, d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, serverId.ResourceGroup, serverId.Name, name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for present of existing %q: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_flexible_server_firewall_rule", id.ID()) } } - properties := postgresqlflexibleservers.FirewallRule{ - FirewallRuleProperties: &postgresqlflexibleservers.FirewallRuleProperties{ - EndIPAddress: utils.String(d.Get("end_ip_address").(string)), - StartIPAddress: utils.String(d.Get("start_ip_address").(string)), + properties := firewallrules.FirewallRule{ + Properties: firewallrules.FirewallRuleProperties{ + EndIPAddress: d.Get("end_ip_address").(string), + StartIPAddress: d.Get("start_ip_address").(string), }, } - future, err := client.CreateOrUpdate(ctx, serverId.ResourceGroup, serverId.Name, name, properties) - if err != nil { + if err = client.CreateOrUpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("creating/updating %q: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the creation/ update of %q: %+v", id, err) - } - d.SetId(id.ID()) return resourcePostgresqlFlexibleServerFirewallRuleRead(d, meta) } @@ -117,14 +110,14 @@ func resourcePostgresqlFlexibleServerFirewallRuleRead(d *pluginsdk.ResourceData, ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerFirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.FirewallRuleName) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] Postgresql Flexible Server Firewall Rule %q does not exist - removing from state", d.Id()) d.SetId("") return nil @@ -132,10 +125,11 @@ func resourcePostgresqlFlexibleServerFirewallRuleRead(d *pluginsdk.ResourceData, return fmt.Errorf("retrieving %q: %+v", id, err) } d.Set("name", id.FirewallRuleName) - d.Set("server_id", parse.NewFlexibleServerID(subscriptionId, id.ResourceGroup, id.FlexibleServerName).ID()) - if props := resp.FirewallRuleProperties; props != nil { - d.Set("end_ip_address", props.EndIPAddress) - d.Set("start_ip_address", props.StartIPAddress) + d.Set("server_id", firewallrules.NewFlexibleServerID(subscriptionId, id.ResourceGroupName, id.ServerName).ID()) + + if model := resp.Model; model != nil { + d.Set("end_ip_address", model.Properties.EndIPAddress) + d.Set("start_ip_address", model.Properties.StartIPAddress) } return nil } @@ -145,18 +139,14 @@ func resourcePostgresqlFlexibleServerFirewallRuleDelete(d *pluginsdk.ResourceDat ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerFirewallRuleID(d.Id()) + id, err := firewallrules.ParseFirewallRuleID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.FlexibleServerName, id.FirewallRuleName) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %q: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the delete of %q: %+v", id, err) - } return nil } diff --git a/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource_test.go b/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource_test.go index 270f7417fe78..80419a2e6aff 100644 --- a/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_firewall_rule_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/firewallrules" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -72,17 +72,17 @@ func TestAccPostgresqlFlexibleServerFirewallRule_update(t *testing.T) { } func (PostgresqlFlexibleServerFirewallRuleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.FlexibleServerFirewallRuleID(state.ID) + id, err := firewallrules.ParseFirewallRuleID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.FlexibleServerFirewallRuleClient.Get(ctx, id.ResourceGroup, id.FlexibleServerName, id.FirewallRuleName) + resp, err := clients.Postgres.FlexibleServerFirewallRuleClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving Postgresql Flexible Server Firewall Rule %q ( Flexible Server: %q / resource group: %q): %+v", id.FirewallRuleName, id.FlexibleServerName, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", id, err) } - return utils.Bool(resp.FirewallRuleProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgresqlFlexibleServerFirewallRuleResource) basic(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_flexible_server_resource.go b/internal/services/postgres/postgresql_flexible_server_resource.go index e705b417c27e..fe5dc76b6397 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_resource.go @@ -6,18 +6,18 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2021-06-01/postgresqlflexibleservers" - "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/serverrestart" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/servers" "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/privatezones" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -46,7 +46,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { }, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.FlexibleServerID(id) + _, err := servers.ParseFlexibleServerID(id) return err }), @@ -92,15 +92,11 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { }, "version": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - string(postgresqlflexibleservers.ServerVersionOneOne), - string(postgresqlflexibleservers.ServerVersionOneTwo), - string(postgresqlflexibleservers.ServerVersionOneThree), - }, false), + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForServerVersion(), false), }, "zone": commonschema.ZoneSingleOptional(), @@ -110,8 +106,8 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - string(postgresqlflexibleservers.CreateModeDefault), - string(postgresqlflexibleservers.CreateModePointInTimeRestore), + string(servers.CreateModeDefault), + string(servers.CreateModePointInTimeRestore), }, false), }, @@ -144,7 +140,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validate.FlexibleServerID, + ValidateFunc: servers.ValidateFlexibleServerID, }, "maintenance_window": { @@ -201,7 +197,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(postgresqlflexibleservers.HighAvailabilityModeZoneRedundant), + string(servers.HighAvailabilityModeZoneRedundant), }, false), }, @@ -220,7 +216,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, } } @@ -234,21 +230,21 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte name := d.Get("name").(string) resourceGroup := d.Get("resource_group_name").(string) - id := parse.NewFlexibleServerID(subscriptionId, resourceGroup, name) + id := servers.NewFlexibleServerID(subscriptionId, resourceGroup, name) - existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("checking for present of existing Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_flexible_server", id.ID()) } createMode := d.Get("create_mode").(string) - if postgresqlflexibleservers.CreateMode(createMode) == postgresqlflexibleservers.CreateModePointInTimeRestore { + if servers.CreateMode(createMode) == servers.CreateModePointInTimeRestore { if _, ok := d.GetOk("source_server_id"); !ok { return fmt.Errorf("`source_server_id` is required when `create_mode` is `PointInTimeRestore`") } @@ -257,7 +253,7 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte } } - if createMode == "" || postgresqlflexibleservers.CreateMode(createMode) == postgresqlflexibleservers.CreateModeDefault { + if createMode == "" || servers.CreateMode(createMode) == servers.CreateModeDefault { if _, ok := d.GetOk("administrator_login"); !ok { return fmt.Errorf("`administrator_login` is required when `create_mode` is `Default`") } @@ -277,15 +273,13 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte sku, err := expandFlexibleServerSku(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("expanding `sku_name` for PostgreSQL Flexible Server %s (Resource Group %q): %v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("expanding `sku_name` for %s: %v", id, err) } - parameters := postgresqlflexibleservers.Server{ - Location: utils.String(location.Normalize(d.Get("location").(string))), - ServerProperties: &postgresqlflexibleservers.ServerProperties{ - CreateMode: postgresqlflexibleservers.CreateMode(d.Get("create_mode").(string)), + parameters := servers.Server{ + Location: location.Normalize(d.Get("location").(string)), + Properties: &servers.ServerProperties{ Network: expandArmServerNetwork(d), - Version: postgresqlflexibleservers.ServerVersion(d.Get("version").(string)), Storage: expandArmServerStorage(d), HighAvailability: expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}), true), Backup: expandArmServerBackup(d), @@ -295,19 +289,29 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte } if v, ok := d.GetOk("administrator_login"); ok && v.(string) != "" { - parameters.ServerProperties.AdministratorLogin = utils.String(v.(string)) + parameters.Properties.AdministratorLogin = utils.String(v.(string)) } if v, ok := d.GetOk("administrator_password"); ok && v.(string) != "" { - parameters.ServerProperties.AdministratorLoginPassword = utils.String(v.(string)) + parameters.Properties.AdministratorLoginPassword = utils.String(v.(string)) + } + + if createMode != "" { + createModeAttr := servers.CreateMode(createMode) + parameters.Properties.CreateMode = &createModeAttr + } + + if v, ok := d.GetOk("version"); ok && v.(string) != "" { + version := servers.ServerVersion(v.(string)) + parameters.Properties.Version = &version } if v, ok := d.GetOk("zone"); ok && v.(string) != "" { - parameters.ServerProperties.AvailabilityZone = utils.String(v.(string)) + parameters.Properties.AvailabilityZone = utils.String(v.(string)) } if v, ok := d.GetOk("source_server_id"); ok && v.(string) != "" { - parameters.SourceServerResourceID = utils.String(v.(string)) + parameters.Properties.SourceServerResourceId = utils.String(v.(string)) } pointInTimeUTC := d.Get("point_in_time_restore_time_in_utc").(string) @@ -316,32 +320,22 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte if err != nil { return fmt.Errorf("unable to parse `point_in_time_restore_time_in_utc` value") } - parameters.ServerProperties.PointInTimeUTC = &date.Time{Time: v} - } - - future, err := client.Create(ctx, id.ResourceGroup, id.Name, parameters) - if err != nil { - return fmt.Errorf("creating Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + parameters.Properties.PointInTimeUTC = utils.String(v.Format(time.RFC3339)) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of the Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if err = client.CreateThenPoll(ctx, id, parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) } // `maintenance_window` could only be updated with, could not be created with if v, ok := d.GetOk("maintenance_window"); ok { - mwParams := postgresqlflexibleservers.ServerForUpdate{ - ServerPropertiesForUpdate: &postgresqlflexibleservers.ServerPropertiesForUpdate{ + mwParams := servers.ServerForUpdate{ + Properties: &servers.ServerPropertiesForUpdate{ MaintenanceWindow: expandArmServerMaintenanceWindow(v.([]interface{})), }, } - mwFuture, err := client.Update(ctx, id.ResourceGroup, id.Name, mwParams) - if err != nil { - return fmt.Errorf("updating Postgresql Flexible Server %q maintenance window (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) - } - - if err := mwFuture.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the update of the Postgresql Flexible Server %q maintenance window (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if err = client.UpdateThenPoll(ctx, id, mwParams); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) } } @@ -355,63 +349,78 @@ func resourcePostgresqlFlexibleServerRead(d *pluginsdk.ResourceData, meta interf ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerID(d.Id()) + id, err := servers.ParseFlexibleServerID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] Postgresql Flexibleserver %q does not exist - removing from state", d.Id()) d.SetId("") return nil } - return fmt.Errorf("retrieving Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) + d.Set("name", id.ServerName) + d.Set("resource_group_name", id.ResourceGroupName) - if props := resp.ServerProperties; props != nil { - d.Set("administrator_login", props.AdministratorLogin) - d.Set("zone", props.AvailabilityZone) - d.Set("version", props.Version) - d.Set("fqdn", props.FullyQualifiedDomainName) + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(&model.Location)) - if network := props.Network; network != nil { - d.Set("public_network_access_enabled", network.PublicNetworkAccess == postgresqlflexibleservers.ServerPublicNetworkAccessStateEnabled) - d.Set("delegated_subnet_id", network.DelegatedSubnetResourceID) - d.Set("private_dns_zone_id", network.PrivateDNSZoneArmResourceID) - } + if props := model.Properties; props != nil { + d.Set("administrator_login", props.AdministratorLogin) + d.Set("zone", props.AvailabilityZone) + d.Set("version", props.Version) + d.Set("fqdn", props.FullyQualifiedDomainName) - if err := d.Set("maintenance_window", flattenArmServerMaintenanceWindow(props.MaintenanceWindow)); err != nil { - return fmt.Errorf("setting `maintenance_window`: %+v", err) - } + if network := props.Network; network != nil { + publicNetworkAccess := false + if network.PublicNetworkAccess != nil { + publicNetworkAccess = *network.PublicNetworkAccess == servers.ServerPublicNetworkAccessStateEnabled + } + d.Set("public_network_access_enabled", publicNetworkAccess) + d.Set("delegated_subnet_id", network.DelegatedSubnetResourceId) + d.Set("private_dns_zone_id", network.PrivateDnsZoneArmResourceId) + } - if storage := props.Storage; storage != nil && storage.StorageSizeGB != nil { - d.Set("storage_mb", (*storage.StorageSizeGB * 1024)) - } + if err := d.Set("maintenance_window", flattenArmServerMaintenanceWindow(props.MaintenanceWindow)); err != nil { + return fmt.Errorf("setting `maintenance_window`: %+v", err) + } - if backup := props.Backup; backup != nil { - d.Set("backup_retention_days", backup.BackupRetentionDays) - d.Set("geo_redundant_backup_enabled", backup.GeoRedundantBackup == postgresqlflexibleservers.GeoRedundantBackupEnumEnabled) + if storage := props.Storage; storage != nil && storage.StorageSizeGB != nil { + d.Set("storage_mb", (*storage.StorageSizeGB * 1024)) + } + + if backup := props.Backup; backup != nil { + d.Set("backup_retention_days", backup.BackupRetentionDays) + + geoRedundantBackup := false + if backup.GeoRedundantBackup != nil { + geoRedundantBackup = *backup.GeoRedundantBackup == servers.GeoRedundantBackupEnumEnabled + } + d.Set("geo_redundant_backup_enabled", geoRedundantBackup) + } + + if err := d.Set("high_availability", flattenFlexibleServerHighAvailability(props.HighAvailability)); err != nil { + return fmt.Errorf("setting `high_availability`: %+v", err) + } } - if err := d.Set("high_availability", flattenFlexibleServerHighAvailability(props.HighAvailability)); err != nil { - return fmt.Errorf("setting `high_availability`: %+v", err) + sku, err := flattenFlexibleServerSku(model.Sku) + if err != nil { + return fmt.Errorf("flattening `sku_name` for %s: %v", id, err) } - } - sku, err := flattenFlexibleServerSku(resp.Sku) - if err != nil { - return fmt.Errorf("flattening `sku_name` for PostgreSQL Flexible Server %s (Resource Group %q): %v", id.Name, id.ResourceGroup, err) - } + d.Set("sku_name", sku) + + return tags.FlattenAndSet(d, model.Tags) - d.Set("sku_name", sku) + } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta interface{}) error { @@ -419,24 +428,24 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerID(d.Id()) + id, err := servers.ParseFlexibleServerID(d.Id()) if err != nil { return err } - parameters := postgresqlflexibleservers.ServerForUpdate{ - Location: utils.String(location.Normalize(d.Get("location").(string))), - ServerPropertiesForUpdate: &postgresqlflexibleservers.ServerPropertiesForUpdate{}, + parameters := servers.ServerForUpdate{ + Location: utils.String(location.Normalize(d.Get("location").(string))), + Properties: &servers.ServerPropertiesForUpdate{}, } var requireFailover bool // failover is only supported when `zone` and `high_availability.0.standby_availability_zone` are exchanged with each other if d.HasChanges("zone", "high_availability") { - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) - if err != nil { + resp, err := client.Get(ctx, *id) + if err != nil || resp.Model == nil { return err } - props := resp.ServerProperties + props := resp.Model.Properties if d.HasChange("zone") { @@ -460,9 +469,9 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte // changes can occur in high_availability.0.standby_availability_zone when zone has not changed in the case where a high_availability block has been newly added or a high_availability block is removed, meaning HA is now disabled } else if d.HasChange("high_availability.0.standby_availability_zone") { - if props != nil && props.HighAvailability != nil { + if props != nil && props.HighAvailability != nil && props.HighAvailability.Mode != nil { // if HA Mode is currently "ZoneRedundant" and is still set to "ZoneRedundant", high_availability.0.standby_availability_zone cannot be changed - if props.HighAvailability.Mode == postgresqlflexibleservers.HighAvailabilityModeZoneRedundant && !d.HasChange("high_availability.0.mode") { + if *props.HighAvailability.Mode == servers.HighAvailabilityModeZoneRedundant && !d.HasChange("high_availability.0.mode") { return fmt.Errorf("an existing `high_availability.0.standby_availability_zone` can only be changed when exchanged with the zone specified in `zone`") } // if high_availability.0.mode changes from "ZoneRedundant", an existing high_availability block has been removed as this is a required field @@ -472,25 +481,25 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte } if d.HasChange("administrator_password") { - parameters.ServerPropertiesForUpdate.AdministratorLoginPassword = utils.String(d.Get("administrator_password").(string)) + parameters.Properties.AdministratorLoginPassword = utils.String(d.Get("administrator_password").(string)) } if d.HasChange("storage_mb") { - parameters.ServerPropertiesForUpdate.Storage = expandArmServerStorage(d) + parameters.Properties.Storage = expandArmServerStorage(d) } if d.HasChange("backup_retention_days") { - parameters.ServerPropertiesForUpdate.Backup = expandArmServerBackup(d) + parameters.Properties.Backup = expandArmServerBackup(d) } if d.HasChange("maintenance_window") { - parameters.ServerPropertiesForUpdate.MaintenanceWindow = expandArmServerMaintenanceWindow(d.Get("maintenance_window").([]interface{})) + parameters.Properties.MaintenanceWindow = expandArmServerMaintenanceWindow(d.Get("maintenance_window").([]interface{})) } if d.HasChange("sku_name") { sku, err := expandFlexibleServerSku(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("expanding `sku_name` for PostgreSQL Flexible Server %s (Resource Group %q): %v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("expanding `sku_name` for %s: %v", id, err) } parameters.Sku = sku } @@ -500,32 +509,26 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte } if d.HasChange("high_availability") { - parameters.HighAvailability = expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}), false) + parameters.Properties.HighAvailability = expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}), false) } - future, err := client.Update(ctx, id.ResourceGroup, id.Name, parameters) - if err != nil { - return fmt.Errorf("updating Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) - } - - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the update of the Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if err = client.UpdateThenPoll(ctx, *id, parameters); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) } if requireFailover { - restartParameters := &postgresqlflexibleservers.RestartParameter{ + restartClient := meta.(*clients.Client).Postgres.ServerRestartClient + + restartServerId := serverrestart.NewFlexibleServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName) + failoverMode := serverrestart.FailoverModePlannedFailover + restartParameters := serverrestart.RestartParameter{ RestartWithFailover: utils.Bool(true), - FailoverMode: postgresqlflexibleservers.FailoverModePlannedFailover, + FailoverMode: &failoverMode, } - future, err := client.Restart(ctx, id.ResourceGroup, id.Name, restartParameters) - if err != nil { + if err = restartClient.ServersRestartThenPoll(ctx, restartServerId, restartParameters); err != nil { return fmt.Errorf("failing over %s: %+v", *id, err) } - - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for failover of %s: %+v", *id, err) - } } return resourcePostgresqlFlexibleServerRead(d, meta) @@ -536,139 +539,135 @@ func resourcePostgresqlFlexibleServerDelete(d *pluginsdk.ResourceData, meta inte ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.FlexibleServerID(d.Id()) + id, err := servers.ParseFlexibleServerID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.Name) - if err != nil { - return fmt.Errorf("deleting Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) - } - - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for the deletion of the Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + if err = client.DeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) } return nil } -func expandArmServerNetwork(d *pluginsdk.ResourceData) *postgresqlflexibleservers.Network { - network := postgresqlflexibleservers.Network{} +func expandArmServerNetwork(d *pluginsdk.ResourceData) *servers.Network { + network := servers.Network{} if v, ok := d.GetOk("delegated_subnet_id"); ok { - network.DelegatedSubnetResourceID = utils.String(v.(string)) + network.DelegatedSubnetResourceId = utils.String(v.(string)) } if v, ok := d.GetOk("private_dns_zone_id"); ok { - network.PrivateDNSZoneArmResourceID = utils.String(v.(string)) + network.PrivateDnsZoneArmResourceId = utils.String(v.(string)) } return &network } -func expandArmServerMaintenanceWindow(input []interface{}) *postgresqlflexibleservers.MaintenanceWindow { +func expandArmServerMaintenanceWindow(input []interface{}) *servers.MaintenanceWindow { if len(input) == 0 { - return &postgresqlflexibleservers.MaintenanceWindow{ + return &servers.MaintenanceWindow{ CustomWindow: utils.String(ServerMaintenanceWindowDisabled), } } v := input[0].(map[string]interface{}) - maintenanceWindow := postgresqlflexibleservers.MaintenanceWindow{ + maintenanceWindow := servers.MaintenanceWindow{ CustomWindow: utils.String(ServerMaintenanceWindowEnabled), - StartHour: utils.Int32(int32(v["start_hour"].(int))), - StartMinute: utils.Int32(int32(v["start_minute"].(int))), - DayOfWeek: utils.Int32(int32(v["day_of_week"].(int))), + StartHour: utils.Int64(int64(v["start_hour"].(int))), + StartMinute: utils.Int64(int64(v["start_minute"].(int))), + DayOfWeek: utils.Int64(int64(v["day_of_week"].(int))), } return &maintenanceWindow } -func expandArmServerStorage(d *pluginsdk.ResourceData) *postgresqlflexibleservers.Storage { - storage := postgresqlflexibleservers.Storage{} +func expandArmServerStorage(d *pluginsdk.ResourceData) *servers.Storage { + storage := servers.Storage{} if v, ok := d.GetOk("storage_mb"); ok { - storage.StorageSizeGB = utils.Int32(int32(v.(int) / 1024)) + storage.StorageSizeGB = utils.Int64(int64(v.(int) / 1024)) } return &storage } -func expandArmServerBackup(d *pluginsdk.ResourceData) *postgresqlflexibleservers.Backup { - backup := postgresqlflexibleservers.Backup{} +func expandArmServerBackup(d *pluginsdk.ResourceData) *servers.Backup { + backup := servers.Backup{} if v, ok := d.GetOk("backup_retention_days"); ok { - backup.BackupRetentionDays = utils.Int32(int32(v.(int))) + backup.BackupRetentionDays = utils.Int64(int64(v.(int))) } + geoRedundantEnabled := servers.GeoRedundantBackupEnumDisabled if geoRedundantBackupEnabled := d.Get("geo_redundant_backup_enabled").(bool); geoRedundantBackupEnabled { - backup.GeoRedundantBackup = postgresqlflexibleservers.GeoRedundantBackupEnumEnabled - } else { - backup.GeoRedundantBackup = postgresqlflexibleservers.GeoRedundantBackupEnumDisabled + geoRedundantEnabled = servers.GeoRedundantBackupEnumEnabled } + backup.GeoRedundantBackup = &geoRedundantEnabled + return &backup } -func expandFlexibleServerSku(name string) (*postgresqlflexibleservers.Sku, error) { +func expandFlexibleServerSku(name string) (*servers.Sku, error) { if name == "" { return nil, nil } parts := strings.SplitAfterN(name, "_", 2) - var tier postgresqlflexibleservers.SkuTier + var tier servers.SkuTier switch strings.TrimSuffix(parts[0], "_") { case "B": - tier = postgresqlflexibleservers.SkuTierBurstable + tier = servers.SkuTierBurstable case "GP": - tier = postgresqlflexibleservers.SkuTierGeneralPurpose + tier = servers.SkuTierGeneralPurpose case "MO": - tier = postgresqlflexibleservers.SkuTierMemoryOptimized + tier = servers.SkuTierMemoryOptimized default: return nil, fmt.Errorf("sku_name %s has unknown sku tier %s", name, parts[0]) } - return &postgresqlflexibleservers.Sku{ - Name: utils.String(parts[1]), + return &servers.Sku{ + Name: parts[1], Tier: tier, }, nil } -func flattenFlexibleServerSku(sku *postgresqlflexibleservers.Sku) (string, error) { - if sku == nil || sku.Name == nil || sku.Tier == "" { +func flattenFlexibleServerSku(sku *servers.Sku) (string, error) { + if sku == nil || sku.Tier == "" { return "", nil } var tier string switch sku.Tier { - case postgresqlflexibleservers.SkuTierBurstable: + case servers.SkuTierBurstable: tier = "B" - case postgresqlflexibleservers.SkuTierGeneralPurpose: + case servers.SkuTierGeneralPurpose: tier = "GP" - case postgresqlflexibleservers.SkuTierMemoryOptimized: + case servers.SkuTierMemoryOptimized: tier = "MO" default: return "", fmt.Errorf("sku_name has unknown sku tier %s", sku.Tier) } - return strings.Join([]string{tier, *sku.Name}, "_"), nil + return strings.Join([]string{tier, sku.Name}, "_"), nil } -func flattenArmServerMaintenanceWindow(input *postgresqlflexibleservers.MaintenanceWindow) []interface{} { - if input == nil || input.CustomWindow == nil || *input.CustomWindow == string(ServerMaintenanceWindowDisabled) { +func flattenArmServerMaintenanceWindow(input *servers.MaintenanceWindow) []interface{} { + if input == nil || input.CustomWindow == nil || *input.CustomWindow == ServerMaintenanceWindowDisabled { return make([]interface{}, 0) } - var dayOfWeek int32 + var dayOfWeek int64 if input.DayOfWeek != nil { dayOfWeek = *input.DayOfWeek } - var startHour int32 + var startHour int64 if input.StartHour != nil { startHour = *input.StartHour } - var startMinute int32 + var startMinute int64 if input.StartMinute != nil { startMinute = *input.StartMinute } @@ -681,17 +680,19 @@ func flattenArmServerMaintenanceWindow(input *postgresqlflexibleservers.Maintena } } -func expandFlexibleServerHighAvailability(inputs []interface{}, isCreate bool) *postgresqlflexibleservers.HighAvailability { +func expandFlexibleServerHighAvailability(inputs []interface{}, isCreate bool) *servers.HighAvailability { if len(inputs) == 0 || inputs[0] == nil { - return &postgresqlflexibleservers.HighAvailability{ - Mode: postgresqlflexibleservers.HighAvailabilityModeDisabled, + highAvailability := servers.HighAvailabilityModeDisabled + return &servers.HighAvailability{ + Mode: &highAvailability, } } input := inputs[0].(map[string]interface{}) - result := postgresqlflexibleservers.HighAvailability{ - Mode: postgresqlflexibleservers.HighAvailabilityMode(input["mode"].(string)), + mode := servers.HighAvailabilityMode(input["mode"].(string)) + result := servers.HighAvailability{ + Mode: &mode, } // service team confirmed it doesn't support to update `high_availability.0.standby_availability_zone` after the PostgreSQL Flexible Server resource is created @@ -704,8 +705,8 @@ func expandFlexibleServerHighAvailability(inputs []interface{}, isCreate bool) * return &result } -func flattenFlexibleServerHighAvailability(ha *postgresqlflexibleservers.HighAvailability) []interface{} { - if ha == nil || ha.Mode == postgresqlflexibleservers.HighAvailabilityModeDisabled { +func flattenFlexibleServerHighAvailability(ha *servers.HighAvailability) []interface{} { + if ha == nil || ha.Mode == nil || *ha.Mode == servers.HighAvailabilityModeDisabled { return []interface{}{} } @@ -716,7 +717,7 @@ func flattenFlexibleServerHighAvailability(ha *postgresqlflexibleservers.HighAva return []interface{}{ map[string]interface{}{ - "mode": string(ha.Mode), + "mode": string(*ha.Mode), "standby_availability_zone": zone, }, } diff --git a/internal/services/postgres/postgresql_flexible_server_resource_test.go b/internal/services/postgres/postgresql_flexible_server_resource_test.go index 9e874fff3c70..d5415ce1cb3e 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2021-06-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -241,17 +241,17 @@ func TestAccPostgresqlFlexibleServer_geoRedundantBackupEnabled(t *testing.T) { } func (PostgresqlFlexibleServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.FlexibleServerID(state.ID) + id, err := servers.ParseFlexibleServerID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.FlexibleServersClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.Postgres.FlexibleServersClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("retrieving Postgresql Flexible Server %q (resource group: %q): %+v", id.Name, id.ResourceGroup, err) + return nil, fmt.Errorf("retrieving %s: %+v", id, err) } - return utils.Bool(resp.ServerProperties != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgresqlFlexibleServerResource) template(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_server_data_source.go b/internal/services/postgres/postgresql_server_data_source.go index 684e80077672..a4787299d113 100644 --- a/internal/services/postgres/postgresql_server_data_source.go +++ b/internal/services/postgres/postgresql_server_data_source.go @@ -4,14 +4,15 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func dataSourcePostgreSqlServer() *pluginsdk.Resource { @@ -57,7 +58,7 @@ func dataSourcePostgreSqlServer() *pluginsdk.Resource { "identity": commonschema.SystemAssignedIdentityComputed(), - "tags": tags.SchemaDataSource(), + "tags": commonschema.TagsDataSource(), }, } } @@ -68,10 +69,10 @@ func dataSourcePostgreSqlServerRead(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + id := servers.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("%s was not found", id) } @@ -79,23 +80,26 @@ func dataSourcePostgreSqlServerRead(d *pluginsdk.ResourceData, meta interface{}) } d.SetId(id.ID()) - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } - if err := d.Set("identity", flattenServerIdentity(resp.Identity)); err != nil { - return fmt.Errorf("setting `identity`: %+v", err) - } + if model := resp.Model; model != nil { + d.Set("location", azure.NormalizeLocation(model.Location)) - if props := resp.ServerProperties; props != nil { - d.Set("fqdn", props.FullyQualifiedDomainName) - d.Set("version", props.Version) - d.Set("administrator_login", props.AdministratorLogin) - } + if err := d.Set("identity", identity.FlattenSystemAssigned(model.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if props := model.Properties; props != nil { + d.Set("fqdn", props.FullyQualifiedDomainName) + d.Set("version", props.Version) + d.Set("administrator_login", props.AdministratorLogin) + } + + if sku := model.Sku; sku != nil { + d.Set("sku_name", sku.Name) + } - if sku := resp.Sku; sku != nil { - d.Set("sku_name", sku.Name) + return tags.FlattenAndSet(d, model.Tags) } - return tags.FlattenAndSet(d, resp.Tags) + return nil } diff --git a/internal/services/postgres/postgresql_server_key_resource.go b/internal/services/postgres/postgresql_server_key_resource.go index c44038a3d35e..1d54ce9cd313 100644 --- a/internal/services/postgres/postgresql_server_key_resource.go +++ b/internal/services/postgres/postgresql_server_key_resource.go @@ -6,15 +6,14 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2020-01-01/serverkeys" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/client" keyVaultParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" resourcesClient "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/client" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -29,7 +28,7 @@ func resourcePostgreSQLServerKey() *pluginsdk.Resource { Delete: resourcePostgreSQLServerKeyDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.ServerKeyID(id) + _, err := serverkeys.ParseKeyID(id) return err }), @@ -45,7 +44,7 @@ func resourcePostgreSQLServerKey() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.ServerID, + ValidateFunc: serverkeys.ValidateServerID, }, "key_vault_key_id": { @@ -84,31 +83,31 @@ func resourcePostgreSQLServerKeyCreateUpdate(d *pluginsdk.ResourceData, meta int ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) defer cancel() - serverId, err := parse.ServerID(d.Get("server_id").(string)) + serverId, err := serverkeys.ParseServerID(d.Get("server_id").(string)) if err != nil { return err } keyVaultKeyURI := d.Get("key_vault_key_id").(string) name, err := getPostgreSQLServerKeyName(ctx, keyVaultsClient, resourcesClient, keyVaultKeyURI) if err != nil { - return fmt.Errorf("cannot compose name for PostgreSQL Server Key (Resource Group %q / Server %q): %+v", serverId.ResourceGroup, serverId.Name, err) + return fmt.Errorf("cannot compose name for %s: %+v", serverId, err) } - locks.ByName(serverId.Name, postgreSQLServerResourceName) - defer locks.UnlockByName(serverId.Name, postgreSQLServerResourceName) + locks.ByName(serverId.ServerName, postgreSQLServerResourceName) + defer locks.UnlockByName(serverId.ServerName, postgreSQLServerResourceName) if d.IsNewResource() { // This resource is a singleton, but its name can be anything. // If you create a new key with different name with the old key, the service will not give you any warning but directly replace the old key with the new key. // Therefore sometimes you cannot get the old key using the GET API since you may not know the name of the old key - resp, err := keysClient.List(ctx, serverId.ResourceGroup, serverId.Name) + resp, err := keysClient.List(ctx, *serverId) if err != nil { - return fmt.Errorf("listing existing PostgreSQL Server Keys in Resource Group %q / Server %q: %+v", serverId.ResourceGroup, serverId.Name, err) + return fmt.Errorf("listing existing Keys in %s: %+v", serverId, err) } - keys := resp.Values() - if len(keys) >= 1 { - if rawId := keys[0].ID; rawId != nil && *rawId != "" { - id, err := parse.ServerKeyID(*rawId) + if resp.Model != nil && len(*resp.Model) >= 1 { + keys := *resp.Model + if rawId := keys[0].Id; rawId != nil && *rawId != "" { + id, err := serverkeys.ParseKeyID(*rawId) if err != nil { return fmt.Errorf("parsing existing Server Key ID %q: %+v", *rawId, err) } @@ -118,21 +117,17 @@ func resourcePostgreSQLServerKeyCreateUpdate(d *pluginsdk.ResourceData, meta int } } - param := postgresql.ServerKey{ - ServerKeyProperties: &postgresql.ServerKeyProperties{ - ServerKeyType: utils.String("AzureKeyVault"), - URI: utils.String(d.Get("key_vault_key_id").(string)), + param := serverkeys.ServerKey{ + Properties: &serverkeys.ServerKeyProperties{ + ServerKeyType: serverkeys.ServerKeyTypeAzureKeyVault, + Uri: utils.String(d.Get("key_vault_key_id").(string)), }, } - id := parse.NewServerKeyID(serverId.SubscriptionId, serverId.ResourceGroup, serverId.Name, *name) - future, err := keysClient.CreateOrUpdate(ctx, id.ServerName, id.KeyName, param, id.ResourceGroup) - if err != nil { + id := serverkeys.NewKeyID(serverId.SubscriptionId, serverId.ResourceGroupName, serverId.ServerName, *name) + if err = keysClient.CreateOrUpdateThenPoll(ctx, id, param); err != nil { return fmt.Errorf("creating/updating %s: %+v", id, err) } - if err := future.WaitForCompletionRef(ctx, keysClient.Client); err != nil { - return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) - } d.SetId(id.ID()) return resourcePostgreSQLServerKeyRead(d, meta) @@ -143,14 +138,14 @@ func resourcePostgreSQLServerKeyRead(d *pluginsdk.ResourceData, meta interface{} ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerKeyID(d.Id()) + id, err := serverkeys.ParseKeyID(d.Id()) if err != nil { return err } - resp, err := keysClient.Get(ctx, id.ResourceGroup, id.ServerName, id.KeyName) + resp, err := keysClient.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -159,9 +154,9 @@ func resourcePostgreSQLServerKeyRead(d *pluginsdk.ResourceData, meta interface{} return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("server_id", parse.NewServerID(id.SubscriptionId, id.ResourceGroup, id.ServerName).ID()) - if props := resp.ServerKeyProperties; props != nil { - d.Set("key_vault_key_id", props.URI) + d.Set("server_id", serverkeys.NewServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName).ID()) + if resp.Model != nil && resp.Model.Properties != nil { + d.Set("key_vault_key_id", resp.Model.Properties.Uri) } return nil @@ -172,7 +167,7 @@ func resourcePostgreSQLServerKeyDelete(d *pluginsdk.ResourceData, meta interface ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerKeyID(d.Id()) + id, err := serverkeys.ParseKeyID(d.Id()) if err != nil { return err } @@ -180,13 +175,9 @@ func resourcePostgreSQLServerKeyDelete(d *pluginsdk.ResourceData, meta interface locks.ByName(id.ServerName, postgreSQLServerResourceName) defer locks.UnlockByName(id.ServerName, postgreSQLServerResourceName) - future, err := client.Delete(ctx, id.ServerName, id.KeyName, id.ResourceGroup) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } return nil } diff --git a/internal/services/postgres/postgresql_server_key_resource_test.go b/internal/services/postgres/postgresql_server_key_resource_test.go index 5e2a2f1f563e..ae8ec247aa10 100644 --- a/internal/services/postgres/postgresql_server_key_resource_test.go +++ b/internal/services/postgres/postgresql_server_key_resource_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2020-01-01/serverkeys" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -68,17 +68,17 @@ func TestAccPostgreSQLServerKey_requiresImport(t *testing.T) { } func (t PostgreSQLServerKeyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ServerKeyID(state.ID) + id, err := serverkeys.ParseKeyID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.ServerKeysClient.Get(ctx, id.ResourceGroup, id.ServerName, id.KeyName) + resp, err := clients.Postgres.ServerKeysClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading Postgresql Server Key (%s): %+v", id.String(), err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgreSQLServerKeyResource) template(data acceptance.TestData) string { diff --git a/internal/services/postgres/postgresql_server_resource.go b/internal/services/postgres/postgresql_server_resource.go index 7df6d83f677e..d65cb06779f6 100644 --- a/internal/services/postgres/postgresql_server_resource.go +++ b/internal/services/postgres/postgresql_server_resource.go @@ -8,18 +8,21 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" - "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/migration" + + "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/replicas" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/serversecurityalertpolicies" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -61,30 +64,34 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { Delete: resourcePostgreSQLServerDelete, Importer: pluginsdk.ImporterValidatingResourceIdThen(func(id string) error { - _, err := parse.ServerID(id) + _, err := servers.ParseServerID(id) return err }, func(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) ([]*pluginsdk.ResourceData, error) { client := meta.(*clients.Client).Postgres.ServersClient - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return []*pluginsdk.ResourceData{d}, err } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - return []*pluginsdk.ResourceData{d}, fmt.Errorf("reading PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return []*pluginsdk.ResourceData{d}, fmt.Errorf("reading %s: %+v", id, err) } d.Set("create_mode", "Default") - if resp.ReplicationRole != nil && *resp.ReplicationRole != "Master" && *resp.ReplicationRole != "None" { - d.Set("create_mode", resp.ReplicationRole) - - sourceServerId, err := parse.ServerID(*resp.MasterServerID) - if err != nil { - return []*pluginsdk.ResourceData{d}, fmt.Errorf("parsing Postgres Main Server ID : %v", err) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + if props.ReplicationRole != nil && *props.ReplicationRole != "Master" && *props.ReplicationRole != "None" { + d.Set("create_mode", props.ReplicationRole) + + sourceServerId, err := servers.ParseServerID(*props.MasterServerId) + if err != nil { + return []*pluginsdk.ResourceData{d}, fmt.Errorf("parsing Postgres Main Server ID : %v", err) + } + d.Set("creation_source_server_id", sourceServerId.ID()) + } } - d.Set("creation_source_server_id", sourceServerId.ID()) } return []*pluginsdk.ResourceData{d}, nil @@ -97,6 +104,11 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(60 * time.Minute), }, + SchemaVersion: 1, + StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{ + 0: migration.PostgresqlServerV0ToV1{}, + }), + Schema: map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, @@ -116,16 +128,10 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { }, "version": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - string(postgresql.NineFullStopFive), - string(postgresql.NineFullStopSix), - string(postgresql.OneOne), - string(postgresql.OneZero), - string(postgresql.OneZeroFullStopZero), - }, false), + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForServerVersion(), false), }, "administrator_login": { @@ -163,21 +169,16 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { }, "create_mode": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(postgresql.CreateModeDefault), - ValidateFunc: validation.StringInSlice([]string{ - string(postgresql.CreateModeDefault), - string(postgresql.CreateModeGeoRestore), - string(postgresql.CreateModePointInTimeRestore), - string(postgresql.CreateModeReplica), - }, false), + Type: pluginsdk.TypeString, + Optional: true, + Default: string(servers.CreateModeDefault), + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForCreateMode(), false), }, "creation_source_server_id": { Type: pluginsdk.TypeString, Optional: true, - ValidateFunc: validate.ServerID, + ValidateFunc: servers.ValidateServerID, }, "identity": commonschema.SystemAssignedIdentityOptional(), @@ -211,15 +212,10 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { }, "ssl_minimal_tls_version_enforced": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(postgresql.TLS12), - ValidateFunc: validation.StringInSlice([]string{ - string(postgresql.TLSEnforcementDisabled), - string(postgresql.TLS10), - string(postgresql.TLS11), - string(postgresql.TLS12), - }, false), + Type: pluginsdk.TypeString, + Optional: true, + Default: string(servers.MinimalTlsVersionEnumTLSOneTwo), + ValidateFunc: validation.StringInSlice(servers.PossibleValuesForMinimalTlsVersionEnum(), false), }, "ssl_enforcement_enabled": { @@ -331,7 +327,7 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { Computed: true, }, - "tags": tags.Schema(), + "tags": commonschema.Tags(), }, CustomizeDiff: pluginsdk.CustomDiffWithAll( @@ -349,10 +345,10 @@ func resourcePostgreSQLServer() *pluginsdk.Resource { return false }), pluginsdk.ForceNewIfChange("create_mode", func(ctx context.Context, old, new, meta interface{}) bool { - oldMode := postgresql.CreateMode(old.(string)) - newMode := postgresql.CreateMode(new.(string)) + oldMode := servers.CreateMode(old.(string)) + newMode := servers.CreateMode(new.(string)) // Instance could not be changed from Default to Replica - if oldMode == postgresql.CreateModeDefault && newMode == postgresql.CreateModeReplica { + if oldMode == servers.CreateModeDefault && newMode == servers.CreateModeReplica { return true } return false @@ -370,52 +366,51 @@ func resourcePostgreSQLServerCreate(d *pluginsdk.ResourceData, meta interface{}) log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Server creation.") - id := parse.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + id := servers.NewServerID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_server", id.ID()) } - mode := postgresql.CreateMode(d.Get("create_mode").(string)) + mode := servers.CreateMode(d.Get("create_mode").(string)) source := d.Get("creation_source_server_id").(string) - version := postgresql.ServerVersion(d.Get("version").(string)) + version := servers.ServerVersion(d.Get("version").(string)) sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { return fmt.Errorf("expanding `sku_name`: %+v", err) } - infraEncrypt := postgresql.InfrastructureEncryptionEnabled + infraEncrypt := servers.InfrastructureEncryptionEnabled if v := d.Get("infrastructure_encryption_enabled"); !v.(bool) { - infraEncrypt = postgresql.InfrastructureEncryptionDisabled + infraEncrypt = servers.InfrastructureEncryptionDisabled } - publicAccess := postgresql.PublicNetworkAccessEnumEnabled + publicAccess := servers.PublicNetworkAccessEnumEnabled if v := d.Get("public_network_access_enabled"); !v.(bool) { - publicAccess = postgresql.PublicNetworkAccessEnumDisabled + publicAccess = servers.PublicNetworkAccessEnumDisabled } - ssl := postgresql.SslEnforcementEnumEnabled + ssl := servers.SslEnforcementEnumEnabled if v := d.Get("ssl_enforcement_enabled"); !v.(bool) { - ssl = postgresql.SslEnforcementEnumDisabled + ssl = servers.SslEnforcementEnumDisabled } - tlsMin := postgresql.MinimalTLSVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) - if ssl == postgresql.SslEnforcementEnumDisabled && tlsMin != postgresql.TLSEnforcementDisabled { + tlsMin := servers.MinimalTlsVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) + if ssl == servers.SslEnforcementEnumDisabled && tlsMin != servers.MinimalTlsVersionEnumTLSEnforcementDisabled { return fmt.Errorf("`ssl_minimal_tls_version_enforced` must be set to `TLSEnforcementDisabled` if `ssl_enforcement_enabled` is set to `false`") } storage := expandPostgreSQLStorageProfile(d) - - var props postgresql.BasicServerPropertiesForCreate + var props servers.ServerPropertiesForCreate switch mode { - case postgresql.CreateModeDefault: + case servers.CreateModeDefault: admin := d.Get("administrator_login").(string) pass := d.Get("administrator_login_password").(string) if admin == "" { @@ -430,89 +425,77 @@ func resourcePostgreSQLServerCreate(d *pluginsdk.ResourceData, meta interface{}) } // check admin - props = &postgresql.ServerPropertiesForDefaultCreate{ - AdministratorLogin: &admin, - AdministratorLoginPassword: &pass, - CreateMode: mode, - InfrastructureEncryption: infraEncrypt, - PublicNetworkAccess: publicAccess, - MinimalTLSVersion: tlsMin, - SslEnforcement: ssl, + props = servers.ServerPropertiesForDefaultCreate{ + AdministratorLogin: admin, + AdministratorLoginPassword: pass, + InfrastructureEncryption: &infraEncrypt, + PublicNetworkAccess: &publicAccess, + MinimalTlsVersion: &tlsMin, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, + Version: &version, } - case postgresql.CreateModePointInTimeRestore: + case servers.CreateModePointInTimeRestore: v, ok := d.GetOk("restore_point_in_time") if !ok || v.(string) == "" { return fmt.Errorf("restore_point_in_time must be set when create_mode is PointInTimeRestore") } - time, _ := time.Parse(time.RFC3339, v.(string)) // should be validated by the schema // d.GetOk cannot identify whether user sets the property that is bool type and has default value. So it has to identify it using `d.GetRawConfig()` if v := d.GetRawConfig().AsValueMap()["public_network_access_enabled"]; !v.IsNull() { return fmt.Errorf("`public_network_access_enabled` doesn't support PointInTimeRestore mode") } - props = &postgresql.ServerPropertiesForRestore{ - CreateMode: mode, - SourceServerID: &source, - RestorePointInTime: &date.Time{ - Time: time, - }, - InfrastructureEncryption: infraEncrypt, - MinimalTLSVersion: tlsMin, - SslEnforcement: ssl, + props = &servers.ServerPropertiesForRestore{ + SourceServerId: source, + RestorePointInTime: v.(string), + InfrastructureEncryption: &infraEncrypt, + MinimalTlsVersion: &tlsMin, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, - } - case postgresql.CreateModeGeoRestore: - props = &postgresql.ServerPropertiesForGeoRestore{ - CreateMode: mode, - SourceServerID: &source, - InfrastructureEncryption: infraEncrypt, - PublicNetworkAccess: publicAccess, - MinimalTLSVersion: tlsMin, - SslEnforcement: ssl, + Version: &version, + } + case servers.CreateModeGeoRestore: + props = &servers.ServerPropertiesForGeoRestore{ + SourceServerId: source, + InfrastructureEncryption: &infraEncrypt, + PublicNetworkAccess: &publicAccess, + MinimalTlsVersion: &tlsMin, + SslEnforcement: &ssl, StorageProfile: storage, - Version: version, + Version: &version, } - case postgresql.CreateModeReplica: - props = &postgresql.ServerPropertiesForReplica{ - CreateMode: mode, - SourceServerID: &source, - InfrastructureEncryption: infraEncrypt, - PublicNetworkAccess: publicAccess, - MinimalTLSVersion: tlsMin, - SslEnforcement: ssl, - Version: version, + case servers.CreateModeReplica: + props = &servers.ServerPropertiesForReplica{ + SourceServerId: source, + InfrastructureEncryption: &infraEncrypt, + PublicNetworkAccess: &publicAccess, + MinimalTlsVersion: &tlsMin, + SslEnforcement: &ssl, + Version: &version, } } - expandedIdentity, err := expandServerIdentity(d.Get("identity").([]interface{})) + expandedIdentity, err := identity.ExpandSystemAssigned(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) } - server := postgresql.ServerForCreate{ + server := servers.ServerForCreate{ Identity: expandedIdentity, - Location: utils.String(location.Normalize(d.Get("location").(string))), + Location: location.Normalize(d.Get("location").(string)), Properties: props, Sku: sku, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.Create(ctx, id.ResourceGroup, id.Name, server) - if err != nil { + if err = client.CreateThenPoll(ctx, id, server); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of %s: %+v", id, err) - } - log.Printf("[DEBUG] Waiting for %s to become available", id) stateConf := &pluginsdk.StateChangeConf{ - Pending: []string{string(postgresql.ServerStateInaccessible)}, - Target: []string{string(postgresql.ServerStateReady)}, + Pending: []string{string(servers.ServerStateInaccessible)}, + Target: []string{string(servers.ServerStateReady)}, Refresh: postgreSqlStateRefreshFunc(ctx, client, id), MinTimeout: 15 * time.Second, Timeout: d.Timeout(pluginsdk.TimeoutCreate), @@ -525,36 +508,28 @@ func resourcePostgreSQLServerCreate(d *pluginsdk.ResourceData, meta interface{}) d.SetId(id.ID()) if v, ok := d.GetOk("threat_detection_policy"); ok { + securityAlertId := serversecurityalertpolicies.NewServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName) alert := expandSecurityAlertPolicy(v) if alert != nil { - future, err := securityClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *alert) - if err != nil { + if err = securityClient.CreateOrUpdateThenPoll(ctx, securityAlertId, *alert); err != nil { return fmt.Errorf("updataing security alert policy for %s: %v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of security alert policy for %s: %+v", id, err) - } } } // Issue tracking the REST API update failure: https://github.com/Azure/azure-rest-api-specs/issues/14117 - if mode == postgresql.CreateModeReplica { + if mode == servers.CreateModeReplica { log.Printf("[INFO] updating `public_network_access_enabled` for %s", id) - properties := postgresql.ServerUpdateParameters{ - ServerUpdateParametersProperties: &postgresql.ServerUpdateParametersProperties{ - PublicNetworkAccess: publicAccess, + properties := servers.ServerUpdateParameters{ + Properties: &servers.ServerUpdateParametersProperties{ + PublicNetworkAccess: &publicAccess, }, } - future, err := client.Update(ctx, id.ResourceGroup, id.Name, properties) - if err != nil { + if err = client.UpdateThenPoll(ctx, id, properties); err != nil { return fmt.Errorf("updating Public Network Access for Replica %q: %+v", id, err) } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of Public Network Access for Replica %q: %+v", id, err) - } } return resourcePostgreSQLServerRead(d, meta) @@ -569,22 +544,22 @@ func resourcePostgreSQLServerUpdate(d *pluginsdk.ResourceData, meta interface{}) // TODO: support for Delta updates - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return fmt.Errorf("parsing Postgres Server ID : %v", err) } // Locks for upscaling of replicas - mode := postgresql.CreateMode(d.Get("create_mode").(string)) + mode := servers.CreateMode(d.Get("create_mode").(string)) primaryID := id.String() - if mode == postgresql.CreateModeReplica { + if mode == servers.CreateModeReplica { primaryID = d.Get("creation_source_server_id").(string) // Wait for possible restarts triggered by scaling primary (and its replicas) log.Printf("[DEBUG] Waiting for %s to become available", *id) stateConf := &pluginsdk.StateChangeConf{ - Pending: []string{string(postgresql.ServerStateInaccessible), "Restarting"}, - Target: []string{string(postgresql.ServerStateReady)}, + Pending: []string{string(servers.ServerStateInaccessible), "Restarting"}, + Target: []string{string(servers.ServerStateReady)}, Refresh: postgreSqlStateRefreshFunc(ctx, client, *id), MinTimeout: 15 * time.Second, Timeout: d.Timeout(pluginsdk.TimeoutCreate), @@ -602,121 +577,110 @@ func resourcePostgreSQLServerUpdate(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("expanding `sku_name`: %v", err) } - if d.HasChange("sku_name") && mode != postgresql.CreateModeReplica { + if d.HasChange("sku_name") && mode != servers.CreateModeReplica { oldRaw, newRaw := d.GetChange("sku_name") old := oldRaw.(string) new := newRaw.(string) if indexOfSku(old) < indexOfSku(new) { - listReplicas, err := replicasClient.ListByServer(ctx, id.ResourceGroup, id.Name) + replicasId := replicas.NewServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName) + listReplicas, err := replicasClient.ListByServer(ctx, replicasId) if err != nil { return fmt.Errorf("listing replicas for %s: %+v", *id, err) } - propertiesReplica := postgresql.ServerUpdateParameters{ + propertiesReplica := servers.ServerUpdateParameters{ Sku: sku, } - for _, replica := range *listReplicas.Value { - replicaId, err := parse.ServerID(*replica.ID) - if err != nil { - return fmt.Errorf("parsing Postgres Server Replica ID : %v", err) - } - future, err := client.Update(ctx, replicaId.ResourceGroup, replicaId.Name, propertiesReplica) - if err != nil { - return fmt.Errorf("updating SKU for Replica %s: %+v", *replicaId, err) - } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for SKU update for Replica %s: %+v", *replicaId, err) + if listReplicas.Model != nil && listReplicas.Model.Value != nil { + replicaList := *listReplicas.Model.Value + for _, replica := range replicaList { + replicaId, err := servers.ParseServerID(*replica.Id) + if err != nil { + return fmt.Errorf("parsing Postgres Server Replica ID : %v", err) + } + if err = client.UpdateThenPoll(ctx, *replicaId, propertiesReplica); err != nil { + return fmt.Errorf("updating SKU for Replica %s: %+v", *replicaId, err) + } } } } } - ssl := postgresql.SslEnforcementEnumEnabled + ssl := servers.SslEnforcementEnumEnabled if v := d.Get("ssl_enforcement_enabled"); !v.(bool) { - ssl = postgresql.SslEnforcementEnumDisabled + ssl = servers.SslEnforcementEnumDisabled } - tlsMin := postgresql.MinimalTLSVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) + tlsMin := servers.MinimalTlsVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) - if ssl == postgresql.SslEnforcementEnumDisabled && tlsMin != postgresql.TLSEnforcementDisabled { + if ssl == servers.SslEnforcementEnumDisabled && tlsMin != servers.MinimalTlsVersionEnumTLSEnforcementDisabled { return fmt.Errorf("`ssl_minimal_tls_version_enforced` must be set to `TLSEnforcementDisabled` if `ssl_enforcement_enabled` is set to `false`") } - expandedIdentity, err := expandServerIdentity(d.Get("identity").([]interface{})) + expandedIdentity, err := identity.ExpandSystemAssigned(d.Get("identity").([]interface{})) if err != nil { return fmt.Errorf("expanding `identity`: %+v", err) } - properties := postgresql.ServerUpdateParameters{ + serverVersion := servers.ServerVersion(d.Get("version").(string)) + properties := servers.ServerUpdateParameters{ Identity: expandedIdentity, - ServerUpdateParametersProperties: &postgresql.ServerUpdateParametersProperties{ - SslEnforcement: ssl, - MinimalTLSVersion: tlsMin, + Properties: &servers.ServerUpdateParametersProperties{ + SslEnforcement: &ssl, + MinimalTlsVersion: &tlsMin, StorageProfile: expandPostgreSQLStorageProfile(d), - Version: postgresql.ServerVersion(d.Get("version").(string)), + Version: &serverVersion, }, Sku: sku, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - if mode == postgresql.CreateModePointInTimeRestore { + if mode == servers.CreateModePointInTimeRestore { // d.GetOk cannot identify whether user sets the property that is bool type and has default value. So it has to identify it using `d.GetRawConfig()` if v := d.GetRawConfig().AsValueMap()["public_network_access_enabled"]; !v.IsNull() { return fmt.Errorf("`public_network_access_enabled` doesn't support PointInTimeRestore mode") } } else { - publicAccess := postgresql.PublicNetworkAccessEnumEnabled + publicAccess := servers.PublicNetworkAccessEnumEnabled if v := d.Get("public_network_access_enabled"); !v.(bool) { - publicAccess = postgresql.PublicNetworkAccessEnumDisabled + publicAccess = servers.PublicNetworkAccessEnumDisabled } - properties.ServerUpdateParametersProperties.PublicNetworkAccess = publicAccess + properties.Properties.PublicNetworkAccess = &publicAccess } oldCreateMode, newCreateMode := d.GetChange("create_mode") - replicaUpdatedToDefault := postgresql.CreateMode(oldCreateMode.(string)) == postgresql.CreateModeReplica && postgresql.CreateMode(newCreateMode.(string)) == postgresql.CreateModeDefault + replicaUpdatedToDefault := servers.CreateMode(oldCreateMode.(string)) == servers.CreateModeReplica && servers.CreateMode(newCreateMode.(string)) == servers.CreateModeDefault if replicaUpdatedToDefault { - properties.ServerUpdateParametersProperties.ReplicationRole = utils.String("None") + properties.Properties.ReplicationRole = utils.String("None") } // Update Admin Password in the separate call when Replication is stopped: https://github.com/Azure/azure-rest-api-specs/issues/16898 if d.HasChange("administrator_login_password") && !replicaUpdatedToDefault { - properties.ServerUpdateParametersProperties.AdministratorLoginPassword = utils.String(d.Get("administrator_login_password").(string)) + properties.Properties.AdministratorLoginPassword = utils.String(d.Get("administrator_login_password").(string)) } - future, err := client.Update(ctx, id.ResourceGroup, id.Name, properties) - if err != nil { + if err = client.UpdateThenPoll(ctx, *id, properties); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of %s: %+v", *id, err) - } // Update Admin Password in a separate call when Replication is stopped: https://github.com/Azure/azure-rest-api-specs/issues/16898 if d.HasChange("administrator_login_password") && replicaUpdatedToDefault { - properties.ServerUpdateParametersProperties.AdministratorLoginPassword = utils.String(d.Get("administrator_login_password").(string)) + properties.Properties.AdministratorLoginPassword = utils.String(d.Get("administrator_login_password").(string)) - future, err := client.Update(ctx, id.ResourceGroup, id.Name, properties) - if err != nil { + if err = client.UpdateThenPoll(ctx, *id, properties); err != nil { return fmt.Errorf("updating Admin Password of %q: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for Admin Password update of %q: %+v", id, err) - } } if v, ok := d.GetOk("threat_detection_policy"); ok { alert := expandSecurityAlertPolicy(v) + securityId := serversecurityalertpolicies.NewServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName) if alert != nil { - future, err := securityClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *alert) - if err != nil { + if err = securityClient.CreateOrUpdateThenPoll(ctx, securityId, *alert); err != nil { return fmt.Errorf("updating security alert policy for %s: %+v", *id, err) } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for update of security alert policy for %s: %+v", *id, err) - } } } @@ -729,14 +693,14 @@ func resourcePostgreSQLServerRead(d *pluginsdk.ResourceData, meta interface{}) e ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return fmt.Errorf("parsing Postgres Server ID : %v", err) } - resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[WARN] %s was not found - removing from state", *id) d.SetId("") return nil @@ -745,56 +709,95 @@ func resourcePostgreSQLServerRead(d *pluginsdk.ResourceData, meta interface{}) e return fmt.Errorf("retrieving %s: %+v", *id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", location.NormalizeNilable(resp.Location)) + d.Set("name", id.ServerName) + d.Set("resource_group_name", id.ResourceGroupName) - tier := postgresql.Basic - if sku := resp.Sku; sku != nil { - d.Set("sku_name", sku.Name) - tier = sku.Tier - } + if model := resp.Model; model != nil { + d.Set("location", location.NormalizeNilable(&model.Location)) - if err := d.Set("identity", flattenServerIdentity(resp.Identity)); err != nil { - return fmt.Errorf("setting `identity`: %+v", err) - } + tier := servers.SkuTierBasic + if sku := model.Sku; sku != nil { + d.Set("sku_name", sku.Name) + if sku.Tier != nil { + tier = *sku.Tier + } + } - if props := resp.ServerProperties; props != nil { - d.Set("administrator_login", props.AdministratorLogin) - d.Set("ssl_minimal_tls_version_enforced", props.MinimalTLSVersion) - d.Set("version", string(props.Version)) + if err := d.Set("identity", identity.FlattenSystemAssigned(model.Identity)); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } - d.Set("infrastructure_encryption_enabled", props.InfrastructureEncryption == postgresql.InfrastructureEncryptionEnabled) - d.Set("public_network_access_enabled", props.PublicNetworkAccess == postgresql.PublicNetworkAccessEnumEnabled) - d.Set("ssl_enforcement_enabled", props.SslEnforcement == postgresql.SslEnforcementEnumEnabled) + if props := model.Properties; props != nil { + d.Set("administrator_login", props.AdministratorLogin) + d.Set("ssl_minimal_tls_version_enforced", props.MinimalTlsVersion) - if storage := props.StorageProfile; storage != nil { - d.Set("storage_mb", storage.StorageMB) - d.Set("backup_retention_days", storage.BackupRetentionDays) - d.Set("auto_grow_enabled", storage.StorageAutogrow == postgresql.StorageAutogrowEnabled) - d.Set("geo_redundant_backup_enabled", storage.GeoRedundantBackup == postgresql.Enabled) - } + version := "" + if props.Version != nil { + version = string(*props.Version) + } + d.Set("version", version) - // Computed - d.Set("fqdn", props.FullyQualifiedDomainName) - } + infrastructureEncryption := false + if props.InfrastructureEncryption != nil { + infrastructureEncryption = *props.InfrastructureEncryption == servers.InfrastructureEncryptionEnabled + } + d.Set("infrastructure_encryption_enabled", infrastructureEncryption) + + publicNetworkAccess := false + if props.PublicNetworkAccess != nil { + publicNetworkAccess = *props.PublicNetworkAccess == servers.PublicNetworkAccessEnumEnabled + } + d.Set("public_network_access_enabled", publicNetworkAccess) + + sslEnforcement := false + if props.SslEnforcement != nil { + sslEnforcement = *props.SslEnforcement == servers.SslEnforcementEnumEnabled + } + d.Set("ssl_enforcement_enabled", sslEnforcement) + + if storage := props.StorageProfile; storage != nil { + d.Set("storage_mb", storage.StorageMB) + d.Set("backup_retention_days", storage.BackupRetentionDays) + + autoGrow := false + if storage.StorageAutogrow != nil { + autoGrow = *storage.StorageAutogrow == servers.StorageAutogrowEnabled + } + d.Set("auto_grow_enabled", autoGrow) - // the basic does not support threat detection policies - if tier == postgresql.GeneralPurpose || tier == postgresql.MemoryOptimized { - secResp, err := securityClient.Get(ctx, id.ResourceGroup, id.Name) - if err != nil && !utils.ResponseWasNotFound(secResp.Response) { - return fmt.Errorf("making read request to postgres server security alert policy: %+v", err) + geoRedundant := false + if storage.GeoRedundantBackup != nil { + geoRedundant = *storage.GeoRedundantBackup == servers.GeoRedundantBackupEnabled + } + d.Set("geo_redundant_backup_enabled", geoRedundant) + } + + // Computed + d.Set("fqdn", props.FullyQualifiedDomainName) } - if !utils.ResponseWasNotFound(secResp.Response) { - block := flattenSecurityAlertPolicy(secResp.SecurityAlertPolicyProperties, d.Get("threat_detection_policy.0.storage_account_access_key").(string)) - if err := d.Set("threat_detection_policy", block); err != nil { - return fmt.Errorf("setting `threat_detection_policy`: %+v", err) + // the basic does not support threat detection policies + if tier == servers.SkuTierGeneralPurpose || tier == servers.SkuTierMemoryOptimized { + securityId := serversecurityalertpolicies.NewServerID(id.SubscriptionId, id.ResourceGroupName, id.ServerName) + secResp, err := securityClient.Get(ctx, securityId) + if err != nil && !response.WasNotFound(secResp.HttpResponse) { + return fmt.Errorf("making read request to postgres server security alert policy: %+v", err) + } + + if !response.WasNotFound(secResp.HttpResponse) { + if secResp.Model != nil { + block := flattenSecurityAlertPolicy(secResp.Model.Properties, d.Get("threat_detection_policy.0.storage_account_access_key").(string)) + if err := d.Set("threat_detection_policy", block); err != nil { + return fmt.Errorf("setting `threat_detection_policy`: %+v", err) + } + } } } + + return tags.FlattenAndSet(d, model.Tags) } - return tags.FlattenAndSet(d, resp.Tags) + return nil } func resourcePostgreSQLServerDelete(d *pluginsdk.ResourceData, meta interface{}) error { @@ -802,20 +805,15 @@ func resourcePostgreSQLServerDelete(d *pluginsdk.ResourceData, meta interface{}) ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.ServerID(d.Id()) + id, err := servers.ParseServerID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.Name) - if err != nil { + if err = client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", *id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) - } - return nil } @@ -828,20 +826,20 @@ func indexOfSku(skuName string) int { return -1 // not found. } -func expandServerSkuName(skuName string) (*postgresql.Sku, error) { +func expandServerSkuName(skuName string) (*servers.Sku, error) { parts := strings.Split(skuName, "_") if len(parts) != 3 { return nil, fmt.Errorf("sku_name (%s) has the wrong number of parts (%d) after splitting on _", skuName, len(parts)) } - var tier postgresql.SkuTier + var tier servers.SkuTier switch parts[0] { case "B": - tier = postgresql.Basic + tier = servers.SkuTierBasic case "GP": - tier = postgresql.GeneralPurpose + tier = servers.SkuTierGeneralPurpose case "MO": - tier = postgresql.MemoryOptimized + tier = servers.SkuTierMemoryOptimized default: return nil, fmt.Errorf("sku_name %s has unknown sku tier %s", skuName, parts[0]) } @@ -851,44 +849,47 @@ func expandServerSkuName(skuName string) (*postgresql.Sku, error) { return nil, fmt.Errorf("cannot convert skuname %s capcity %s to int", skuName, parts[2]) } - return &postgresql.Sku{ - Name: utils.String(skuName), - Tier: tier, - Capacity: utils.Int32(int32(capacity)), + return &servers.Sku{ + Name: skuName, + Tier: &tier, + Capacity: utils.Int64(int64(capacity)), Family: utils.String(parts[1]), }, nil } -func expandPostgreSQLStorageProfile(d *pluginsdk.ResourceData) *postgresql.StorageProfile { - storage := postgresql.StorageProfile{} +func expandPostgreSQLStorageProfile(d *pluginsdk.ResourceData) *servers.StorageProfile { + storage := servers.StorageProfile{} // now override whatever we may have from the block with the top level properties if v, ok := d.GetOk("auto_grow_enabled"); ok { - storage.StorageAutogrow = postgresql.StorageAutogrowDisabled + autogrowEnabled := servers.StorageAutogrowDisabled if v.(bool) { - storage.StorageAutogrow = postgresql.StorageAutogrowEnabled + autogrowEnabled = servers.StorageAutogrowEnabled } + storage.StorageAutogrow = &autogrowEnabled } if v, ok := d.GetOk("backup_retention_days"); ok { - storage.BackupRetentionDays = utils.Int32(int32(v.(int))) + storage.BackupRetentionDays = utils.Int64(int64(v.(int))) } if v, ok := d.GetOk("geo_redundant_backup_enabled"); ok { - storage.GeoRedundantBackup = postgresql.Disabled + geoRedundantBackup := servers.GeoRedundantBackupDisabled + if v.(bool) { - storage.GeoRedundantBackup = postgresql.Enabled + geoRedundantBackup = servers.GeoRedundantBackupEnabled } + storage.GeoRedundantBackup = &geoRedundantBackup } if v, ok := d.GetOk("storage_mb"); ok { - storage.StorageMB = utils.Int32(int32(v.(int))) + storage.StorageMB = utils.Int64(int64(v.(int))) } return &storage } -func expandSecurityAlertPolicy(i interface{}) *postgresql.ServerSecurityAlertPolicy { +func expandSecurityAlertPolicy(i interface{}) *serversecurityalertpolicies.ServerSecurityAlertPolicy { slice := i.([]interface{}) if len(slice) == 0 { return nil @@ -896,12 +897,12 @@ func expandSecurityAlertPolicy(i interface{}) *postgresql.ServerSecurityAlertPol block := slice[0].(map[string]interface{}) - state := postgresql.ServerSecurityAlertPolicyStateEnabled + state := serversecurityalertpolicies.ServerSecurityAlertPolicyStateEnabled if !block["enabled"].(bool) { - state = postgresql.ServerSecurityAlertPolicyStateDisabled + state = serversecurityalertpolicies.ServerSecurityAlertPolicyStateDisabled } - props := &postgresql.SecurityAlertPolicyProperties{ + props := &serversecurityalertpolicies.SecurityAlertPolicyProperties{ State: state, } @@ -918,7 +919,7 @@ func expandSecurityAlertPolicy(i interface{}) *postgresql.ServerSecurityAlertPol } if v, ok := block["retention_days"]; ok { - props.RetentionDays = utils.Int32(int32(v.(int))) + props.RetentionDays = utils.Int64(int64(v.(int))) } if v, ok := block["storage_account_access_key"]; ok && v.(string) != "" { @@ -929,12 +930,12 @@ func expandSecurityAlertPolicy(i interface{}) *postgresql.ServerSecurityAlertPol props.StorageEndpoint = utils.String(v.(string)) } - return &postgresql.ServerSecurityAlertPolicy{ - SecurityAlertPolicyProperties: props, + return &serversecurityalertpolicies.ServerSecurityAlertPolicy{ + Properties: props, } } -func flattenSecurityAlertPolicy(props *postgresql.SecurityAlertPolicyProperties, accessKey string) interface{} { +func flattenSecurityAlertPolicy(props *serversecurityalertpolicies.SecurityAlertPolicyProperties, accessKey string) interface{} { if props == nil { return nil } @@ -946,13 +947,13 @@ func flattenSecurityAlertPolicy(props *postgresql.SecurityAlertPolicyProperties, props.StorageEndpoint != nil && *props.StorageEndpoint == "" && props.RetentionDays != nil && *props.RetentionDays == 0 && props.EmailAccountAdmins != nil && !*props.EmailAccountAdmins && - props.State == postgresql.ServerSecurityAlertPolicyStateDisabled { + props.State == serversecurityalertpolicies.ServerSecurityAlertPolicyStateDisabled { return nil } block := map[string]interface{}{} - block["enabled"] = props.State == postgresql.ServerSecurityAlertPolicyStateEnabled + block["enabled"] = props.State == serversecurityalertpolicies.ServerSecurityAlertPolicyStateEnabled block["disabled_alerts"] = flattenSecurityAlertPolicySet(props.DisabledAlerts) block["email_addresses"] = flattenSecurityAlertPolicySet(props.EmailAddresses) @@ -972,39 +973,6 @@ func flattenSecurityAlertPolicy(props *postgresql.SecurityAlertPolicyProperties, return []interface{}{block} } -func expandServerIdentity(input []interface{}) (*postgresql.ResourceIdentity, error) { - expanded, err := identity.ExpandSystemAssigned(input) - if err != nil { - return nil, err - } - - if expanded.Type == identity.TypeNone { - return nil, nil - } - - return &postgresql.ResourceIdentity{ - Type: postgresql.IdentityType(string(expanded.Type)), - }, nil -} - -func flattenServerIdentity(input *postgresql.ResourceIdentity) []interface{} { - var transition *identity.SystemAssigned - - if input != nil { - transition = &identity.SystemAssigned{ - Type: identity.Type(string(input.Type)), - } - if input.PrincipalID != nil { - transition.PrincipalId = input.PrincipalID.String() - } - if input.TenantID != nil { - transition.TenantId = input.TenantID.String() - } - } - - return identity.FlattenSystemAssigned(transition) -} - func flattenSecurityAlertPolicySet(input *[]string) []interface{} { if input == nil { return make([]interface{}, 0) @@ -1020,23 +988,23 @@ func flattenSecurityAlertPolicySet(input *[]string) []interface{} { return utils.FlattenStringSlice(input) } -func postgreSqlStateRefreshFunc(ctx context.Context, client *postgresql.ServersClient, id parse.ServerId) pluginsdk.StateRefreshFunc { +func postgreSqlStateRefreshFunc(ctx context.Context, client *servers.ServersClient, id servers.ServerId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.Get(ctx, id.ResourceGroup, id.Name) - if !utils.ResponseWasNotFound(res.Response) && err != nil { + res, err := client.Get(ctx, id) + if !response.WasNotFound(res.HttpResponse) && err != nil { return nil, "", fmt.Errorf("retrieving status of %s: %+v", id, err) } // This is an issue with the RP, there is a 10 to 15 second lag before the // service will actually return the server - if utils.ResponseWasNotFound(res.Response) { - return res, string(postgresql.ServerStateInaccessible), nil + if response.WasNotFound(res.HttpResponse) { + return res, string(servers.ServerStateInaccessible), nil } - if res.ServerProperties != nil && res.ServerProperties.UserVisibleState != "" { - return res, string(res.ServerProperties.UserVisibleState), nil + if res.Model != nil && res.Model.Properties != nil && res.Model.Properties.UserVisibleState != nil && *res.Model.Properties.UserVisibleState != "" { + return res, string(*res.Model.Properties.UserVisibleState), nil } - return res, string(postgresql.ServerStateInaccessible), nil + return res, string(servers.ServerStateInaccessible), nil } } diff --git a/internal/services/postgres/postgresql_server_resource_test.go b/internal/services/postgres/postgresql_server_resource_test.go index 5174e8fa6153..4bef238fd4f6 100644 --- a/internal/services/postgres/postgresql_server_resource_test.go +++ b/internal/services/postgres/postgresql_server_resource_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/servers" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -393,17 +393,17 @@ func TestMinTlsVersionOnServerUpdate(t *testing.T) { } func (t PostgreSQLServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.ServerID(state.ID) + id, err := servers.ParseServerID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.ServersClient.Get(ctx, id.ResourceGroup, id.Name) + resp, err := clients.Postgres.ServersClient.Get(ctx, *id) if err != nil { return nil, fmt.Errorf("reading Postgresql Server (%s): %+v", id.String(), err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (PostgreSQLServerResource) template(data acceptance.TestData, sku, version string) string { diff --git a/internal/services/postgres/postgresql_virtual_network_rule_resource.go b/internal/services/postgres/postgresql_virtual_network_rule_resource.go index 56db2624ee7e..1e72e7443f6f 100644 --- a/internal/services/postgres/postgresql_virtual_network_rule_resource.go +++ b/internal/services/postgres/postgresql_virtual_network_rule_resource.go @@ -6,12 +6,12 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/virtualnetworkrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -25,7 +25,7 @@ func resourcePostgreSQLVirtualNetworkRule() *pluginsdk.Resource { Update: resourcePostgreSQLVirtualNetworkRuleCreateUpdate, Delete: resourcePostgreSQLVirtualNetworkRuleDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { - _, err := parse.VirtualNetworkRuleID(id) + _, err := virtualnetworkrules.ParseVirtualNetworkRuleID(id) return err }), @@ -74,38 +74,33 @@ func resourcePostgreSQLVirtualNetworkRuleCreateUpdate(d *pluginsdk.ResourceData, ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - id := parse.NewVirtualNetworkRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) + id := virtualnetworkrules.NewVirtualNetworkRuleID(subscriptionId, d.Get("resource_group_name").(string), d.Get("server_name").(string), d.Get("name").(string)) if d.IsNewResource() { - existing, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + existing, err := client.Get(ctx, id) if err != nil { - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return fmt.Errorf("checking for presence of existing %s: %+v", id, err) } } - if !utils.ResponseWasNotFound(existing.Response) { + if !response.WasNotFound(existing.HttpResponse) { return tf.ImportAsExistsError("azurerm_postgresql_virtual_network_rule", id.ID()) } } - parameters := postgresql.VirtualNetworkRule{ - VirtualNetworkRuleProperties: &postgresql.VirtualNetworkRuleProperties{ - VirtualNetworkSubnetID: utils.String(d.Get("subnet_id").(string)), + parameters := virtualnetworkrules.VirtualNetworkRule{ + Properties: &virtualnetworkrules.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetId: d.Get("subnet_id").(string), IgnoreMissingVnetServiceEndpoint: utils.Bool(d.Get("ignore_missing_vnet_service_endpoint").(bool)), }, } - future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, parameters) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for creation of %s: %+v", id, err) - } - // Wait for the provisioning state to become ready - log.Printf("[DEBUG] Waiting for %s to become ready: %+v", id, err) + log.Printf("[DEBUG] Waiting for %s to become ready", id) stateConf := &pluginsdk.StateChangeConf{ Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, Target: []string{"Ready"}, @@ -120,7 +115,7 @@ func resourcePostgreSQLVirtualNetworkRuleCreateUpdate(d *pluginsdk.ResourceData, stateConf.Timeout = d.Timeout(pluginsdk.TimeoutUpdate) } - if _, err = stateConf.WaitForStateContext(ctx); err != nil { + if _, err := stateConf.WaitForStateContext(ctx); err != nil { return fmt.Errorf("waiting for %s to be created or updated: %+v", id, err) } @@ -133,29 +128,31 @@ func resourcePostgreSQLVirtualNetworkRuleRead(d *pluginsdk.ResourceData, meta in ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.VirtualNetworkRuleID(d.Id()) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(d.Id()) if err != nil { return err } - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, *id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[INFO] Error reading PostgreSQL Virtual Network Rule %q - removing from state", d.Id()) d.SetId("") return nil } - return fmt.Errorf("retrieving Virtual Network Rule %q (PostgreSQL Server %q / Resource Group %q): %+v", id.Name, id.ServerName, id.ResourceGroup, err) + return fmt.Errorf("retrieving %s: %+v", id, err) } - d.Set("name", id.Name) - d.Set("resource_group_name", id.ResourceGroup) + d.Set("name", id.VirtualNetworkRuleName) + d.Set("resource_group_name", id.ResourceGroupName) d.Set("server_name", id.ServerName) - if props := resp.VirtualNetworkRuleProperties; props != nil { - d.Set("subnet_id", props.VirtualNetworkSubnetID) - d.Set("ignore_missing_vnet_service_endpoint", props.IgnoreMissingVnetServiceEndpoint) + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("subnet_id", props.VirtualNetworkSubnetId) + d.Set("ignore_missing_vnet_service_endpoint", props.IgnoreMissingVnetServiceEndpoint) + } } return nil @@ -166,28 +163,23 @@ func resourcePostgreSQLVirtualNetworkRuleDelete(d *pluginsdk.ResourceData, meta ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := parse.VirtualNetworkRuleID(d.Id()) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(d.Id()) if err != nil { return err } - future, err := client.Delete(ctx, id.ResourceGroup, id.ServerName, id.Name) - if err != nil { - return fmt.Errorf("deleting PostgreSQL Virtual Network Rule %q (PostgreSQL Server: %q, Resource Group: %q): %+v", id.Name, id.ServerName, id.ResourceGroup, err) - } - - if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("waiting for deletion of Virtual Network Rule %q (PostgreSQL Server %q / Resource Group %q): %+v", id.Name, id.ServerName, id.ResourceGroup, err) + if err := client.DeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) } return nil } -func postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *postgresql.VirtualNetworkRulesClient, id parse.VirtualNetworkRuleId) pluginsdk.StateRefreshFunc { +func postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client *virtualnetworkrules.VirtualNetworkRulesClient, id virtualnetworkrules.VirtualNetworkRuleId) pluginsdk.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := client.Get(ctx, id) if err != nil { - if utils.ResponseWasNotFound(resp.Response) { + if response.WasNotFound(resp.HttpResponse) { log.Printf("[DEBUG] Retrieving %s returned 404.", id) return nil, "ResponseNotFound", nil } @@ -195,9 +187,11 @@ func postgreSQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, cli return nil, "", fmt.Errorf("polling for the state of the %s: %+v", id, err) } - if props := resp.VirtualNetworkRuleProperties; props != nil { - log.Printf("[DEBUG] Retrieving %s returned Status %s", id, props.State) - return resp, string(props.State), nil + if model := resp.Model; model != nil { + if props := model.Properties; props != nil && props.State != nil { + log.Printf("[DEBUG] Retrieving %s returned Status %s", id, string(*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. diff --git a/internal/services/postgres/postgresql_virtual_network_rule_resource_test.go b/internal/services/postgres/postgresql_virtual_network_rule_resource_test.go index 1c817c7e4816..064decb00b96 100644 --- a/internal/services/postgres/postgresql_virtual_network_rule_resource_test.go +++ b/internal/services/postgres/postgresql_virtual_network_rule_resource_test.go @@ -6,10 +6,10 @@ import ( "regexp" "testing" + "github.com/hashicorp/go-azure-sdk/resource-manager/postgresql/2017-12-01/virtualnetworkrules" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -109,32 +109,27 @@ func TestAccPostgreSQLVirtualNetworkRule_IgnoreEndpointValid(t *testing.T) { } func (r PostgreSQLVirtualNetworkRuleResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.VirtualNetworkRuleID(state.ID) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(state.ID) if err != nil { return nil, err } - resp, err := clients.Postgres.VirtualNetworkRulesClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name) + resp, err := clients.Postgres.VirtualNetworkRulesClient.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("reading Postgresql Virtual Network Rule (%s): %+v", id.String(), err) + return nil, fmt.Errorf("reading %s: %+v", id, err) } - return utils.Bool(resp.ID != nil), nil + return utils.Bool(resp.Model != nil), nil } func (r PostgreSQLVirtualNetworkRuleResource) Destroy(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - id, err := parse.VirtualNetworkRuleID(state.ID) + id, err := virtualnetworkrules.ParseVirtualNetworkRuleID(state.ID) if err != nil { return nil, err } - future, err := client.Postgres.VirtualNetworkRulesClient.Delete(ctx, id.ResourceGroup, id.ServerName, id.Name) - if err != nil { - return nil, fmt.Errorf("deleting Postgresql Virtual Network Rule (%s): %+v", id.String(), err) - } - - if err := future.WaitForCompletionRef(ctx, client.Postgres.VirtualNetworkRulesClient.Client); err != nil { - return nil, fmt.Errorf("waiting for deletion of Postgresql Virtual Network Rule (%s): %+v", id.String(), err) + if err := client.Postgres.VirtualNetworkRulesClient.DeleteThenPoll(ctx, *id); err != nil { + return nil, fmt.Errorf("deleting %s: %+v", id, err) } return utils.Bool(true), nil diff --git a/internal/services/postgres/resourceids.go b/internal/services/postgres/resourceids.go index 09280329ad93..3a7c16023bbe 100644 --- a/internal/services/postgres/resourceids.go +++ b/internal/services/postgres/resourceids.go @@ -1,13 +1,3 @@ package postgres //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AzureActiveDirectoryAdministrator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/administrators/activeDirectory -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Configuration -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/configuration1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Database -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/database1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FirewallRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/firewallRule1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Server -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ServerKey -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/key1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=VirtualNetworkRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/virtualNetworkRule1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FlexibleServer -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FlexibleServerFirewallRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/firewallRule1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FlexibleServerConfiguration -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/configuration1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=FlexibleServerDatabase -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/database1 diff --git a/internal/services/postgres/validate/configuration_id.go b/internal/services/postgres/validate/configuration_id.go deleted file mode 100644 index 5fbc75f538b1..000000000000 --- a/internal/services/postgres/validate/configuration_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func ConfigurationID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ConfigurationID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/configuration_id_test.go b/internal/services/postgres/validate/configuration_id_test.go deleted file mode 100644 index 098402a2fa38..000000000000 --- a/internal/services/postgres/validate/configuration_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestConfigurationID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/configurations/configuration1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/CONFIGURATIONS/CONFIGURATION1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ConfigurationID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/database_id.go b/internal/services/postgres/validate/database_id.go deleted file mode 100644 index 24a8ecbddcf3..000000000000 --- a/internal/services/postgres/validate/database_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func DatabaseID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.DatabaseID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/database_id_test.go b/internal/services/postgres/validate/database_id_test.go deleted file mode 100644 index 8744c371280c..000000000000 --- a/internal/services/postgres/validate/database_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestDatabaseID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/databases/database1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/DATABASES/DATABASE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := DatabaseID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/firewall_rule_id.go b/internal/services/postgres/validate/firewall_rule_id.go deleted file mode 100644 index e0ded2db618a..000000000000 --- a/internal/services/postgres/validate/firewall_rule_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func FirewallRuleID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.FirewallRuleID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/firewall_rule_id_test.go b/internal/services/postgres/validate/firewall_rule_id_test.go deleted file mode 100644 index 77777ead15bc..000000000000 --- a/internal/services/postgres/validate/firewall_rule_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestFirewallRuleID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/firewallRules/firewallRule1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/FIREWALLRULES/FIREWALLRULE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := FirewallRuleID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/flexible_server_configuration_id.go b/internal/services/postgres/validate/flexible_server_configuration_id.go deleted file mode 100644 index fc654717d926..000000000000 --- a/internal/services/postgres/validate/flexible_server_configuration_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func FlexibleServerConfigurationID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.FlexibleServerConfigurationID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/flexible_server_configuration_id_test.go b/internal/services/postgres/validate/flexible_server_configuration_id_test.go deleted file mode 100644 index 682bd62c85c7..000000000000 --- a/internal/services/postgres/validate/flexible_server_configuration_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestFlexibleServerConfigurationID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Valid: false, - }, - - { - // missing ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Valid: false, - }, - - { - // missing value for ConfigurationName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/configurations/configuration1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/CONFIGURATIONS/CONFIGURATION1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := FlexibleServerConfigurationID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/flexible_server_database_id.go b/internal/services/postgres/validate/flexible_server_database_id.go deleted file mode 100644 index c899f3d7fc8f..000000000000 --- a/internal/services/postgres/validate/flexible_server_database_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func FlexibleServerDatabaseID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.FlexibleServerDatabaseID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/flexible_server_database_id_test.go b/internal/services/postgres/validate/flexible_server_database_id_test.go deleted file mode 100644 index 9ee1cd685943..000000000000 --- a/internal/services/postgres/validate/flexible_server_database_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestFlexibleServerDatabaseID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Valid: false, - }, - - { - // missing DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Valid: false, - }, - - { - // missing value for DatabaseName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/databases/database1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/DATABASES/DATABASE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := FlexibleServerDatabaseID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/flexible_server_firewall_rule_id.go b/internal/services/postgres/validate/flexible_server_firewall_rule_id.go deleted file mode 100644 index 62063f69a97d..000000000000 --- a/internal/services/postgres/validate/flexible_server_firewall_rule_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func FlexibleServerFirewallRuleID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.FlexibleServerFirewallRuleID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/flexible_server_firewall_rule_id_test.go b/internal/services/postgres/validate/flexible_server_firewall_rule_id_test.go deleted file mode 100644 index 8eed3cdd11fe..000000000000 --- a/internal/services/postgres/validate/flexible_server_firewall_rule_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestFlexibleServerFirewallRuleID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for FlexibleServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Valid: false, - }, - - { - // missing FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/", - Valid: false, - }, - - { - // missing value for FirewallRuleName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1/firewallRules/firewallRule1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1/FIREWALLRULES/FIREWALLRULE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := FlexibleServerFirewallRuleID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/flexible_server_id.go b/internal/services/postgres/validate/flexible_server_id.go deleted file mode 100644 index 1b998295da2f..000000000000 --- a/internal/services/postgres/validate/flexible_server_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func FlexibleServerID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.FlexibleServerID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/flexible_server_id_test.go b/internal/services/postgres/validate/flexible_server_id_test.go deleted file mode 100644 index b0f0eae7d837..000000000000 --- a/internal/services/postgres/validate/flexible_server_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestFlexibleServerID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibleServer1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/FLEXIBLESERVERS/FLEXIBLESERVER1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := FlexibleServerID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/server_id.go b/internal/services/postgres/validate/server_id.go deleted file mode 100644 index a7864ea1966c..000000000000 --- a/internal/services/postgres/validate/server_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func ServerID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ServerID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/server_id_test.go b/internal/services/postgres/validate/server_id_test.go deleted file mode 100644 index 1fcf76082f48..000000000000 --- a/internal/services/postgres/validate/server_id_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestServerID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ServerID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/server_key_id.go b/internal/services/postgres/validate/server_key_id.go deleted file mode 100644 index fda821517df8..000000000000 --- a/internal/services/postgres/validate/server_key_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func ServerKeyID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.ServerKeyID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/server_key_id_test.go b/internal/services/postgres/validate/server_key_id_test.go deleted file mode 100644 index c63b0d673ad5..000000000000 --- a/internal/services/postgres/validate/server_key_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestServerKeyID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // missing KeyName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Valid: false, - }, - - { - // missing value for KeyName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/keys/key1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/KEYS/KEY1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := ServerKeyID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/postgres/validate/virtual_network_rule_id.go b/internal/services/postgres/validate/virtual_network_rule_id.go deleted file mode 100644 index 550db479d5e1..000000000000 --- a/internal/services/postgres/validate/virtual_network_rule_id.go +++ /dev/null @@ -1,23 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import ( - "fmt" - - "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" -) - -func VirtualNetworkRuleID(input interface{}, key string) (warnings []string, errors []error) { - v, ok := input.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected %q to be a string", key)) - return - } - - if _, err := parse.VirtualNetworkRuleID(v); err != nil { - errors = append(errors, err) - } - - return -} diff --git a/internal/services/postgres/validate/virtual_network_rule_id_test.go b/internal/services/postgres/validate/virtual_network_rule_id_test.go deleted file mode 100644 index bb3af88774cd..000000000000 --- a/internal/services/postgres/validate/virtual_network_rule_id_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package validate - -// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten - -import "testing" - -func TestVirtualNetworkRuleID(t *testing.T) { - cases := []struct { - Input string - Valid bool - }{ - - { - // empty - Input: "", - Valid: false, - }, - - { - // missing SubscriptionId - Input: "/", - Valid: false, - }, - - { - // missing value for SubscriptionId - Input: "/subscriptions/", - Valid: false, - }, - - { - // missing ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", - Valid: false, - }, - - { - // missing value for ResourceGroup - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", - Valid: false, - }, - - { - // missing ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/", - Valid: false, - }, - - { - // missing value for ServerName - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", - Valid: false, - }, - - { - // missing Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/", - Valid: false, - }, - - { - // missing value for Name - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/", - Valid: false, - }, - - { - // valid - Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/server1/virtualNetworkRules/virtualNetworkRule1", - Valid: true, - }, - - { - // upper-cased - Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.DBFORPOSTGRESQL/SERVERS/SERVER1/VIRTUALNETWORKRULES/VIRTUALNETWORKRULE1", - Valid: false, - }, - } - for _, tc := range cases { - t.Logf("[DEBUG] Testing Value %s", tc.Input) - _, errors := VirtualNetworkRuleID(tc.Input, "test") - valid := len(errors) == 0 - - if tc.Valid != valid { - t.Fatalf("Expected %t but got %t", tc.Valid, valid) - } - } -} diff --git a/internal/services/privatedns/private_dns_a_record_data_source.go b/internal/services/privatedns/private_dns_a_record_data_source.go new file mode 100644 index 000000000000..10a00a32012f --- /dev/null +++ b/internal/services/privatedns/private_dns_a_record_data_source.go @@ -0,0 +1,95 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsARecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsARecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsARecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeA, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmPrivateDnsARecords(props.ARecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_a_record_data_source_test.go b/internal/services/privatedns/private_dns_a_record_data_source_test.go new file mode 100644 index 000000000000..e4d82a1de91c --- /dev/null +++ b/internal/services/privatedns/private_dns_a_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsARecordDataSource struct{} + +func TestAccDataSourcePrivateDnsARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_a_record", "test") + r := PrivateDnsARecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsARecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_a_record" "test" { + name = azurerm_private_dns_a_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsARecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_a_record_resource.go b/internal/services/privatedns/private_dns_a_record_resource.go index 0b07277f51c4..6945e4e0c7b8 100644 --- a/internal/services/privatedns/private_dns_a_record_resource.go +++ b/internal/services/privatedns/private_dns_a_record_resource.go @@ -192,11 +192,11 @@ func flattenAzureRmPrivateDnsARecords(records *[]recordsets.ARecord) []string { } for _, record := range *records { - if record.Ipv4Address == nil { + if record.IPv4Address == nil { continue } - results = append(results, *record.Ipv4Address) + results = append(results, *record.IPv4Address) } return results @@ -209,7 +209,7 @@ func expandAzureRmPrivateDnsARecords(d *pluginsdk.ResourceData) *[]recordsets.AR for i, v := range recordStrings { ipv4 := v.(string) records[i] = recordsets.ARecord{ - Ipv4Address: &ipv4, + IPv4Address: &ipv4, } } diff --git a/internal/services/privatedns/private_dns_aaaa_record_data_source.go b/internal/services/privatedns/private_dns_aaaa_record_data_source.go new file mode 100644 index 000000000000..832b1a4bcca1 --- /dev/null +++ b/internal/services/privatedns/private_dns_aaaa_record_data_source.go @@ -0,0 +1,96 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/set" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsAaaaRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsAAAARecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: set.HashIPv6Address, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsAAAARecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeAAAA, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmPrivateDnsAaaaRecords(props.AaaaRecords)); err != nil { + return fmt.Errorf("setting `records`: %+v", err) + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_aaaa_record_data_source_test.go b/internal/services/privatedns/private_dns_aaaa_record_data_source_test.go new file mode 100644 index 000000000000..db9486d0dcd2 --- /dev/null +++ b/internal/services/privatedns/private_dns_aaaa_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsAAAARecordDataSource struct{} + +func TestAccDataSourcePrivateDnsAAAARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_aaaa_record", "test") + r := PrivateDnsAAAARecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsAAAARecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_aaaa_record" "test" { + name = azurerm_private_dns_aaaa_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsAAAARecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_aaaa_record_resource.go b/internal/services/privatedns/private_dns_aaaa_record_resource.go index 45d6ebac54fe..c58eaed50d18 100644 --- a/internal/services/privatedns/private_dns_aaaa_record_resource.go +++ b/internal/services/privatedns/private_dns_aaaa_record_resource.go @@ -188,11 +188,11 @@ func flattenAzureRmPrivateDnsAaaaRecords(records *[]recordsets.AaaaRecord) []str } for _, record := range *records { - if record.Ipv6Address == nil { + if record.IPv6Address == nil { continue } - results = append(results, *record.Ipv6Address) + results = append(results, *record.IPv6Address) } return results @@ -205,7 +205,7 @@ func expandAzureRmPrivateDnsAaaaRecords(d *pluginsdk.ResourceData) *[]recordsets for i, v := range recordStrings { ipv6 := v.(string) records[i] = recordsets.AaaaRecord{ - Ipv6Address: &ipv6, + IPv6Address: &ipv6, } } diff --git a/internal/services/privatedns/private_dns_cname_record_data_source.go b/internal/services/privatedns/private_dns_cname_record_data_source.go new file mode 100644 index 000000000000..18f1b5a21fb6 --- /dev/null +++ b/internal/services/privatedns/private_dns_cname_record_data_source.go @@ -0,0 +1,101 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsCNameRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsCNameRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "target_resource_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsCNameRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeCNAME, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + cname := "" + if props.CnameRecord != nil && props.CnameRecord.Cname != nil { + cname = *props.CnameRecord.Cname + } + d.Set("record", cname) + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_cname_record_data_source_test.go b/internal/services/privatedns/private_dns_cname_record_data_source_test.go new file mode 100644 index 000000000000..e258e35c377c --- /dev/null +++ b/internal/services/privatedns/private_dns_cname_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsCNameRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsCNameRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_cname_record", "test") + r := PrivateDnsCNameRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record").HasValue("contoso.com"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsCNameRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_cname_record" "test" { + name = azurerm_private_dns_cname_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsCNameRecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_mx_record_data_source.go b/internal/services/privatedns/private_dns_mx_record_data_source.go new file mode 100644 index 000000000000..74114ebd393f --- /dev/null +++ b/internal/services/privatedns/private_dns_mx_record_data_source.go @@ -0,0 +1,106 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsMxRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsMxRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "preference": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + "exchange": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourcePrivateDnsMxRecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsMxRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeMX, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmPrivateDnsMxRecords(props.MxRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_mx_record_data_source_test.go b/internal/services/privatedns/private_dns_mx_record_data_source_test.go new file mode 100644 index 000000000000..5ffba71ecdff --- /dev/null +++ b/internal/services/privatedns/private_dns_mx_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsMxRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsMxRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_mx_record", "test") + r := PrivateDnsMxRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsMxRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_mx_record" "test" { + name = azurerm_private_dns_mx_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsMxRecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_mx_record_resource.go b/internal/services/privatedns/private_dns_mx_record_resource.go index 85b4c9116d91..baacaf37710f 100644 --- a/internal/services/privatedns/private_dns_mx_record_resource.go +++ b/internal/services/privatedns/private_dns_mx_record_resource.go @@ -1,6 +1,7 @@ package privatedns import ( + "bytes" "fmt" "time" @@ -240,3 +241,14 @@ func expandAzureRmPrivateDnsMxRecords(d *pluginsdk.ResourceData) *[]recordsets.M return &records } + +func resourcePrivateDnsMxRecordHash(v interface{}) int { + var buf bytes.Buffer + + if m, ok := v.(map[string]interface{}); ok { + buf.WriteString(fmt.Sprintf("%d-", m["preference"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["exchange"].(string))) + } + + return pluginsdk.HashString(buf.String()) +} diff --git a/internal/services/privatedns/private_dns_ptr_record_data_source.go b/internal/services/privatedns/private_dns_ptr_record_data_source.go new file mode 100644 index 000000000000..810d513a56d8 --- /dev/null +++ b/internal/services/privatedns/private_dns_ptr_record_data_source.go @@ -0,0 +1,95 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsPtrRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsPtrRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "records": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + Set: pluginsdk.HashString, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsPtrRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypePTR, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("records", flattenAzureRmPrivateDnsPtrRecords(props.PtrRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_ptr_record_data_source_test.go b/internal/services/privatedns/private_dns_ptr_record_data_source_test.go new file mode 100644 index 000000000000..9f256843578a --- /dev/null +++ b/internal/services/privatedns/private_dns_ptr_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsPtrRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsPtrRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_ptr_record", "test") + r := PrivateDnsPtrRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("records.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsPtrRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_ptr_record" "test" { + name = azurerm_private_dns_ptr_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsPtrRecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_soa_record_data_source.go b/internal/services/privatedns/private_dns_soa_record_data_source.go new file mode 100644 index 000000000000..614ae5258efa --- /dev/null +++ b/internal/services/privatedns/private_dns_soa_record_data_source.go @@ -0,0 +1,129 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsSoaRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsSoaRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "@", + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "email": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "host_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "expire_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "minimum_ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "refresh_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "retry_time": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "serial_number": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsSoaRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeSOA, "@") + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if soaRecord := props.SoaRecord; soaRecord != nil { + d.Set("email", soaRecord.Email) + d.Set("host_name", soaRecord.Host) + d.Set("expire_time", soaRecord.ExpireTime) + d.Set("minimum_ttl", soaRecord.MinimumTtl) + d.Set("refresh_time", soaRecord.RefreshTime) + d.Set("retry_time", soaRecord.RetryTime) + d.Set("serial_number", soaRecord.SerialNumber) + } + + return tags.FlattenAndSet(d, props.Metadata) + } + + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_soa_record_data_source_test.go b/internal/services/privatedns/private_dns_soa_record_data_source_test.go new file mode 100644 index 000000000000..7fa2bad1994e --- /dev/null +++ b/internal/services/privatedns/private_dns_soa_record_data_source_test.go @@ -0,0 +1,71 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsSoaRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsSoaRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_soa_record", "test") + r := PrivateDnsSoaRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basicWithDataSource(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("name").HasValue("@"), + check.That(data.ResourceName).Key("email").HasValue("testemail.com"), + check.That(data.ResourceName).Key("host_name").HasValue("azureprivatedns.net"), + check.That(data.ResourceName).Key("expire_time").HasValue("2419200"), + check.That(data.ResourceName).Key("minimum_ttl").HasValue("10"), + check.That(data.ResourceName).Key("refresh_time").HasValue("3600"), + check.That(data.ResourceName).Key("retry_time").HasValue("300"), + check.That(data.ResourceName).Key("serial_number").HasValue("1"), + check.That(data.ResourceName).Key("ttl").HasValue("3600"), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsSoaRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +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 + + soa_record { + email = "testemail.com" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (d PrivateDnsSoaRecordDataSource) basicWithDataSource(data acceptance.TestData) string { + config := d.basic(data) + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_soa_record" "test" { + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, config) +} diff --git a/internal/services/privatedns/private_dns_srv_record_data_source.go b/internal/services/privatedns/private_dns_srv_record_data_source.go new file mode 100644 index 000000000000..db4e79146ba1 --- /dev/null +++ b/internal/services/privatedns/private_dns_srv_record_data_source.go @@ -0,0 +1,116 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsSrvRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsSrvRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "priority": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "weight": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "port": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "target": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourcePrivateDnsSrvRecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsSrvRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeSRV, d.Get("name").(string)) + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmPrivateDnsSrvRecords(props.SrvRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_srv_record_data_source_test.go b/internal/services/privatedns/private_dns_srv_record_data_source_test.go new file mode 100644 index 000000000000..1fe5592d7cf9 --- /dev/null +++ b/internal/services/privatedns/private_dns_srv_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsSrvRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsSrvRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_srv_record", "test") + r := PrivateDnsSrvRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsSrvRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_srv_record" "test" { + name = azurerm_private_dns_srv_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsSrvRecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_srv_record_resource.go b/internal/services/privatedns/private_dns_srv_record_resource.go index 847b4925bb34..24873f3bd753 100644 --- a/internal/services/privatedns/private_dns_srv_record_resource.go +++ b/internal/services/privatedns/private_dns_srv_record_resource.go @@ -1,6 +1,7 @@ package privatedns import ( + "bytes" "fmt" "time" @@ -252,3 +253,16 @@ func expandAzureRmPrivateDnsSrvRecords(d *pluginsdk.ResourceData) *[]recordsets. return &records } + +func resourcePrivateDnsSrvRecordHash(v interface{}) int { + var buf bytes.Buffer + + if m, ok := v.(map[string]interface{}); ok { + buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["weight"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) + } + + return pluginsdk.HashString(buf.String()) +} diff --git a/internal/services/privatedns/private_dns_txt_record_data_source.go b/internal/services/privatedns/private_dns_txt_record_data_source.go new file mode 100644 index 000000000000..e95913ac01ac --- /dev/null +++ b/internal/services/privatedns/private_dns_txt_record_data_source.go @@ -0,0 +1,102 @@ +package privatedns + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/privatedns/2018-09-01/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" +) + +func dataSourcePrivateDnsTxtRecord() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourcePrivateDnsTxtRecordRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "value": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + }, + } +} + +func dataSourcePrivateDnsTxtRecordRead(d *pluginsdk.ResourceData, meta interface{}) error { + recordSetsClient := meta.(*clients.Client).PrivateDns.RecordSetsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, d.Get("resource_group_name").(string), d.Get("zone_name").(string), recordsets.RecordTypeTXT, d.Get("name").(string)) + + resp, err := recordSetsClient.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("reading %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.RelativeRecordSetName) + d.Set("resource_group_name", id.ResourceGroupName) + d.Set("zone_name", id.PrivateZoneName) + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + d.Set("ttl", props.Ttl) + d.Set("fqdn", props.Fqdn) + + if err := d.Set("record", flattenAzureRmPrivateDnsTxtRecords(props.TxtRecords)); err != nil { + return err + } + + return tags.FlattenAndSet(d, props.Metadata) + + } + } + + return nil +} diff --git a/internal/services/privatedns/private_dns_txt_record_data_source_test.go b/internal/services/privatedns/private_dns_txt_record_data_source_test.go new file mode 100644 index 000000000000..0412df7f8a66 --- /dev/null +++ b/internal/services/privatedns/private_dns_txt_record_data_source_test.go @@ -0,0 +1,43 @@ +package privatedns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PrivateDnsTxtRecordDataSource struct{} + +func TestAccDataSourcePrivateDnsTxtRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_private_dns_txt_record", "test") + r := PrivateDnsTxtRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (PrivateDnsTxtRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_private_dns_txt_record" "test" { + name = azurerm_private_dns_txt_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_private_dns_zone.test.name +} +`, PrivateDnsTxtRecordResource{}.basic(data)) +} diff --git a/internal/services/privatedns/private_dns_zone_data_source.go b/internal/services/privatedns/private_dns_zone_data_source.go index 2d528c2112ed..6e112d3b3ef9 100644 --- a/internal/services/privatedns/private_dns_zone_data_source.go +++ b/internal/services/privatedns/private_dns_zone_data_source.go @@ -66,12 +66,10 @@ func dataSourcePrivateDnsZoneRead(d *pluginsdk.ResourceData, meta interface{}) e ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) - id := privatezones.NewPrivateDnsZoneID(subscriptionId, resourceGroup, name) + id := privatezones.NewPrivateDnsZoneID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) var resp *privatezones.PrivateZone - if resourceGroup != "" { + if id.ResourceGroupName != "" { zone, err := client.Get(ctx, id) if err != nil || zone.Model == nil { if response.WasNotFound(zone.HttpResponse) { @@ -83,23 +81,23 @@ func dataSourcePrivateDnsZoneRead(d *pluginsdk.ResourceData, meta interface{}) e } else { resourcesClient := meta.(*clients.Client).Resource.ResourcesClient - zone, err := findPrivateZone(ctx, client, resourcesClient, name) + zone, err := findPrivateZone(ctx, client, resourcesClient, id.PrivateZoneName) if err != nil { return err } if zone == nil { - return fmt.Errorf("Private DNS Zone %q was not found", name) + return fmt.Errorf("%s was not found", id) } resp = &zone.zone - resourceGroup = zone.resourceGroup + id.ResourceGroupName = zone.resourceGroup } d.SetId(id.ID()) - d.Set("name", name) - d.Set("resource_group_name", resourceGroup) + d.Set("name", id.PrivateZoneName) + d.Set("resource_group_name", id.ResourceGroupName) if props := resp.Properties; props != nil { d.Set("number_of_record_sets", props.NumberOfRecordSets) diff --git a/internal/services/privatedns/private_dns_zone_data_source_test.go b/internal/services/privatedns/private_dns_zone_data_source_test.go index c3da28038a20..56bcf029b1a7 100644 --- a/internal/services/privatedns/private_dns_zone_data_source_test.go +++ b/internal/services/privatedns/private_dns_zone_data_source_test.go @@ -14,10 +14,13 @@ func TestAccDataSourcePrivateDNSZone_basic(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_private_dns_zone", "test") r := PrivateDnsZoneDatasource{} + resourceName := "azurerm_private_dns_zone.test" + data.DataSourceTest(t, []acceptance.TestStep{ { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("id").MatchesOtherKey(check.That(resourceName).Key("id")), check.That(data.ResourceName).Key("tags.%").HasValue("0"), ), }, @@ -28,10 +31,13 @@ func TestAccDataSourcePrivateDNSZone_tags(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_private_dns_zone", "test") r := PrivateDnsZoneDatasource{} + resourceName := "azurerm_private_dns_zone.test" + data.DataSourceTest(t, []acceptance.TestStep{ { Config: r.tags(data), Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("id").MatchesOtherKey(check.That(resourceName).Key("id")), check.That(data.ResourceName).Key("tags.%").HasValue("1"), check.That(data.ResourceName).Key("tags.hello").HasValue("world"), ), @@ -42,42 +48,37 @@ func TestAccDataSourcePrivateDNSZone_tags(t *testing.T) { func TestAccDataSourcePrivateDNSZone_withoutResourceGroupName(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_private_dns_zone", "test") r := PrivateDnsZoneDatasource{} - resourceGroupName := fmt.Sprintf("acctestRG-%d", data.RandomInteger) + resourceName := "azurerm_private_dns_zone.test" + + // This test is split across multiple test steps to avoid an API race + // condition that occures when running multiple test cases in parallel data.DataSourceTest(t, []acceptance.TestStep{ { - Config: r.onlyNamePrep(data, resourceGroupName), + Config: r.template(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(resourceName).Key("id").Exists(), + ), }, { - Config: r.onlyName(data, resourceGroupName), + Config: r.withoutResourceGroupName(data), Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).Key("resource_group_name").HasValue(resourceGroupName), + check.That(data.ResourceName).Key("id").MatchesOtherKey(check.That(resourceName).Key("id")), + check.That(data.ResourceName).Key("resource_group_name").MatchesOtherKey(check.That(resourceName).Key("resource_group_name")), ), }, }) } -func (PrivateDnsZoneDatasource) basic(data acceptance.TestData) string { +func (r PrivateDnsZoneDatasource) basic(data acceptance.TestData) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_private_dns_zone" "test" { - name = "acctestzone%d.internal" - resource_group_name = azurerm_resource_group.test.name -} +%[1]s data "azurerm_private_dns_zone" "test" { name = azurerm_private_dns_zone.test.name resource_group_name = azurerm_private_dns_zone.test.resource_group_name } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, r.template(data)) } func (PrivateDnsZoneDatasource) tags(data acceptance.TestData) string { @@ -87,12 +88,12 @@ provider "azurerm" { } resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" + name = "acctestRG-%[1]d" + location = "%[2]s" } resource "azurerm_private_dns_zone" "test" { - name = "acctestzone%d.internal" + name = "acctestzone%[1]d.internal" resource_group_name = azurerm_resource_group.test.name tags = { @@ -104,33 +105,33 @@ data "azurerm_private_dns_zone" "test" { name = azurerm_private_dns_zone.test.name resource_group_name = azurerm_private_dns_zone.test.resource_group_name } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary) } -func (PrivateDnsZoneDatasource) onlyNamePrep(data acceptance.TestData, resourceGroupName string) string { +func (PrivateDnsZoneDatasource) template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} } resource "azurerm_resource_group" "test" { - name = "%s" - location = "%s" + name = "acctestRG-%[1]d" + location = "%[2]s" } resource "azurerm_private_dns_zone" "test" { - name = "acctestzone%d.internal" + name = "acctestzone%[1]d.internal" resource_group_name = azurerm_resource_group.test.name } -`, resourceGroupName, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary) } -func (r PrivateDnsZoneDatasource) onlyName(data acceptance.TestData, resourceGroupName string) string { +func (r PrivateDnsZoneDatasource) withoutResourceGroupName(data acceptance.TestData) string { return fmt.Sprintf(` -%s +%[1]s data "azurerm_private_dns_zone" "test" { name = azurerm_private_dns_zone.test.name } -`, r.onlyNamePrep(data, resourceGroupName)) +`, r.template(data)) } diff --git a/internal/services/privatedns/registration.go b/internal/services/privatedns/registration.go index 26035b51507c..40b0b0fd4217 100644 --- a/internal/services/privatedns/registration.go +++ b/internal/services/privatedns/registration.go @@ -25,7 +25,15 @@ func (r Registration) WebsiteCategories() []string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_private_dns_zone": dataSourcePrivateDnsZone(), + "azurerm_private_dns_zone": dataSourcePrivateDnsZone(), + "azurerm_private_dns_a_record": dataSourcePrivateDnsARecord(), + "azurerm_private_dns_aaaa_record": dataSourcePrivateDnsAaaaRecord(), + "azurerm_private_dns_cname_record": dataSourcePrivateDnsCNameRecord(), + "azurerm_private_dns_mx_record": dataSourcePrivateDnsMxRecord(), + "azurerm_private_dns_ptr_record": dataSourcePrivateDnsPtrRecord(), + "azurerm_private_dns_soa_record": dataSourcePrivateDnsSoaRecord(), + "azurerm_private_dns_srv_record": dataSourcePrivateDnsSrvRecord(), + "azurerm_private_dns_txt_record": dataSourcePrivateDnsTxtRecord(), } } diff --git a/internal/services/recoveryservices/backup_policy_vm_resource.go b/internal/services/recoveryservices/backup_policy_vm_resource.go index 7f6325f03ac3..80c96013db1c 100644 --- a/internal/services/recoveryservices/backup_policy_vm_resource.go +++ b/internal/services/recoveryservices/backup_policy_vm_resource.go @@ -353,6 +353,16 @@ func expandBackupProtectionPolicyVMSchedule(d *pluginsdk.ResourceData, times []d return nil, fmt.Errorf("`hour_duration` must be specified when `backup.0.frequency` is `Hourly`") } + if interval == 0 && duration == 0 { + return nil, fmt.Errorf("`hour_interval` and `hour_duration` must be specified when `backup.0.frequency` is `Hourly`") + } + if interval == 0 { + return nil, fmt.Errorf("`hour_interval` must be specified when `backup.0.frequency` is `Hourly`") + } + if duration == 0 { + return nil, fmt.Errorf("`hour_duration` must be specified when `backup.0.frequency` is `Hourly`") + } + if duration%interval != 0 { return nil, fmt.Errorf("`hour_duration` must be multiplier of `hour_interval`") } diff --git a/internal/services/recoveryservices/backup_policy_vm_workload_resource.go b/internal/services/recoveryservices/backup_policy_vm_workload_resource.go new file mode 100644 index 000000000000..abd9f4cca0f9 --- /dev/null +++ b/internal/services/recoveryservices/backup_policy_vm_workload_resource.go @@ -0,0 +1,1096 @@ +package recoveryservices + +import ( + "context" + "fmt" + "regexp" + "time" + + "github.com/Azure/azure-sdk-for-go/services/recoveryservices/mgmt/2021-12-01/backup" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/set" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type BackupProtectionPolicyVMWorkloadModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + RecoveryVaultName string `tfschema:"recovery_vault_name"` + ProtectionPolicies []ProtectionPolicy `tfschema:"protection_policy"` + Settings []Settings `tfschema:"settings"` + WorkloadType string `tfschema:"workload_type"` +} + +type ProtectionPolicy struct { + Backup []Backup `tfschema:"backup"` + PolicyType string `tfschema:"policy_type"` + RetentionDaily []RetentionDaily `tfschema:"retention_daily"` + RetentionWeekly []RetentionWeekly `tfschema:"retention_weekly"` + RetentionMonthly []RetentionMonthly `tfschema:"retention_monthly"` + RetentionYearly []RetentionYearly `tfschema:"retention_yearly"` + SimpleRetention []SimpleRetention `tfschema:"simple_retention"` +} + +type Backup struct { + Frequency string `tfschema:"frequency"` + FrequencyInMinutes int32 `tfschema:"frequency_in_minutes"` + Time string `tfschema:"time"` + Weekdays []string `tfschema:"weekdays"` +} + +type RetentionDaily struct { + Count int32 `tfschema:"count"` +} + +type RetentionWeekly struct { + Count int32 `tfschema:"count"` + Weekdays []string `tfschema:"weekdays"` +} + +type RetentionMonthly struct { + Count int32 `tfschema:"count"` + FormatType string `tfschema:"format_type"` + Monthdays []int `tfschema:"monthdays"` + Weeks []string `tfschema:"weeks"` + Weekdays []string `tfschema:"weekdays"` +} + +type RetentionYearly struct { + Count int32 `tfschema:"count"` + FormatType string `tfschema:"format_type"` + Months []string `tfschema:"months"` + Monthdays []int `tfschema:"monthdays"` + Weeks []string `tfschema:"weeks"` + Weekdays []string `tfschema:"weekdays"` +} + +type SimpleRetention struct { + Count int32 `tfschema:"count"` +} + +type Settings struct { + CompressionEnabled bool `tfschema:"compression_enabled"` + TimeZone string `tfschema:"time_zone"` +} + +type BackupProtectionPolicyVMWorkloadResource struct{} + +var _ sdk.ResourceWithUpdate = BackupProtectionPolicyVMWorkloadResource{} + +func (r BackupProtectionPolicyVMWorkloadResource) ResourceType() string { + return "azurerm_backup_policy_vm_workload" +} + +func (r BackupProtectionPolicyVMWorkloadResource) ModelObject() interface{} { + return &BackupProtectionPolicyVMWorkloadModel{} +} + +func (r BackupProtectionPolicyVMWorkloadResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.BackupPolicyID +} + +func (r BackupProtectionPolicyVMWorkloadResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.BackupPolicyName, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "recovery_vault_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.RecoveryServicesVaultName, + }, + + "protection_policy": { + Type: pluginsdk.TypeSet, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "policy_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.PolicyTypeDifferential), + string(backup.PolicyTypeFull), + string(backup.PolicyTypeIncremental), + string(backup.PolicyTypeLog), + }, false), + }, + + "backup": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "frequency": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.ScheduleRunTypeDaily), + string(backup.ScheduleRunTypeWeekly), + }, false), + }, + + "frequency_in_minutes": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntInSlice([]int{ + 15, + 30, + 60, + 120, + 240, + 480, + 720, + 1440, + }), + }, + + "time": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^([01][0-9]|[2][0-3]):([03][0])$"), + "Time of day must match the format HH:mm where HH is 00-23 and mm is 00 or 30", + ), + }, + + "weekdays": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.IsDayOfTheWeek(true), + }, + }, + }, + }, + }, + + "retention_daily": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(7, 9999), + }, + }, + }, + }, + + "retention_weekly": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 5163), + }, + + "weekdays": { + Type: pluginsdk.TypeSet, + Required: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.IsDayOfTheWeek(true), + }, + }, + }, + }, + }, + + "retention_monthly": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 1188), + }, + + "format_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.RetentionScheduleFormatDaily), + string(backup.RetentionScheduleFormatWeekly), + }, false), + }, + + "monthdays": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeInt, + ValidateFunc: validation.IntBetween(0, 28), + }, + }, + + "weeks": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.WeekOfMonthFirst), + string(backup.WeekOfMonthSecond), + string(backup.WeekOfMonthThird), + string(backup.WeekOfMonthFourth), + string(backup.WeekOfMonthLast), + }, false), + }, + }, + + "weekdays": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.IsDayOfTheWeek(true), + }, + }, + }, + }, + }, + + "retention_yearly": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 99), + }, + + "format_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.RetentionScheduleFormatDaily), + string(backup.RetentionScheduleFormatWeekly), + }, false), + }, + + "months": { + Type: pluginsdk.TypeSet, + Required: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.IsMonth(true), + }, + }, + + "monthdays": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeInt, + ValidateFunc: validation.IntBetween(0, 28), + }, + }, + + "weeks": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.WeekOfMonthFirst), + string(backup.WeekOfMonthSecond), + string(backup.WeekOfMonthThird), + string(backup.WeekOfMonthFourth), + string(backup.WeekOfMonthLast), + }, false), + }, + }, + + "weekdays": { + Type: pluginsdk.TypeSet, + Optional: true, + Set: set.HashStringIgnoreCase, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.IsDayOfTheWeek(true), + }, + }, + }, + }, + }, + + "simple_retention": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "count": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(7, 35), + }, + }, + }, + }, + }, + }, + }, + + "settings": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "time_zone": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "compression_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + + "workload_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(backup.WorkloadTypeSQLDataBase), + string(backup.WorkloadTypeSAPHanaDatabase), + }, false), + }, + } +} + +func (r BackupProtectionPolicyVMWorkloadResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r BackupProtectionPolicyVMWorkloadResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model BackupProtectionPolicyVMWorkloadModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.RecoveryServices.ProtectionPoliciesClient + subscriptionId := metadata.Client.Account.SubscriptionId + + id := parse.NewBackupPolicyID(subscriptionId, model.ResourceGroupName, model.RecoveryVaultName, model.Name) + + existing, err := client.Get(ctx, id.VaultName, id.ResourceGroup, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_backup_policy_vm_workload", id.ID()) + } + + protectionPolicy, err := expandBackupProtectionPolicyVMWorkloadProtectionPolicies(model.ProtectionPolicies, model.WorkloadType) + if err != nil { + return err + } + + properties := &backup.ProtectionPolicyResource{ + Properties: &backup.AzureVMWorkloadProtectionPolicy{ + BackupManagementType: backup.ManagementTypeBasicProtectionPolicyBackupManagementTypeAzureWorkload, + Settings: expandBackupProtectionPolicyVMWorkloadSettings(model.Settings), + SubProtectionPolicy: protectionPolicy, + WorkLoadType: backup.WorkloadType(model.WorkloadType), + }, + } + + if _, err := client.CreateOrUpdate(ctx, id.VaultName, id.ResourceGroup, id.Name, *properties); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r BackupProtectionPolicyVMWorkloadResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.RecoveryServices.ProtectionPoliciesClient + + id, err := parse.BackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model BackupProtectionPolicyVMWorkloadModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + existing, err := client.Get(ctx, id.VaultName, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + props, _ := existing.Properties.AsAzureVMWorkloadProtectionPolicy() + + if metadata.ResourceData.HasChange("settings") { + props.Settings = expandBackupProtectionPolicyVMWorkloadSettings(model.Settings) + } + + if metadata.ResourceData.HasChange("protection_policy") { + protectionPolicy, err := expandBackupProtectionPolicyVMWorkloadProtectionPolicies(model.ProtectionPolicies, model.WorkloadType) + if err != nil { + return err + } + + props.SubProtectionPolicy = protectionPolicy + } + + existing.Properties = props + + if _, err := client.CreateOrUpdate(ctx, id.VaultName, id.ResourceGroup, id.Name, existing); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r BackupProtectionPolicyVMWorkloadResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.RecoveryServices.ProtectionPoliciesClient + + id, err := parse.BackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.VaultName, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + state := BackupProtectionPolicyVMWorkloadModel{ + Name: id.Name, + ResourceGroupName: id.ResourceGroup, + RecoveryVaultName: id.VaultName, + } + + if props := resp.Properties; props != nil { + vmWorkload, _ := props.AsAzureVMWorkloadProtectionPolicy() + state.WorkloadType = string(vmWorkload.WorkLoadType) + state.Settings = flattenBackupProtectionPolicyVMWorkloadSettings(vmWorkload.Settings) + state.ProtectionPolicies = flattenBackupProtectionPolicyVMWorkloadProtectionPolicies(vmWorkload.SubProtectionPolicy) + } + + return metadata.Encode(&state) + }, + } +} + +func (r BackupProtectionPolicyVMWorkloadResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.RecoveryServices.ProtectionPoliciesClient + + id, err := parse.BackupPolicyID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.VaultName, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for the deletion of %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandBackupProtectionPolicyVMWorkloadSettings(input []Settings) *backup.Settings { + if len(input) == 0 { + return &backup.Settings{} + } + + settings := input[0] + result := &backup.Settings{ + IsCompression: utils.Bool(settings.CompressionEnabled), + } + + if settings.TimeZone != "" { + result.TimeZone = utils.String(settings.TimeZone) + } + + return result +} + +func flattenBackupProtectionPolicyVMWorkloadSettings(input *backup.Settings) []Settings { + if input == nil { + return make([]Settings, 0) + } + + result := make([]Settings, 0) + + result = append(result, Settings{ + CompressionEnabled: *input.IsCompression, + TimeZone: *input.TimeZone, + }) + + return result +} + +func expandBackupProtectionPolicyVMWorkloadProtectionPolicies(input []ProtectionPolicy, workloadType string) (*[]backup.SubProtectionPolicy, error) { + if len(input) == 0 { + return nil, nil + } + + results := make([]backup.SubProtectionPolicy, 0) + + for _, item := range input { + if workloadType == string(backup.WorkloadTypeSQLDataBase) && item.PolicyType == string(backup.PolicyTypeIncremental) { + return nil, fmt.Errorf("the Incremental backup isn't supported when `workload_type` is `SQLDataBase`") + } + + backupBlock := item.Backup[0] + + // getting this ready now because its shared between *everything*, time is... complicated for this resource + timeOfDay := backupBlock.Time + times := make([]date.Time, 0) + if timeOfDay != "" { + dateOfDay, err := time.Parse(time.RFC3339, fmt.Sprintf("2018-07-30T%s:00Z", timeOfDay)) + if err != nil { + return nil, fmt.Errorf("generating time from %q for policy): %+v", timeOfDay, err) + } + times = append(times, date.Time{Time: dateOfDay}) + } + + switch backupBlock.Frequency { + case string(backup.ScheduleRunTypeDaily): + if item.RetentionDaily == nil || len(item.RetentionDaily) == 0 { + return nil, fmt.Errorf("`retention_daily` must be set when `backup.0.frequency` is `Daily`") + } + + if weekdays := backupBlock.Weekdays; weekdays != nil && len(weekdays) > 0 { + return nil, fmt.Errorf("`backup.0.weekdays` should be not set when `backup.0.frequency` is `Daily`") + } + case string(backup.ScheduleRunTypeWeekly): + if item.RetentionDaily != nil && len(item.RetentionDaily) > 0 { + return nil, fmt.Errorf("`retention_daily` must be not set when `backup.0.frequency` is `Weekly`") + } + + if item.PolicyType != string(backup.PolicyTypeLog) && (backupBlock.Weekdays == nil || len(backupBlock.Weekdays) == 0) { + return nil, fmt.Errorf("`backup.weekdays` must be set when `policy_type` is not `Log` and `backup.frequency` is `Weekly`") + } + + if item.PolicyType == string(backup.PolicyTypeFull) && (item.RetentionWeekly == nil || len(item.RetentionWeekly) == 0) { + return nil, fmt.Errorf("`retention_weekly` must be set when `policy_type` is `Full` and `backup.frequency` is `Weekly`") + } + } + + result := backup.SubProtectionPolicy{ + PolicyType: backup.PolicyType(item.PolicyType), + SchedulePolicy: expandBackupProtectionPolicyVMWorkloadSchedulePolicy(item, times), + } + + if v, err := expandBackupProtectionPolicyVMWorkloadRetentionPolicy(item, times); err != nil { + return nil, err + } else { + result.RetentionPolicy = v + } + + results = append(results, result) + } + + return &results, nil +} + +func flattenBackupProtectionPolicyVMWorkloadProtectionPolicies(input *[]backup.SubProtectionPolicy) []ProtectionPolicy { + results := make([]ProtectionPolicy, 0) + if input == nil { + return results + } + + for _, item := range *input { + var policyType backup.PolicyType + if item.PolicyType != "" { + policyType = item.PolicyType + } + + result := ProtectionPolicy{ + PolicyType: string(policyType), + Backup: flattenBackupProtectionPolicyVMWorkloadSchedulePolicy(item.SchedulePolicy, policyType), + } + + if retentionPolicy := item.RetentionPolicy; retentionPolicy != nil { + if longTermRetentionPolicy, ok := retentionPolicy.AsLongTermRetentionPolicy(); ok { + result.RetentionDaily = flattenBackupProtectionPolicyVMWorkloadRetentionDaily(longTermRetentionPolicy.DailySchedule) + result.RetentionWeekly = flattenBackupProtectionPolicyVMWorkloadRetentionWeekly(longTermRetentionPolicy.WeeklySchedule) + result.RetentionMonthly = flattenBackupProtectionPolicyVMWorkloadRetentionMonthly(longTermRetentionPolicy.MonthlySchedule) + result.RetentionYearly = flattenBackupProtectionPolicyVMWorkloadRetentionYearly(longTermRetentionPolicy.YearlySchedule) + } else { + simpleRetentionPolicy, _ := retentionPolicy.AsSimpleRetentionPolicy() + result.SimpleRetention = flattenBackupProtectionPolicyVMWorkloadSimpleRetention(simpleRetentionPolicy.RetentionDuration) + } + } + + results = append(results, result) + } + + return results +} + +func expandBackupProtectionPolicyVMWorkloadSchedulePolicy(input ProtectionPolicy, times []date.Time) backup.BasicSchedulePolicy { + if input.PolicyType == string(backup.PolicyTypeLog) { + schedule := backup.LogSchedulePolicy{ + SchedulePolicyType: backup.SchedulePolicyTypeLogSchedulePolicy, + } + + if v := input.Backup[0].FrequencyInMinutes; v != 0 { + schedule.ScheduleFrequencyInMins = utils.Int32(v) + } + + result, _ := schedule.AsBasicSchedulePolicy() + return result + } else { + schedule := backup.SimpleSchedulePolicy{ + SchedulePolicyType: backup.SchedulePolicyTypeSimpleSchedulePolicy, + } + + backupBlock := input.Backup[0] + if backupBlock.Frequency != "" { + schedule.ScheduleRunFrequency = backup.ScheduleRunType(backupBlock.Frequency) + } + + if times != nil && len(times) > 0 { + schedule.ScheduleRunTimes = × + } + + if v := backupBlock.Weekdays; v != nil && len(v) > 0 { + days := make([]backup.DayOfWeek, 0) + for _, day := range v { + days = append(days, backup.DayOfWeek(day)) + } + schedule.ScheduleRunDays = &days + } + + result, _ := schedule.AsBasicSchedulePolicy() + return result + } +} + +func flattenBackupProtectionPolicyVMWorkloadSchedulePolicy(input backup.BasicSchedulePolicy, policyType backup.PolicyType) []Backup { + if input == nil { + return nil + } + + backupBlock := Backup{} + + if policyType == backup.PolicyTypeLog { + logSchedulePolicy, _ := input.AsLogSchedulePolicy() + + if v := logSchedulePolicy.ScheduleFrequencyInMins; v != nil { + backupBlock.FrequencyInMinutes = *v + } + } else { + simpleSchedulePolicy, _ := input.AsSimpleSchedulePolicy() + + backupBlock.Frequency = string(simpleSchedulePolicy.ScheduleRunFrequency) + + if times := simpleSchedulePolicy.ScheduleRunTimes; times != nil && len(*times) > 0 { + backupBlock.Time = (*times)[0].Format("15:04") + } + + if days := simpleSchedulePolicy.ScheduleRunDays; days != nil { + weekdays := make([]string, 0) + for _, d := range *days { + weekdays = append(weekdays, string(d)) + } + backupBlock.Weekdays = weekdays + } + } + + return []Backup{backupBlock} +} + +func expandBackupProtectionPolicyVMWorkloadRetentionPolicy(input ProtectionPolicy, times []date.Time) (backup.BasicRetentionPolicy, error) { + if input.PolicyType == string(backup.PolicyTypeFull) { + retentionPolicy := backup.LongTermRetentionPolicy{ + RetentionPolicyType: backup.RetentionPolicyTypeLongTermRetentionPolicy, + } + + if input.RetentionDaily != nil && len(input.RetentionDaily) > 0 { + retentionDaily := input.RetentionDaily[0] + + retentionPolicy.DailySchedule = &backup.DailyRetentionSchedule{ + RetentionTimes: ×, + RetentionDuration: &backup.RetentionDuration{ + Count: utils.Int32(retentionDaily.Count), + DurationType: backup.RetentionDurationTypeDays, + }, + } + } + + if input.RetentionWeekly != nil && len(input.RetentionWeekly) > 0 { + retentionWeekly := input.RetentionWeekly[0] + + retentionPolicy.WeeklySchedule = &backup.WeeklyRetentionSchedule{ + RetentionTimes: ×, + RetentionDuration: &backup.RetentionDuration{ + Count: utils.Int32(retentionWeekly.Count), + DurationType: backup.RetentionDurationTypeWeeks, + }, + } + + if v := retentionWeekly.Weekdays; v != nil && len(v) > 0 { + days := make([]backup.DayOfWeek, 0) + for _, day := range v { + days = append(days, backup.DayOfWeek(day)) + } + retentionPolicy.WeeklySchedule.DaysOfTheWeek = &days + } + } + + if input.RetentionMonthly != nil && len(input.RetentionMonthly) > 0 { + retentionMonthly := input.RetentionMonthly[0] + + if input.Backup[0].Frequency == string(backup.ScheduleRunTypeWeekly) && retentionMonthly.FormatType != string(backup.RetentionScheduleFormatWeekly) { + return nil, fmt.Errorf("`retention_monthly.format_type` must be `Weekly` when `policy_type` is `Full` and `frequency` is `Weekly`") + } + + if retentionMonthly.FormatType == string(backup.RetentionScheduleFormatDaily) && (retentionMonthly.Monthdays == nil || len(retentionMonthly.Monthdays) == 0) { + return nil, fmt.Errorf("`retention_monthly.monthdays` must be set when `retention_monthly.format_type` is `Daily`") + } + + if retentionMonthly.FormatType == string(backup.RetentionScheduleFormatWeekly) && ((retentionMonthly.Weeks == nil || len(retentionMonthly.Weeks) == 0) || (retentionMonthly.Weekdays == nil || len(retentionMonthly.Weekdays) == 0)) { + return nil, fmt.Errorf("`retention_monthly.weeks` and `retention_monthly.weekdays` must be set when `retention_monthly.format_type` is `Weekly`") + } + + retentionPolicy.MonthlySchedule = &backup.MonthlyRetentionSchedule{ + RetentionScheduleFormatType: backup.RetentionScheduleFormat(retentionMonthly.FormatType), + RetentionScheduleDaily: expandBackupProtectionPolicyVMWorkloadRetentionDailyFormat(retentionMonthly.Monthdays), + RetentionScheduleWeekly: expandBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(retentionMonthly.Weekdays, retentionMonthly.Weeks), + RetentionTimes: ×, + RetentionDuration: &backup.RetentionDuration{ + Count: utils.Int32(retentionMonthly.Count), + DurationType: backup.RetentionDurationTypeMonths, + }, + } + } + + if input.RetentionYearly != nil && len(input.RetentionYearly) > 0 { + retentionYearly := input.RetentionYearly[0] + + if input.Backup[0].Frequency == string(backup.ScheduleRunTypeWeekly) && retentionYearly.FormatType != string(backup.RetentionScheduleFormatWeekly) { + return nil, fmt.Errorf("`retention_yearly.format_type` must be `Weekly` when `policy_type` is `Full` and `frequency` is `Weekly`") + } + + if retentionYearly.FormatType == string(backup.RetentionScheduleFormatDaily) && (retentionYearly.Monthdays == nil || len(retentionYearly.Monthdays) == 0) { + return nil, fmt.Errorf("`retention_yearly.monthdays` must be set when `retention_yearly.format_type` is `Daily`") + } + + if retentionYearly.FormatType == string(backup.RetentionScheduleFormatWeekly) && ((retentionYearly.Weeks == nil || len(retentionYearly.Weeks) == 0) || (retentionYearly.Weekdays == nil || len(retentionYearly.Weekdays) == 0)) { + return nil, fmt.Errorf("`retention_yearly.weeks` and `retention_yearly.weekdays` must be set when `retention_yearly.format_type` is `Weekly`") + } + + retentionPolicy.YearlySchedule = &backup.YearlyRetentionSchedule{ + RetentionScheduleFormatType: backup.RetentionScheduleFormat(retentionYearly.FormatType), + RetentionScheduleDaily: expandBackupProtectionPolicyVMWorkloadRetentionDailyFormat(retentionYearly.Monthdays), + RetentionScheduleWeekly: expandBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(retentionYearly.Weekdays, retentionYearly.Weeks), + RetentionTimes: ×, + RetentionDuration: &backup.RetentionDuration{ + Count: utils.Int32(retentionYearly.Count), + DurationType: backup.RetentionDurationTypeYears, + }, + } + + if v := retentionYearly.Months; v != nil { + months := make([]backup.MonthOfYear, 0) + for _, month := range v { + months = append(months, backup.MonthOfYear(month)) + } + retentionPolicy.YearlySchedule.MonthsOfYear = &months + } + } + + return retentionPolicy, nil + } else { + retentionPolicy := backup.SimpleRetentionPolicy{ + RetentionPolicyType: backup.RetentionPolicyTypeSimpleRetentionPolicy, + } + + if input.SimpleRetention != nil && len(input.SimpleRetention) > 0 { + simpleRetention := input.SimpleRetention[0] + + retentionPolicy.RetentionDuration = &backup.RetentionDuration{ + Count: utils.Int32(simpleRetention.Count), + DurationType: backup.RetentionDurationTypeDays, + } + } + + return retentionPolicy, nil + } +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionDaily(input *backup.DailyRetentionSchedule) []RetentionDaily { + if input == nil { + return nil + } + + retentionDailyBlock := RetentionDaily{} + + if duration := input.RetentionDuration; duration != nil { + if v := duration.Count; v != nil { + retentionDailyBlock.Count = *v + } + } + + return []RetentionDaily{retentionDailyBlock} +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionWeekly(input *backup.WeeklyRetentionSchedule) []RetentionWeekly { + if input == nil { + return nil + } + + retentionWeeklyBlock := RetentionWeekly{} + + if duration := input.RetentionDuration; duration != nil { + if v := duration.Count; v != nil { + retentionWeeklyBlock.Count = *v + } + } + + if days := input.DaysOfTheWeek; days != nil { + weekdays := make([]string, 0) + for _, d := range *days { + weekdays = append(weekdays, string(d)) + } + retentionWeeklyBlock.Weekdays = weekdays + } + + return []RetentionWeekly{retentionWeeklyBlock} +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionMonthly(input *backup.MonthlyRetentionSchedule) []RetentionMonthly { + if input == nil { + return nil + } + + retentionMonthlyBlock := RetentionMonthly{} + + if duration := input.RetentionDuration; duration != nil { + if v := duration.Count; v != nil { + retentionMonthlyBlock.Count = *v + } + } + + if formatType := input.RetentionScheduleFormatType; formatType != "" { + retentionMonthlyBlock.FormatType = string(formatType) + } + + if weekly := input.RetentionScheduleWeekly; weekly != nil { + retentionMonthlyBlock.Weekdays, retentionMonthlyBlock.Weeks = flattenBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(weekly) + } + + if daily := input.RetentionScheduleDaily; daily != nil { + retentionMonthlyBlock.Monthdays = flattenBackupProtectionPolicyVMWorkloadRetentionDailyFormat(daily) + } + + return []RetentionMonthly{retentionMonthlyBlock} +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionYearly(input *backup.YearlyRetentionSchedule) []RetentionYearly { + if input == nil { + return nil + } + + retentionYearlyBlock := RetentionYearly{} + + if duration := input.RetentionDuration; duration != nil { + if v := duration.Count; v != nil { + retentionYearlyBlock.Count = *v + } + } + + if formatType := input.RetentionScheduleFormatType; formatType != "" { + retentionYearlyBlock.FormatType = string(formatType) + } + + if weekly := input.RetentionScheduleWeekly; weekly != nil { + retentionYearlyBlock.Weekdays, retentionYearlyBlock.Weeks = flattenBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(weekly) + } + + if v := input.MonthsOfYear; v != nil { + months := make([]string, 0) + for _, d := range *v { + months = append(months, string(d)) + } + retentionYearlyBlock.Months = months + } + + if daily := input.RetentionScheduleDaily; daily != nil { + retentionYearlyBlock.Monthdays = flattenBackupProtectionPolicyVMWorkloadRetentionDailyFormat(daily) + } + + return []RetentionYearly{retentionYearlyBlock} +} + +func flattenBackupProtectionPolicyVMWorkloadSimpleRetention(input *backup.RetentionDuration) []SimpleRetention { + if input == nil { + return nil + } + + simpleRetentionBlock := SimpleRetention{} + + if v := input.Count; v != nil { + simpleRetentionBlock.Count = *v + } + + return []SimpleRetention{simpleRetentionBlock} +} + +func expandBackupProtectionPolicyVMWorkloadRetentionDailyFormat(input []int) *backup.DailyRetentionFormat { + if input == nil || len(input) == 0 { + return nil + } + + daily := backup.DailyRetentionFormat{} + + days := make([]backup.Day, 0) + for _, item := range input { + day := backup.Day{ + Date: utils.Int32(int32((item))), + } + + if item == 0 { + day.IsLast = utils.Bool(true) + } else { + day.IsLast = utils.Bool(false) + } + + days = append(days, day) + } + daily.DaysOfTheMonth = &days + + return &daily +} + +func expandBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(weekdays, weeks []string) *backup.WeeklyRetentionFormat { + if (weekdays == nil && weeks == nil) || (len(weekdays) == 0 && len(weeks) == 0) { + return nil + } + + weekly := backup.WeeklyRetentionFormat{} + + if weekdays != nil && len(weekdays) > 0 { + weekdaysBlock := make([]backup.DayOfWeek, 0) + for _, day := range weekdays { + weekdaysBlock = append(weekdaysBlock, backup.DayOfWeek(day)) + } + weekly.DaysOfTheWeek = &weekdaysBlock + } + + if weeks != nil && len(weeks) > 0 { + weeksBlock := make([]backup.WeekOfMonth, 0) + for _, week := range weeks { + weeksBlock = append(weeksBlock, backup.WeekOfMonth(week)) + } + weekly.WeeksOfTheMonth = &weeksBlock + } + + return &weekly +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionWeeklyFormat(input *backup.WeeklyRetentionFormat) (weekdays, weeks []string) { + if v := input.DaysOfTheWeek; v != nil { + days := make([]string, 0) + for _, d := range *v { + days = append(days, string(d)) + } + weekdays = days + } + + if v := input.WeeksOfTheMonth; v != nil { + days := make([]string, 0) + for _, d := range *v { + days = append(days, string(d)) + } + weeks = days + } + + return weekdays, weeks +} + +func flattenBackupProtectionPolicyVMWorkloadRetentionDailyFormat(input *backup.DailyRetentionFormat) []int { + result := make([]int, 0) + + if days := input.DaysOfTheMonth; days != nil { + for _, d := range *days { + result = append(result, int(*d.Date)) + } + } + + return result +} diff --git a/internal/services/recoveryservices/backup_policy_vm_workload_resource_test.go b/internal/services/recoveryservices/backup_policy_vm_workload_resource_test.go new file mode 100644 index 000000000000..c17fae6af74c --- /dev/null +++ b/internal/services/recoveryservices/backup_policy_vm_workload_resource_test.go @@ -0,0 +1,319 @@ +package recoveryservices_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type BackupProtectionPolicyVMWorkloadResource struct{} + +func TestAccBackupProtectionPolicyVMWorkload_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_backup_policy_vm_workload", "test") + r := BackupProtectionPolicyVMWorkloadResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccBackupProtectionPolicyVMWorkload_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_backup_policy_vm_workload", "test") + r := BackupProtectionPolicyVMWorkloadResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (t BackupProtectionPolicyVMWorkloadResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.BackupPolicyID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.RecoveryServices.ProtectionPoliciesClient.Get(ctx, id.VaultName, id.ResourceGroup, id.Name) + if err != nil { + return nil, fmt.Errorf("reading Recovery Service Protection Policy (%s): %+v", id.String(), err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (r BackupProtectionPolicyVMWorkloadResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-bpvmw-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-rsv-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Standard" + soft_delete_enabled = false +} + +resource "azurerm_backup_policy_vm_workload" "test" { + name = "acctest-bpvmw-%d" + resource_group_name = azurerm_resource_group.test.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + + workload_type = "SQLDataBase" + + settings { + time_zone = "UTC" + compression_enabled = false + } + + protection_policy { + policy_type = "Full" + + backup { + frequency = "Daily" + time = "15:00" + } + + retention_daily { + count = 8 + } + + retention_monthly { + format_type = "Daily" + count = 10 + monthdays = [27, 28] + } + + retention_yearly { + format_type = "Daily" + count = 10 + months = ["February"] + monthdays = [27, 28] + } + } + + protection_policy { + policy_type = "Log" + + backup { + frequency_in_minutes = 15 + } + + simple_retention { + count = 8 + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r BackupProtectionPolicyVMWorkloadResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-bpvmw-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-rsv-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Standard" + soft_delete_enabled = false +} + +resource "azurerm_backup_policy_vm_workload" "test" { + name = "acctest-bpvmw-%d" + resource_group_name = azurerm_resource_group.test.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + + workload_type = "SAPHanaDatabase" + + settings { + time_zone = "UTC" + compression_enabled = true + } + + protection_policy { + policy_type = "Full" + + backup { + frequency = "Weekly" + time = "15:00" + weekdays = ["Monday", "Tuesday"] + } + + retention_weekly { + weekdays = ["Monday", "Tuesday"] + count = 4 + } + + retention_monthly { + format_type = "Weekly" + weeks = ["Third"] + weekdays = ["Tuesday"] + count = 10 + } + + retention_yearly { + format_type = "Weekly" + months = ["May", "February"] + weeks = ["Third"] + weekdays = ["Tuesday"] + count = 8 + } + } + + protection_policy { + policy_type = "Incremental" + + backup { + frequency = "Weekly" + weekdays = ["Saturday", "Friday"] + time = "23:00" + } + + simple_retention { + count = 11 + } + } + + protection_policy { + policy_type = "Log" + + backup { + frequency_in_minutes = 15 + } + + simple_retention { + count = 8 + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r BackupProtectionPolicyVMWorkloadResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-bpvmw-%d" + location = "%s" +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-rsv-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Standard" + soft_delete_enabled = false +} + +resource "azurerm_backup_policy_vm_workload" "test" { + name = "acctest-bpvmw-%d" + resource_group_name = azurerm_resource_group.test.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + + workload_type = "SAPHanaDatabase" + + settings { + time_zone = "Pacific Standard Time" + compression_enabled = false + } + + protection_policy { + policy_type = "Full" + + backup { + frequency = "Weekly" + time = "16:00" + weekdays = ["Tuesday", "Thursday"] + } + + retention_weekly { + weekdays = ["Tuesday", "Thursday"] + count = 5 + } + + retention_monthly { + format_type = "Weekly" + weeks = ["Third", "First"] + weekdays = ["Tuesday", "Thursday"] + count = 11 + } + + retention_yearly { + format_type = "Weekly" + months = ["July", "February"] + weeks = ["Third", "First"] + weekdays = ["Tuesday", "Thursday"] + count = 9 + } + } + + protection_policy { + policy_type = "Differential" + + backup { + frequency = "Weekly" + weekdays = ["Saturday", "Sunday"] + time = "17:00" + } + + simple_retention { + count = 12 + } + } + + protection_policy { + policy_type = "Log" + + backup { + frequency_in_minutes = 30 + } + + simple_retention { + count = 9 + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/recoveryservices/registration.go b/internal/services/recoveryservices/registration.go index 1554272cf34c..d8ec8352fe30 100644 --- a/internal/services/recoveryservices/registration.go +++ b/internal/services/recoveryservices/registration.go @@ -7,12 +7,25 @@ import ( type Registration struct{} -var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +var ( + _ sdk.TypedServiceRegistration = Registration{} + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +) func (r Registration) AssociatedGitHubLabel() string { return "service/recovery-services" } +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{} +} + +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + BackupProtectionPolicyVMWorkloadResource{}, + } +} + // Name is the name of this Service func (r Registration) Name() string { return "Recovery Services" diff --git a/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go b/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go index bd15cf701759..539cba753f20 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -186,6 +187,63 @@ func resourceSiteRecoveryReplicatedVM() *pluginsdk.Resource { ValidateFunc: azure.ValidateResourceID, DiffSuppressFunc: suppress.CaseDifference, }, + + "target_disk_encryption": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "disk_encryption_key": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_url": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.NestedItemId, + }, + + "vault_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.VaultID, + }, + }, + }, + }, + "key_encryption_key": { + Type: pluginsdk.TypeList, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_url": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.NestedItemId, + }, + + "vault_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.VaultID, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -291,6 +349,7 @@ func resourceSiteRecoveryReplicatedItemCreate(d *pluginsdk.ResourceData, meta in RecoveryReplicaDiskAccountType: &targetReplicaDiskType, RecoveryTargetDiskAccountType: &targetDiskType, RecoveryDiskEncryptionSetID: &targetEncryptionDiskSetID, + DiskEncryptionInfo: expandTargetDiskEncryption(diskInput["target_disk_encryption"].([]interface{})), }) } @@ -388,6 +447,7 @@ func resourceSiteRecoveryReplicatedItemUpdateInternal(ctx context.Context, d *pl DiskID: &diskId, RecoveryReplicaDiskAccountType: &targetReplicaDiskType, RecoveryTargetDiskAccountType: &targetDiskType, + DiskEncryptionInfo: expandTargetDiskEncryption(diskInput["target_disk_encryption"].([]interface{})), }) } @@ -515,6 +575,8 @@ func resourceSiteRecoveryReplicatedItemRead(d *pluginsdk.ResourceData, meta inte } diskOutput["target_disk_encryption_set_id"] = recoveryEncryptionSetId + diskOutput["target_disk_encryption"] = flattenTargetDiskEncryption(disk) + disksOutput = append(disksOutput, diskOutput) } d.Set("managed_disk", pluginsdk.NewSet(resourceSiteRecoveryReplicatedVMDiskHash, disksOutput)) @@ -653,3 +715,75 @@ func waitForReplicationToBeHealthyRefreshFunc(d *pluginsdk.ResourceData, meta in return resp, *resp.Properties.ReplicationHealth, nil } } + +func expandTargetDiskEncryption(diskEncryptionInfoList []interface{}) *siterecovery.DiskEncryptionInfo { + if len(diskEncryptionInfoList) == 0 { + return &siterecovery.DiskEncryptionInfo{} + } + diskEncryptionInfoMap := diskEncryptionInfoList[0].(map[string]interface{}) + + dek := diskEncryptionInfoMap["disk_encryption_key"].([]interface{})[0].(map[string]interface{}) + diskEncryptionInfo := &siterecovery.DiskEncryptionInfo{ + DiskEncryptionKeyInfo: &siterecovery.DiskEncryptionKeyInfo{ + SecretIdentifier: utils.String(dek["secret_url"].(string)), + KeyVaultResourceArmID: utils.String(dek["vault_id"].(string)), + }, + } + + if keyEncryptionKey := diskEncryptionInfoMap["key_encryption_key"].([]interface{}); len(keyEncryptionKey) > 0 { + kek := keyEncryptionKey[0].(map[string]interface{}) + diskEncryptionInfo.KeyEncryptionKeyInfo = &siterecovery.KeyEncryptionKeyInfo{ + KeyIdentifier: utils.String(kek["key_url"].(string)), + KeyVaultResourceArmID: utils.String(kek["vault_id"].(string)), + } + } + + return diskEncryptionInfo +} + +func flattenTargetDiskEncryption(disk siterecovery.A2AProtectedManagedDiskDetails) []interface{} { + secretUrl := "" + dekVaultId := "" + keyUrl := "" + kekVaultId := "" + + if disk.SecretIdentifier != nil { + secretUrl = *disk.SecretIdentifier + } + if disk.DekKeyVaultArmID != nil { + dekVaultId = *disk.DekKeyVaultArmID + } + if disk.KeyIdentifier != nil { + keyUrl = *disk.KeyIdentifier + } + if disk.KekKeyVaultArmID != nil { + kekVaultId = *disk.KekKeyVaultArmID + } + + if secretUrl == "" && dekVaultId == "" && keyUrl == "" && kekVaultId == "" { + return []interface{}{} + } + + diskEncryptionKeys := make([]interface{}, 0) + if secretUrl != "" || dekVaultId != "" { + diskEncryptionKeys = append(diskEncryptionKeys, map[string]interface{}{ + "secret_url": secretUrl, + "vault_id": dekVaultId, + }) + } + + keyEncryptionKeys := make([]interface{}, 0) + if keyUrl != "" || kekVaultId != "" { + keyEncryptionKeys = append(keyEncryptionKeys, map[string]interface{}{ + "key_url": keyUrl, + "vault_id": kekVaultId, + }) + } + + return []interface{}{ + map[string]interface{}{ + "disk_encryption_key": diskEncryptionKeys, + "key_encryption_key": keyEncryptionKeys, + }, + } +} diff --git a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go index 7c11849d169a..be81e6c00052 100644 --- a/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go +++ b/internal/services/recoveryservices/site_recovery_replicated_vm_resource_test.go @@ -60,6 +60,21 @@ func TestAccSiteRecoveryReplicatedVm_zone2zone(t *testing.T) { }) } +func TestAccSiteRecoveryReplicatedVm_targetDiskEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_site_recovery_replicated_vm", "test") + r := SiteRecoveryReplicatedVmResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.targetDiskEncryption(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (SiteRecoveryReplicatedVmResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -882,6 +897,265 @@ resource "azurerm_site_recovery_replicated_vm" "test" { `, data.RandomInteger, data.Locations.Primary, data.Locations.Secondary) } +func (SiteRecoveryReplicatedVmResource) targetDiskEncryption(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + purge_soft_deleted_secrets_on_destroy = false + } + } +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-recovery-%[1]d-1" + location = "%[2]s" +} +resource "azurerm_resource_group" "test2" { + name = "acctestRG-recovery-%[1]d-2" + location = "%[3]s" +} + +resource "azurerm_key_vault" "test1" { + name = "acckv-%[1]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" + enabled_for_disk_encryption = true + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "service-principal" { + key_vault_id = azurerm_key_vault.test1.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "Create", + "Delete", + "Get", + "Update", + ] + + secret_permissions = [ + "Get", + "Delete", + "Set", + ] +} + +resource "azurerm_key_vault_key" "test1" { + name = "examplekey" + key_vault_id = azurerm_key_vault.test1.id + key_type = "RSA" + key_size = 3072 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] + + depends_on = [ + azurerm_key_vault_access_policy.service-principal + ] +} + +resource "azurerm_recovery_services_vault" "test" { + name = "acctest-vault-%[1]d" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name + sku = "Standard" + soft_delete_enabled = false +} +resource "azurerm_site_recovery_fabric" "test1" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + name = "acctest-fabric1-%[1]d" + location = azurerm_resource_group.test.location +} +resource "azurerm_site_recovery_protection_container" "test1" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + recovery_fabric_name = azurerm_site_recovery_fabric.test1.name + name = "acctest-protection-cont1-%[1]d" +} +resource "azurerm_site_recovery_protection_container" "test2" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + recovery_fabric_name = azurerm_site_recovery_fabric.test1.name + name = "acctest-protection-cont2-t-%[1]d" +} +resource "azurerm_site_recovery_replication_policy" "test" { + resource_group_name = azurerm_resource_group.test2.name + recovery_vault_name = azurerm_recovery_services_vault.test.name + name = "acctest-policy-%[1]d" + recovery_point_retention_in_minutes = 24 * 60 + application_consistent_snapshot_frequency_in_minutes = 4 * 60 +} +resource "azurerm_site_recovery_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_site_recovery_fabric.test1.name + recovery_source_protection_container_name = azurerm_site_recovery_protection_container.test1.name + recovery_target_protection_container_id = azurerm_site_recovery_protection_container.test2.id + recovery_replication_policy_id = azurerm_site_recovery_replication_policy.test.id + name = "mapping-%[1]d" +} +resource "azurerm_virtual_network" "test1" { + name = "net-%[1]d" + resource_group_name = azurerm_resource_group.test.name + address_space = ["192.168.1.0/24"] + location = azurerm_site_recovery_fabric.test1.location +} +resource "azurerm_subnet" "test1" { + name = "snet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test1.name + address_prefixes = ["192.168.1.0/24"] +} +resource "azurerm_network_interface" "test" { + name = "vm-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + ip_configuration { + name = "vm-%[1]d" + subnet_id = azurerm_subnet.test1.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_windows_virtual_machine" "vm" { + name = "acctvm%[4]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_D2s_v3" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + zone = "1" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2022-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } +} + +resource "azurerm_virtual_machine_extension" "test" { + name = "AzureDiskEncryption" + publisher = "Microsoft.Azure.Security" + type = "AzureDiskEncryption" + type_handler_version = "2.2" + auto_upgrade_minor_version = false + virtual_machine_id = azurerm_windows_virtual_machine.vm.id + + settings = < skuWeight[new.(string)] + } + return false + }), + ), } } diff --git a/internal/services/redis/redis_cache_resource_test.go b/internal/services/redis/redis_cache_resource_test.go index 411212a21910..f77f76f76cb8 100644 --- a/internal/services/redis/redis_cache_resource_test.go +++ b/internal/services/redis/redis_cache_resource_test.go @@ -496,6 +496,27 @@ func TestAccRedisCache_identity(t *testing.T) { }) } +func TestAccRedisCache_SkuDowngrade(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_redis_cache", "test") + r := RedisCacheResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.standard(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.SkuDowngrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + }) +} + func (t RedisCacheResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.CacheID(state.ID) if err != nil { @@ -1307,6 +1328,35 @@ resource "azurerm_redis_cache" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger) } +func (RedisCacheResource) SkuDowngrade(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_redis_cache" "test" { + name = "acctestRedis-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + capacity = 1 + family = "C" + sku_name = "Basic" + enable_non_ssl_port = false + redis_configuration { + } + + tags = { + environment = "production" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func testCheckSSLInConnectionString(resourceName string, propertyName string, requireSSL bool) acceptance.TestCheckFunc { return func(s *acceptance.State) error { // Ensure we have enough information in state to look up in API diff --git a/internal/services/resource/resource_group_template_deployment_resource_test.go b/internal/services/resource/resource_group_template_deployment_resource_test.go index 415c4566f47d..cd9cd2835551 100644 --- a/internal/services/resource/resource_group_template_deployment_resource_test.go +++ b/internal/services/resource/resource_group_template_deployment_resource_test.go @@ -72,7 +72,24 @@ func TestAccResourceGroupTemplateDeployment_singleItemIncorrectCasing(t *testing check.That(data.ResourceName).ExistsInAzure(r), ), }, + }) +} + +func TestAccResourceGroupTemplateDeployment_inconsistentProviderCasing(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_resource_group_template_deployment", "test") + r := ResourceGroupTemplateDeploymentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.inconsistentProviderCasing(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, data.ImportStep(), + { // delete item + Config: r.inconsistentProviderCasingEmpty(data), + }, }) } @@ -452,6 +469,83 @@ PARAM `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, value) } +func (ResourceGroupTemplateDeploymentResource) inconsistentProviderCasing(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = %q +} + +resource "azurerm_resource_group_template_deployment" "test" { + name = "acctest" + resource_group_name = azurerm_resource_group.test.name + deployment_mode = "Complete" + + template_content = <