Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support managed identity for "azurerm_spring_cloud_app" #8336

Merged
merged 4 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 94 additions & 11 deletions azurerm/internal/services/appplatform/spring_cloud_app_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/Azure/azure-sdk-for-go/services/preview/appplatform/mgmt/2019-05-01-preview/appplatform"
"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/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
Expand All @@ -19,8 +20,9 @@ import (

func resourceArmSpringCloudApp() *schema.Resource {
return &schema.Resource{
Create: resourceArmSpringCloudAppCreate,
Create: resourceArmSpringCloudAppCreateUpdate,
Read: resourceArmSpringCloudAppRead,
Update: resourceArmSpringCloudAppCreateUpdate,
Delete: resourceArmSpringCloudAppDelete,

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
Expand All @@ -31,6 +33,7 @@ func resourceArmSpringCloudApp() *schema.Resource {
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),
},

Expand All @@ -50,35 +53,75 @@ func resourceArmSpringCloudApp() *schema.Resource {
ForceNew: true,
ValidateFunc: validate.SpringCloudServiceName,
},

"identity": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
// other two enum: 'UserAssigned', 'SystemAssignedUserAssigned' are not supported for now
ValidateFunc: validation.StringInSlice([]string{
string(appplatform.SystemAssigned),
}, false),
},

"principal_id": {
Type: schema.TypeString,
Computed: true,
},

"tenant_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func resourceArmSpringCloudAppCreate(d *schema.ResourceData, meta interface{}) error {
func resourceArmSpringCloudAppCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).AppPlatform.AppsClient
servicesClient := meta.(*clients.Client).AppPlatform.ServicesClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

name := d.Get("name").(string)
resourceGroup := d.Get("resource_group_name").(string)
serviceName := d.Get("service_name").(string)

existing, err := client.Get(ctx, resourceGroup, serviceName, name, "")
serviceResp, err := servicesClient.Get(ctx, resourceGroup, serviceName)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("checking for presence of existing Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
}
return fmt.Errorf("unable to retrieve Spring Cloud Service %q (Resource Group %q): %+v", serviceName, resourceGroup, err)
}
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_spring_cloud_app", *existing.ID)

if d.IsNewResource() {
existing, err := client.Get(ctx, resourceGroup, serviceName, name, "")
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("checking for presence of existing Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
}
}
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_spring_cloud_app", *existing.ID)
}
}

future, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, name, appplatform.AppResource{})
app := appplatform.AppResource{
Location: serviceResp.Location,
Identity: expandSpringCloudAppIdentity(d.Get("identity").([]interface{})),
}
future, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, name, app)
if err != nil {
return fmt.Errorf("creating Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
return fmt.Errorf("creating/update Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
}
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for creation of Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
return fmt.Errorf("waiting for creation/update of Spring Cloud App %q (Spring Cloud Service %q / Resource Group %q): %+v", name, serviceName, resourceGroup, err)
}

resp, err := client.Get(ctx, resourceGroup, serviceName, name, "")
Expand Down Expand Up @@ -117,6 +160,10 @@ func resourceArmSpringCloudAppRead(d *schema.ResourceData, meta interface{}) err
d.Set("resource_group_name", id.ResourceGroup)
d.Set("service_name", id.ServiceName)

if err := d.Set("identity", flattenSpringCloudAppIdentity(resp.Identity)); err != nil {
return fmt.Errorf("setting `identity`: %s", err)
}

return nil
}

Expand All @@ -136,3 +183,39 @@ func resourceArmSpringCloudAppDelete(d *schema.ResourceData, meta interface{}) e

return nil
}

func expandSpringCloudAppIdentity(input []interface{}) *appplatform.ManagedIdentityProperties {
if len(input) == 0 || input[0] == nil {
return &appplatform.ManagedIdentityProperties{
Type: appplatform.None,
}
}
identity := input[0].(map[string]interface{})
return &appplatform.ManagedIdentityProperties{
Type: appplatform.ManagedIdentityType(identity["type"].(string)),
}
}

func flattenSpringCloudAppIdentity(identity *appplatform.ManagedIdentityProperties) []interface{} {
if identity == nil {
return make([]interface{}, 0)
}

principalId := ""
if identity.PrincipalID != nil {
principalId = *identity.PrincipalID
}

tenantId := ""
if identity.TenantID != nil {
tenantId = *identity.TenantID
}

return []interface{}{
map[string]interface{}{
"principal_id": principalId,
"tenant_id": tenantId,
"type": string(identity.Type),
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,60 @@ func TestAccAzureRMSpringCloudApp_requiresImport(t *testing.T) {
})
}

func TestAccAzureRMSpringCloudApp_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_spring_cloud_app", "test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMSpringCloudAppDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSpringCloudApp_complete(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSpringCloudAppExists(data.ResourceName),
resource.TestCheckResourceAttrSet(data.ResourceName, "identity.0.principal_id"),
resource.TestCheckResourceAttrSet(data.ResourceName, "identity.0.tenant_id"),
),
},
data.ImportStep(),
},
})
}

func TestAccAzureRMSpringCloudApp_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_spring_cloud_app", "test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMSpringCloudAppDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSpringCloudApp_basic(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSpringCloudAppExists(data.ResourceName),
),
},
data.ImportStep(),
{
Config: testAccAzureRMSpringCloudApp_complete(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSpringCloudAppExists(data.ResourceName),
),
},
data.ImportStep(),
{
Config: testAccAzureRMSpringCloudApp_basic(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSpringCloudAppExists(data.ResourceName),
),
},
data.ImportStep(),
},
})
}

func testCheckAzureRMSpringCloudAppExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down Expand Up @@ -125,6 +179,23 @@ resource "azurerm_spring_cloud_app" "import" {
`, template)
}

func testAccAzureRMSpringCloudApp_complete(data acceptance.TestData) string {
template := testAccAzureRMSpringCloudApp_template(data)
return fmt.Sprintf(`
%s

resource "azurerm_spring_cloud_app" "test" {
name = "acctest-sca-%d"
resource_group_name = azurerm_spring_cloud_service.test.resource_group_name
service_name = azurerm_spring_cloud_service.test.name

identity {
type = "SystemAssigned"
}
}
`, template, data.RandomInteger)
}

func testAccAzureRMSpringCloudApp_template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
23 changes: 22 additions & 1 deletion website/docs/r/spring_cloud_app.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ resource "azurerm_spring_cloud_app" "example" {
name = "example-springcloudapp"
resource_group_name = azurerm_resource_group.example.name
service_name = azurerm_spring_cloud_service.example.name

identity {
type = "SystemAssigned"
}
}
```

Expand All @@ -43,20 +47,37 @@ The following arguments are supported:

* `resource_group_name` - (Required) Specifies the name of the resource group in which to create the Spring Cloud Application. Changing this forces a new resource to be created.

* `service_name` - (Required) Specifies the name of the Spring Cloud Service resource. Changing this forces a new resource to be created.
* `service_name` - (Required) Specifies the name of the Spring Cloud Service resource. Changing this forces a new resource to be created.

* `identity` - (Optional) An `identity` block as defined below.

---

An `identity` block supports the following:

* `type` - (Required) Specifies the identity type of the Spring Cloud Application. Possible value is `SystemAssigned`.

## Attributes Reference

The following attributes are exported:

* `id` - The ID of the Spring Cloud Application.

---

An `identity` block exports the following:

* `principal_id` - The Principal ID for the Service Principal associated with the Managed Service Identity of this Spring Cloud Application.

* `tenant_id` - The Tenant ID for the Service Principal associated with the Managed Service Identity of this Spring Cloud Application.

## 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 Spring Cloud Application.
* `read` - (Defaults to 5 minutes) Used when retrieving the Spring Cloud Application.
* `update` - (Defaults to 30 minutes) Used when updating the Spring Cloud Application.
* `delete` - (Defaults to 30 minutes) Used when deleting the Spring Cloud Application.

## Import
Expand Down