From 33b5a7fd18b4695d38e322de1700949caf83fd86 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 4 Jun 2020 09:19:00 +0100 Subject: [PATCH] refactor, tests, linting, docs --- .teamcity/components/generated/services.kt | 1 + .../internal/services/blueprints/blueprint.go | 13 --- .../blueprint_assignment_resource.go | 27 +++--- .../blueprint_published_version_datasource.go | 27 +++--- .../blueprint_assignment_resource_test.go | 59 +++++++++++- .../blueprint_definition_data_source_test.go | 12 +-- ...rint_published_version_data_source_test.go | 9 +- .../services/blueprints/validate/blueprint.go | 3 +- .../docs/d/blueprint_definition.html.markdown | 10 +-- .../blueprint_published_version.html.markdown | 4 +- .../docs/r/blueprint_assignment.html.markdown | 90 ++++++++++++++++++- 11 files changed, 194 insertions(+), 61 deletions(-) diff --git a/.teamcity/components/generated/services.kt b/.teamcity/components/generated/services.kt index 12da944f4e5b..6fb6df7bc9bf 100644 --- a/.teamcity/components/generated/services.kt +++ b/.teamcity/components/generated/services.kt @@ -10,6 +10,7 @@ var services = mapOf( "authorization" to "Authorization", "automation" to "Automation", "batch" to "Batch", + "blueprints" to "Blueprints", "bot" to "Bot", "cdn" to "CDN", "cognitive" to "Cognitive Services", diff --git a/azurerm/internal/services/blueprints/blueprint.go b/azurerm/internal/services/blueprints/blueprint.go index f46fbf60ae80..f060cbf9ece9 100644 --- a/azurerm/internal/services/blueprints/blueprint.go +++ b/azurerm/internal/services/blueprints/blueprint.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/blueprints/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/msi/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -210,15 +209,3 @@ func flattenArmBlueprintAssignmentResourceGroups(input map[string]*blueprint.Res return string(b), nil } - -func splitPublishedVersionID(input string) (blueprintID, versionName string) { - versionID, err := parse.VersionID(input) - if err != nil { - return "", "" - } - - versionName = versionID.Name - blueprintID = fmt.Sprintf("/%s/providers/Microsoft.Blueprint/blueprints/%s", versionID.Scope, versionID.Blueprint) - - return -} diff --git a/azurerm/internal/services/blueprints/blueprint_assignment_resource.go b/azurerm/internal/services/blueprints/blueprint_assignment_resource.go index c1a7cf0004d6..8f69be7d96a0 100644 --- a/azurerm/internal/services/blueprints/blueprint_assignment_resource.go +++ b/azurerm/internal/services/blueprints/blueprint_assignment_resource.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/blueprints/parse" @@ -57,20 +58,6 @@ func resourceArmBlueprintAssignment() *schema.Resource { "identity": ManagedIdentitySchema(), - //"blueprint_id": { - // Type: schema.TypeString, - // Optional: true, - // Computed: true, - // ValidateFunc: validate.BlueprintID, - //}, - // - //"version_name": { - // Type: schema.TypeString, - // Optional: true, - // Computed: true, - // ValidateFunc: validation.StringIsNotEmpty, - //}, - // "version_id": { Type: schema.TypeString, Required: true, @@ -148,6 +135,18 @@ func resourceArmBlueprintAssignmentCreateUpdate(d *schema.ResourceData, meta int blueprintId := d.Get("version_id").(string) targetScope := d.Get("target_subscription_id").(string) + if d.IsNewResource() { + resp, err := client.Get(ctx, targetScope, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("failure checking for existing Blueprint Assignment %q in scope %q", name, targetScope) + } + } + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_blueprint_assignment", *resp.ID) + } + } + assignment := blueprint.Assignment{ AssignmentProperties: &blueprint.AssignmentProperties{ BlueprintID: utils.String(blueprintId), // This is mislabeled - The ID is that of the Published Version, not just the Blueprint diff --git a/azurerm/internal/services/blueprints/blueprint_published_version_datasource.go b/azurerm/internal/services/blueprints/blueprint_published_version_datasource.go index fdb856dd3238..6baf3e53ba1d 100644 --- a/azurerm/internal/services/blueprints/blueprint_published_version_datasource.go +++ b/azurerm/internal/services/blueprints/blueprint_published_version_datasource.go @@ -2,14 +2,14 @@ package blueprints import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" - mgValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/validate" "time" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + mgValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -23,6 +23,12 @@ func dataSourceArmBlueprintPublishedVersion() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "blueprint_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "scope_id": { Type: schema.TypeString, Required: true, @@ -32,12 +38,6 @@ func dataSourceArmBlueprintPublishedVersion() *schema.Resource { ), }, - "blueprint_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validate.NoEmptyStrings, - }, - "version": { Type: schema.TypeString, Required: true, @@ -45,13 +45,12 @@ func dataSourceArmBlueprintPublishedVersion() *schema.Resource { }, // Computed - - "type": { + "description": { Type: schema.TypeString, Computed: true, }, - "time_created": { + "display_name": { Type: schema.TypeString, Computed: true, }, @@ -66,12 +65,12 @@ func dataSourceArmBlueprintPublishedVersion() *schema.Resource { Computed: true, }, - "display_name": { + "time_created": { Type: schema.TypeString, Computed: true, }, - "description": { + "type": { Type: schema.TypeString, Computed: true, }, diff --git a/azurerm/internal/services/blueprints/tests/blueprint_assignment_resource_test.go b/azurerm/internal/services/blueprints/tests/blueprint_assignment_resource_test.go index ceb670372a7a..5f7ed1ce18fd 100644 --- a/azurerm/internal/services/blueprints/tests/blueprint_assignment_resource_test.go +++ b/azurerm/internal/services/blueprints/tests/blueprint_assignment_resource_test.go @@ -32,6 +32,28 @@ func TestAccBlueprintAssignment_basic(t *testing.T) { }) } +func TestAccBlueprintAssignment_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_blueprint_assignment", "test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMBlueprintAssignmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBlueprintAssignment_basic(data, "testAcc_basicSubscription", "v0.1_testAcc"), + Check: resource.ComposeTestCheckFunc( + testCheckBlueprintAssignmentExists(data.ResourceName), + ), + }, + { + Config: testAccBlueprintAssignment_requiresImport(data, "testAcc_basicSubscription", "v0.1_testAcc"), + ExpectError: acceptance.RequiresImportError("azurerm_blueprint_assignment"), + }, + }, + }) +} + // Scenario: BP with RG's, locking and parameters/policies stored at Subscription, applied to subscription func TestAccBlueprintAssignment_subscriptionComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_blueprint_assignment", "test") @@ -123,8 +145,10 @@ func testCheckAzureRMBlueprintAssignmentDestroy(s *terraform.State) error { } func testAccBlueprintAssignment_basic(data acceptance.TestData, bpName string, version string) string { + subscription := data.Client().SubscriptionIDAlt return fmt.Sprintf(` provider "azurerm" { + subscription_id = "%s" features {} } @@ -175,15 +199,18 @@ resource "azurerm_blueprint_assignment" "test" { azurerm_role_assignment.test ] } -`, bpName, version, data.RandomInteger, data.RandomInteger, data.Locations.Primary) +`, subscription, bpName, version, data.RandomInteger, data.RandomInteger, data.Locations.Primary) } // This test config creates a UM-MSI and assigns Owner to the target subscription. This is necessary due to the changes // the referenced Blueprint Version needs to make to successfully apply. If the test does not exit cleanly, "dangling" // resources can include the Role Assignment(s) at the Subscription, which will need to be removed func testAccBlueprintAssignment_subscriptionComplete(data acceptance.TestData, bpName string, version string) string { + subscription := data.Client().SubscriptionIDAlt + return fmt.Sprintf(` provider "azurerm" { + subscription_id = "%s" features {} } @@ -192,7 +219,7 @@ data "azurerm_client_config" "current" {} data "azurerm_subscription" "test" {} data "azurerm_blueprint_definition" "test" { - name = "%s" + name = "%s" scope_id = data.azurerm_subscription.test.id } @@ -267,7 +294,7 @@ resource "azurerm_blueprint_assignment" "test" { azurerm_role_assignment.owner ] } -`, bpName, version, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, subscription, bpName, version, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } func testAccBlueprintAssignment_rootManagementGroup(data acceptance.TestData, bpName string, version string) string { @@ -340,3 +367,29 @@ resource "azurerm_blueprint_assignment" "test" { } `, bpName, version, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Primary) } + +func testAccBlueprintAssignment_requiresImport(data acceptance.TestData, bpName string, version string) string { + template := testAccBlueprintAssignment_basic(data, bpName, version) + + return fmt.Sprintf(` +%s + +resource "azurerm_blueprint_assignment" "import" { + name = azurerm_blueprint_assignment.test.name + target_subscription_id = azurerm_blueprint_assignment.test.target_subscription_id + version_id = azurerm_blueprint_assignment.test.version_id + location = azurerm_blueprint_assignment.test.location + + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id] + } + + depends_on = [ + azurerm_role_assignment.test + ] +} + + +`, template) +} diff --git a/azurerm/internal/services/blueprints/tests/blueprint_definition_data_source_test.go b/azurerm/internal/services/blueprints/tests/blueprint_definition_data_source_test.go index 42c65e537574..2e9ad607931c 100644 --- a/azurerm/internal/services/blueprints/tests/blueprint_definition_data_source_test.go +++ b/azurerm/internal/services/blueprints/tests/blueprint_definition_data_source_test.go @@ -17,7 +17,7 @@ func TestAccDataSourceBlueprintDefinition_basic(t *testing.T) { Providers: acceptance.SupportedProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceBlueprintDefinition_basic(), + Config: testAccDataSourceBlueprintDefinition_basic(data), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(data.ResourceName, "description", "Acceptance Test stub for Blueprints at Subscription"), resource.TestCheckResourceAttr(data.ResourceName, "name", "testAcc_basicSubscription"), @@ -71,9 +71,11 @@ func TestAccDataSourceBlueprintDefinition_basicAtChildManagementGroup(t *testing }) } -func testAccDataSourceBlueprintDefinition_basic() string { - return ` +func testAccDataSourceBlueprintDefinition_basic(data acceptance.TestData) string { + subscription := data.Client().SubscriptionIDAlt + return fmt.Sprintf(` provider "azurerm" { + subscription_id = "%s" features {} } @@ -84,7 +86,7 @@ data "azurerm_blueprint_definition" "test" { scope_id = data.azurerm_subscription.current.id } -` +`, subscription) } func testAccDataSourceBlueprintDefinition_basicAtManagementGroup(managementGroup string) string { @@ -99,7 +101,7 @@ data "azurerm_management_group" "test" { data "azurerm_blueprint_definition" "test" { name = "testAcc_staticStubManagementGroup" - scope_id = data.azurerm_management_group.test.id + scope_id = data.azurerm_management_group.test.id } `, managementGroup) diff --git a/azurerm/internal/services/blueprints/tests/blueprint_published_version_data_source_test.go b/azurerm/internal/services/blueprints/tests/blueprint_published_version_data_source_test.go index 2fc9f2612e39..31f35f93c3aa 100644 --- a/azurerm/internal/services/blueprints/tests/blueprint_published_version_data_source_test.go +++ b/azurerm/internal/services/blueprints/tests/blueprint_published_version_data_source_test.go @@ -17,7 +17,7 @@ func TestAccDataSourceBlueprintPublishedVersion_atSubscription(t *testing.T) { Providers: acceptance.SupportedProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceBlueprintPublishedVersion_atSubscription("testAcc_basicSubscription", "v0.1_testAcc"), + Config: testAccDataSourceBlueprintPublishedVersion_atSubscription(data, "testAcc_basicSubscription", "v0.1_testAcc"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(data.ResourceName, "target_scope", "subscription"), resource.TestCheckResourceAttr(data.ResourceName, "description", "Acceptance Test stub for Blueprints at Subscription"), @@ -69,9 +69,12 @@ func TestAccDataSourceBlueprintPublishedVersion_atChildManagementGroup(t *testin }) } -func testAccDataSourceBlueprintPublishedVersion_atSubscription(bpName, version string) string { +func testAccDataSourceBlueprintPublishedVersion_atSubscription(data acceptance.TestData, bpName string, version string) string { + subscription := data.Client().SubscriptionIDAlt + return fmt.Sprintf(` provider "azurerm" { + subscription_id = "%s" features {} } @@ -82,7 +85,7 @@ data "azurerm_blueprint_published_version" "test" { blueprint_name = "%s" version = "%s" } -`, bpName, version) +`, subscription, bpName, version) } func testAccDataSourceBlueprintPublishedVersion_atRootManagementGroup(bpName, version string) string { diff --git a/azurerm/internal/services/blueprints/validate/blueprint.go b/azurerm/internal/services/blueprints/validate/blueprint.go index e25de337c686..5955facb88c1 100644 --- a/azurerm/internal/services/blueprints/validate/blueprint.go +++ b/azurerm/internal/services/blueprints/validate/blueprint.go @@ -2,8 +2,9 @@ package validate import ( "fmt" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/blueprints/parse" "regexp" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/blueprints/parse" ) func BlueprintID(i interface{}, k string) (warnings []string, errors []error) { diff --git a/website/docs/d/blueprint_definition.html.markdown b/website/docs/d/blueprint_definition.html.markdown index a36bed0a082b..537699ebae82 100644 --- a/website/docs/d/blueprint_definition.html.markdown +++ b/website/docs/d/blueprint_definition.html.markdown @@ -21,7 +21,7 @@ data "azurerm_management_group" "root" { data "azurerm_blueprint_definition" "example" { name = "exampleManagementGroupBP" - scope_id = data.azurerm_management_group.root.id + scope_id = data.azurerm_management_group.root.id } ``` @@ -36,15 +36,15 @@ data "azurerm_blueprint_definition" "example" { * `id` - The Azure Resource ID of the Blueprint Definition. -* `target_scope` - The target scope. +* `description` - The description of the Blueprint Definition. * `display_name` - The display name of the Blueprint Definition. -* `description` - The description of the Blueprint Definition. +* `last_modified` - The timestamp of when this last modification was saved to the Blueprint Definition. -* `time_created` - The timestamp of when this Blueprint Definition was created. +* `target_scope` - The target scope. -* `last_modified` - The timestamp of when this last modification was saved to the Blueprint Definition. +* `time_created` - The timestamp of when this Blueprint Definition was created. * `versions` - A list of versions published for this Blueprint Definition. diff --git a/website/docs/d/blueprint_published_version.html.markdown b/website/docs/d/blueprint_published_version.html.markdown index dcb0340e8dc6..aab0cbb9fd3f 100644 --- a/website/docs/d/blueprint_published_version.html.markdown +++ b/website/docs/d/blueprint_published_version.html.markdown @@ -24,10 +24,10 @@ data "azurerm_blueprint_published_version" "test" { ## Argument Reference -* `scope_id` - (Required) The Resource ID of the scope where the Blueprint Definition is stored. This will be with either a Subscription ID or Management Group ID. - * `blueprint_name` - (Required) The name of the Blueprint Definition +* `scope_id` - (Required) The Resource ID of the scope where the Blueprint Definition is stored. This will be with either a Subscription ID or Management Group ID. + * `version` - (Required) The Version name of the Published Version of the Blueprint Definition diff --git a/website/docs/r/blueprint_assignment.html.markdown b/website/docs/r/blueprint_assignment.html.markdown index 20253ddec8a0..1ce2e83923b9 100644 --- a/website/docs/r/blueprint_assignment.html.markdown +++ b/website/docs/r/blueprint_assignment.html.markdown @@ -11,6 +11,94 @@ description: |- Manages a Blueprint Assignment resource ## Example Usage +```hcl +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" {} + +data "azurerm_subscription" "example" {} + +data "azurerm_blueprint_definition" "example" { + name = "exampleBlueprint" + scope_id = data.azurerm_subscription.example.id +} + +data "azurerm_blueprint_published_version" "example" { + scope_id = data.azurerm_blueprint_definition.example.scope_id + blueprint_name = data.azurerm_blueprint_definition.example.name + version = "v1.0.0" +} + +resource "azurerm_resource_group" "example" { + name = "exampleRG-bp" + location = "westeurope" + + tags = { + Environment = "example" + } +} + +resource "azurerm_user_assigned_identity" "example" { + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + name = "bp-user-example" +} + +resource "azurerm_role_assignment" "operator" { + scope = data.azurerm_subscription.example.id + role_definition_name = "Blueprint Operator" + principal_id = azurerm_user_assigned_identity.example.principal_id +} + +resource "azurerm_role_assignment" "owner" { + scope = data.azurerm_subscription.example.id + role_definition_name = "Owner" + principal_id = azurerm_user_assigned_identity.example.principal_id +} + +resource "azurerm_blueprint_assignment" "example" { + name = "testAccBPAssignment" + target_subscription_id = data.azurerm_subscription.example.id + version_id = data.azurerm_blueprint_published_version.example.id + location = azurerm_resource_group.example.location + + lock_mode = "AllResourcesDoNotDelete" + + lock_exclude_principals = [ + data.azurerm_client_config.current.object_id, + ] + + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.example.id] + } + + resource_groups = < **NOTE:** Improperly formatted JSON, or missing values required by a Blueprint will cause the assignment to fail. -* `lock_mode` - (Optional) The locking mode of the Blueprint Assignment. One of `None`, `AllResourcesReadOnly`, or `AlResourcesDoNotDelete`. +* `lock_mode` - (Optional) The locking mode of the Blueprint Assignment. One of `None` (Default), `AllResourcesReadOnly`, or `AlResourcesDoNotDelete`. * `lock_exclude_principals` - (Optional) a list of up to 5 Principal IDs that are permitted to bypass the locks applied by the Blueprint.