Skip to content

Commit

Permalink
New resource: azurerm_tenant_configuration (#11697)
Browse files Browse the repository at this point in the history
  • Loading branch information
Neil Ye authored Jul 1, 2021
1 parent 490a02c commit 441857e
Show file tree
Hide file tree
Showing 9 changed files with 503 additions and 3 deletions.
9 changes: 7 additions & 2 deletions azurerm/internal/services/portal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package parse

import (
"fmt"
"strings"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
)

type PortalTenantConfigurationId struct {
Name string
}

func NewPortalTenantConfigurationID(name string) PortalTenantConfigurationId {
return PortalTenantConfigurationId{
Name: name,
}
}

func (id PortalTenantConfigurationId) String() string {
segments := []string{
fmt.Sprintf("Name %q", id.Name),
}
segmentsStr := strings.Join(segments, " / ")
return fmt.Sprintf("%s: (%s)", "Portal Tenant Configuration", segmentsStr)
}

func (id PortalTenantConfigurationId) ID() string {
fmtString := "/providers/Microsoft.Portal/tenantConfigurations/%s"
return fmt.Sprintf(fmtString, id.Name)
}

// PortalTenantConfigurationID parses a PortalTenantConfiguration ID into an PortalTenantConfigurationId struct
func PortalTenantConfigurationID(input string) (*PortalTenantConfigurationId, error) {
id, err := azure.ParseAzureResourceIDWithoutSubscription(input)
if err != nil {
return nil, err
}

resourceId := PortalTenantConfigurationId{}

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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package parse

import (
"testing"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/resourceid"
)

var _ resourceid.Formatter = PortalTenantConfigurationId{}

func TestPortalTenantConfigurationIDFormatter(t *testing.T) {
actual := NewPortalTenantConfigurationID("default").ID()
expected := "/providers/Microsoft.Portal/tenantConfigurations/default"
if actual != expected {
t.Fatalf("Expected %q but got %q", expected, actual)
}
}

func TestPortalTenantConfigurationID(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *PortalTenantConfigurationId
}{

{
// 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: &PortalTenantConfigurationId{
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 := PortalTenantConfigurationID(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)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package portal

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/portal/mgmt/2019-01-01-preview/portal"
"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"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk"
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 resourcePortalTenantConfiguration() *pluginsdk.Resource {
return &pluginsdk.Resource{
Create: resourcePortalTenantConfigurationCreateUpdate,
Read: resourcePortalTenantConfigurationRead,
Update: resourcePortalTenantConfigurationCreateUpdate,
Delete: resourcePortalTenantConfigurationDelete,

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: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.PortalTenantConfigurationID(id)
return err
}),

Schema: map[string]*pluginsdk.Schema{
"private_markdown_storage_enforced": {
Type: pluginsdk.TypeBool,
Required: true,
},
},
}
}

func resourcePortalTenantConfigurationCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Portal.TenantConfigurationsClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

id := parse.NewPortalTenantConfigurationID("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_portal_tenant_configuration", id.ID())
}
}

parameters := portal.Configuration{
ConfigurationProperties: &portal.ConfigurationProperties{
EnforcePrivateMarkdownStorage: utils.Bool(d.Get("private_markdown_storage_enforced").(bool)),
},
}

if _, err := client.Create(ctx, parameters); err != nil {
return fmt.Errorf("creating/updating %s: %+v", id, err)
}

d.SetId(id.ID())

return resourcePortalTenantConfigurationRead(d, meta)
}

func resourcePortalTenantConfigurationRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Portal.TenantConfigurationsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.PortalTenantConfigurationID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] %s was not found - removing from state!", *id)
d.SetId("")
return nil
}
return fmt.Errorf("retrieving %s: %+v", *id, err)
}

if props := resp.ConfigurationProperties; props != nil {
d.Set("private_markdown_storage_enforced", props.EnforcePrivateMarkdownStorage)
}

return nil
}

func resourcePortalTenantConfigurationDelete(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Portal.TenantConfigurationsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.PortalTenantConfigurationID(d.Id())
if err != nil {
return err
}

if _, err := client.Delete(ctx); err != nil {
return fmt.Errorf("deleting %s: %+v", *id, err)
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package portal_test

import (
"context"
"fmt"
"testing"

"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/internal/tf/pluginsdk"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

type PortalTenantConfigurationResource struct{}

func TestAccPortalTenantConfiguration(t *testing.T) {
// NOTE: this is a combined test rather than separate split out tests due to
// Azure only being able provision one default Tenant Configuration at a time
acceptance.RunTestsInSequence(t, map[string]map[string]func(t *testing.T){
"resource": {
"basic": testAccPortalTenantConfiguration_basic,
"update": testAccPortalTenantConfiguration_update,
"requiresImport": testAccPortalTenantConfiguration_requiresImport,
},
})
}

func testAccPortalTenantConfiguration_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_portal_tenant_configuration", "test")
r := PortalTenantConfigurationResource{}
data.ResourceSequentialTest(t, r, []acceptance.TestStep{
{
Config: r.basic(true),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func testAccPortalTenantConfiguration_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_portal_tenant_configuration", "test")
r := PortalTenantConfigurationResource{}
data.ResourceSequentialTest(t, r, []acceptance.TestStep{
{
Config: r.basic(true),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.RequiresImportErrorStep(r.requiresImport),
})
}

func testAccPortalTenantConfiguration_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_portal_tenant_configuration", "test")
r := PortalTenantConfigurationResource{}
data.ResourceSequentialTest(t, r, []acceptance.TestStep{
{
Config: r.basic(true),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.basic(false),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (r PortalTenantConfigurationResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.PortalTenantConfigurationID(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(enforcePrivateMarkdownStorage bool) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_portal_tenant_configuration" "test" {
private_markdown_storage_enforced = %t
}
`, enforcePrivateMarkdownStorage)
}

func (r PortalTenantConfigurationResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_portal_tenant_configuration" "import" {
private_markdown_storage_enforced = azurerm_portal_tenant_configuration.test.private_markdown_storage_enforced
}
`, r.basic(true))
}
3 changes: 2 additions & 1 deletion azurerm/internal/services/portal/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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_dashboard": resourceDashboard(),
"azurerm_dashboard": resourceDashboard(), // TODO 3.0 rename to azurerm_portal_dashboard
"azurerm_portal_tenant_configuration": resourcePortalTenantConfiguration(),
}
}
Loading

0 comments on commit 441857e

Please sign in to comment.