From 415baf26db175b7bb0cee569f80d63e1fa66a8c5 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Sat, 8 May 2021 13:03:19 +0800 Subject: [PATCH] New resource: azurerm_tenant_configuration --- .../internal/services/portal/client/client.go | 9 +- .../portal/parse/tenant_configuration.go | 53 ++++++++ .../portal/parse/tenant_configuration_test.go | 80 ++++++++++++ .../portal_tenant_configuration_resource.go | 121 ++++++++++++++++++ ...rtal_tenant_configuration_resource_test.go | 81 ++++++++++++ .../internal/services/portal/registration.go | 3 +- .../internal/services/portal/resourceids.go | 1 + .../validate/tenant_configuration_id.go | 23 ++++ .../validate/tenant_configuration_id_test.go | 52 ++++++++ .../docs/r/tenant_configuration.html.markdown | 52 ++++++++ 10 files changed, 472 insertions(+), 3 deletions(-) create mode 100644 azurerm/internal/services/portal/parse/tenant_configuration.go create mode 100644 azurerm/internal/services/portal/parse/tenant_configuration_test.go create mode 100644 azurerm/internal/services/portal/portal_tenant_configuration_resource.go create mode 100644 azurerm/internal/services/portal/portal_tenant_configuration_resource_test.go create mode 100644 azurerm/internal/services/portal/validate/tenant_configuration_id.go create mode 100644 azurerm/internal/services/portal/validate/tenant_configuration_id_test.go create mode 100644 website/docs/r/tenant_configuration.html.markdown diff --git a/azurerm/internal/services/portal/client/client.go b/azurerm/internal/services/portal/client/client.go index 5231c5e41f54..661b7b039744 100644 --- a/azurerm/internal/services/portal/client/client.go +++ b/azurerm/internal/services/portal/client/client.go @@ -6,14 +6,19 @@ import ( ) type Client struct { - DashboardsClient *portal.DashboardsClient + DashboardsClient *portal.DashboardsClient + TenantConfigurationsClient *portal.TenantConfigurationsClient } func NewClient(o *common.ClientOptions) *Client { dashboardsClient := portal.NewDashboardsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&dashboardsClient.Client, o.ResourceManagerAuthorizer) + tenantConfigurationsClient := portal.NewTenantConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&tenantConfigurationsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - DashboardsClient: &dashboardsClient, + DashboardsClient: &dashboardsClient, + TenantConfigurationsClient: &tenantConfigurationsClient, } } diff --git a/azurerm/internal/services/portal/parse/tenant_configuration.go b/azurerm/internal/services/portal/parse/tenant_configuration.go new file mode 100644 index 000000000000..aebac9b2b5b6 --- /dev/null +++ b/azurerm/internal/services/portal/parse/tenant_configuration.go @@ -0,0 +1,53 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type TenantConfigurationId struct { + Name string +} + +func NewTenantConfigurationID(name string) TenantConfigurationId { + return TenantConfigurationId{ + Name: name, + } +} + +func (id TenantConfigurationId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Tenant Configuration", segmentsStr) +} + +func (id TenantConfigurationId) ID() string { + fmtString := "/providers/Microsoft.Portal/tenantConfigurations/%s" + return fmt.Sprintf(fmtString, id.Name) +} + +// TenantConfigurationID parses a TenantConfiguration ID into an TenantConfigurationId struct +func TenantConfigurationID(input string) (*TenantConfigurationId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := TenantConfigurationId{} + + if resourceId.Name, err = id.PopSegment("tenantConfigurations"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/azurerm/internal/services/portal/parse/tenant_configuration_test.go b/azurerm/internal/services/portal/parse/tenant_configuration_test.go new file mode 100644 index 000000000000..7849f4161658 --- /dev/null +++ b/azurerm/internal/services/portal/parse/tenant_configuration_test.go @@ -0,0 +1,80 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = TenantConfigurationId{} + +func TestTenantConfigurationIDFormatter(t *testing.T) { + actual := NewTenantConfigurationID("default").ID() + expected := "/providers/Microsoft.Portal/tenantConfigurations/default" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestTenantConfigurationID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *TenantConfigurationId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing Name + Input: "/providers/Microsoft.Portal/", + Error: true, + }, + + { + // missing value for Name + Input: "/providers/Microsoft.Portal/tenantConfigurations/", + Error: true, + }, + + { + // valid + Input: "/providers/Microsoft.Portal/tenantConfigurations/default", + Expected: &TenantConfigurationId{ + Name: "default", + }, + }, + + { + // upper-cased + Input: "/PROVIDERS/MICROSOFT.PORTAL/TENANTCONFIGURATIONS/DEFAULT", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := TenantConfigurationID(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.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/azurerm/internal/services/portal/portal_tenant_configuration_resource.go b/azurerm/internal/services/portal/portal_tenant_configuration_resource.go new file mode 100644 index 000000000000..24d0f5380676 --- /dev/null +++ b/azurerm/internal/services/portal/portal_tenant_configuration_resource.go @@ -0,0 +1,121 @@ +package portal + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/portal/mgmt/2019-01-01-preview/portal" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "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/services/portal/parse" + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceTenantConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceTenantConfigurationCreateUpdate, + Read: resourceTenantConfigurationRead, + Update: resourceTenantConfigurationCreateUpdate, + Delete: resourceTenantConfigurationDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.TenantConfigurationID(id) + return err + }), + + Schema: map[string]*schema.Schema{ + "enforce_private_markdown_storage": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceTenantConfigurationCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Portal.TenantConfigurationsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id := parse.NewTenantConfigurationID("default") + + if d.IsNewResource() { + existing, err := client.Get(ctx) + 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_tenant_configuration", id.ID()) + } + } + + tenantConfiguration := portal.Configuration{ + ConfigurationProperties: &portal.ConfigurationProperties{ + EnforcePrivateMarkdownStorage: utils.Bool(d.Get("enforce_private_markdown_storage").(bool)), + }, + } + + if _, err := client.Create(ctx, tenantConfiguration); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceTenantConfigurationRead(d, meta) +} + +func resourceTenantConfigurationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Portal.TenantConfigurationsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.TenantConfigurationID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + if props := resp.ConfigurationProperties; props != nil { + d.Set("enforce_private_markdown_storage", props.EnforcePrivateMarkdownStorage) + } + + return nil +} + +func resourceTenantConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Portal.TenantConfigurationsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.TenantConfigurationID(d.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil +} diff --git a/azurerm/internal/services/portal/portal_tenant_configuration_resource_test.go b/azurerm/internal/services/portal/portal_tenant_configuration_resource_test.go new file mode 100644 index 000000000000..9c16e9300901 --- /dev/null +++ b/azurerm/internal/services/portal/portal_tenant_configuration_resource_test.go @@ -0,0 +1,81 @@ +package portal_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/portal/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type PortalTenantConfigurationResource struct{} + +func TestAccPortalTenantConfiguration_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_tenant_configuration", "test") + r := PortalTenantConfigurationResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccPortalTenantConfiguration_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_tenant_configuration", "test") + r := PortalTenantConfigurationResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (r PortalTenantConfigurationResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.TenantConfigurationID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Portal.TenantConfigurationsClient.Get(ctx) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %v", id.String(), err) + } + + return utils.Bool(resp.ConfigurationProperties != nil), nil +} + +func (r PortalTenantConfigurationResource) basic() string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_tenant_configuration" "test" { + enforce_private_markdown_storage = true +} +`) +} + +func (r PortalTenantConfigurationResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_portal_tenant_configuration" "import" { + enforce_private_markdown_storage = azurerm_portal_tenant_configuration.test.enforce_private_markdown_storage +} +`, r.basic()) +} diff --git a/azurerm/internal/services/portal/registration.go b/azurerm/internal/services/portal/registration.go index 2e1ca66d8834..8c2e5bb558d5 100644 --- a/azurerm/internal/services/portal/registration.go +++ b/azurerm/internal/services/portal/registration.go @@ -26,6 +26,7 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_dashboard": resourceDashboard(), + "azurerm_dashboard": resourceDashboard(), + "azurerm_tenant_configuration": resourceTenantConfiguration(), } } diff --git a/azurerm/internal/services/portal/resourceids.go b/azurerm/internal/services/portal/resourceids.go index 47d36192b2a4..adf00928188a 100644 --- a/azurerm/internal/services/portal/resourceids.go +++ b/azurerm/internal/services/portal/resourceids.go @@ -1,3 +1,4 @@ package portal //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Dashboard -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Portal/dashboards/dashboard1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=TenantConfiguration -id=/providers/Microsoft.Portal/tenantConfigurations/default diff --git a/azurerm/internal/services/portal/validate/tenant_configuration_id.go b/azurerm/internal/services/portal/validate/tenant_configuration_id.go new file mode 100644 index 000000000000..3e23467ed5fb --- /dev/null +++ b/azurerm/internal/services/portal/validate/tenant_configuration_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/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/portal/parse" +) + +func TenantConfigurationID(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.TenantConfigurationID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/azurerm/internal/services/portal/validate/tenant_configuration_id_test.go b/azurerm/internal/services/portal/validate/tenant_configuration_id_test.go new file mode 100644 index 000000000000..fcb73b12ed3f --- /dev/null +++ b/azurerm/internal/services/portal/validate/tenant_configuration_id_test.go @@ -0,0 +1,52 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestTenantConfigurationID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing Name + Input: "/providers/Microsoft.Portal/", + Valid: false, + }, + + { + // missing value for Name + Input: "/providers/Microsoft.Portal/tenantConfigurations/", + Valid: false, + }, + + { + // valid + Input: "/providers/Microsoft.Portal/tenantConfigurations/default", + Valid: true, + }, + + { + // upper-cased + Input: "/PROVIDERS/MICROSOFT.PORTAL/TENANTCONFIGURATIONS/DEFAULT", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := TenantConfigurationID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/tenant_configuration.html.markdown b/website/docs/r/tenant_configuration.html.markdown new file mode 100644 index 000000000000..a8c058734c58 --- /dev/null +++ b/website/docs/r/tenant_configuration.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "Portal" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_tenant_configuration" +description: |- + Manages Portal Tenant Configuration. +--- + +# azurerm_tenant_configuration + +Manages Portal Tenant Configuration. + +~> **Note:** User has to be a Tenant Admin for managing this resource. + +## Example Usage + +```hcl +resource "azurerm_tenant_configuration" "example" { + enforce_private_markdown_storage = true +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `enforce_private_markdown_storage` - (Required) Is Markdown tile which used to display custom and static content enabled? + +~> **Note:** When `enforce_private_markdown_storage` is set to `true` Markdown tile will require external storage configuration (URI). The inline content configuration will be prohibited. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Portal Tenant Configuration. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Portal Tenant Configuration. +* `read` - (Defaults to 5 minutes) Used when retrieving the Portal Tenant Configuration. +* `update` - (Defaults to 30 minutes) Used when updating the Portal Tenant Configuration. +* `delete` - (Defaults to 30 minutes) Used when deleting the Portal Tenant Configuration. + +## Import + +Portal Tenant Configurations can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_tenant_configuration.example /providers/Microsoft.Portal/tenantConfigurations/default +```