Skip to content

Commit

Permalink
[Enhance] azurerm_redis_cache - support config identity (#16990)
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhang3 authored Jun 2, 2022
1 parent e9e6686 commit eddd5b2
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 1 deletion.
83 changes: 82 additions & 1 deletion internal/services/redis/redis_cache_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2021-06-01/redis"
"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/zones"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -295,6 +296,8 @@ func resourceRedisCache() *pluginsdk.Resource {
ValidateFunc: validation.IntBetween(1, 3),
},

"identity": commonschema.SystemAssignedUserAssignedIdentityOptional(),

"replicas_per_primary": {
Type: pluginsdk.TypeInt,
Optional: true,
Expand Down Expand Up @@ -370,6 +373,11 @@ func resourceRedisCacheCreate(d *pluginsdk.ResourceData, meta interface{}) error
publicNetworkAccess = redis.PublicNetworkAccessDisabled
}

redisIdentity, err := expandRedisIdentity(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf(`expanding "identity": %v`, err)
}

parameters := redis.CreateParameters{
Location: utils.String(location),
CreateProperties: &redis.CreateProperties{
Expand All @@ -383,7 +391,8 @@ func resourceRedisCacheCreate(d *pluginsdk.ResourceData, meta interface{}) error
RedisConfiguration: redisConfiguration,
PublicNetworkAccess: publicNetworkAccess,
},
Tags: expandedTags,
Identity: redisIdentity,
Tags: expandedTags,
}

if v, ok := d.GetOk("shard_count"); ok {
Expand Down Expand Up @@ -564,6 +573,26 @@ func resourceRedisCacheUpdate(d *pluginsdk.ResourceData, meta interface{}) error
return fmt.Errorf("waiting for Redis Cache %q (Resource Group %q) to become available: %+v", id.RediName, id.ResourceGroup, err)
}

// identity cannot be updated with sku,publicNetworkAccess,redisVersion etc.
if d.HasChange("identity") {
redisIdentity, err := expandRedisIdentity(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf(`expanding "identity": %v`, err)
}

identityParameter := redis.UpdateParameters{
Identity: redisIdentity,
}
if _, err := client.Update(ctx, id.ResourceGroup, id.RediName, identityParameter); err != nil {
return fmt.Errorf("updating Redis Cache identity %q identity (Resource Group %q): %+v", id.RediName, id.ResourceGroup, err)
}

log.Printf("[DEBUG] Waiting for Redis Cache %q (Resource Group %q) to become available", id.RediName, id.ResourceGroup)
if _, err = stateConf.WaitForStateContext(ctx); err != nil {
return fmt.Errorf("waiting for Redis Cache %q (Resource Group %q) to become available: %+v", id.RediName, id.ResourceGroup, err)
}
}

patchSchedule := expandRedisPatchSchedule(d)

if patchSchedule == nil || len(*patchSchedule.ScheduleEntries.ScheduleEntries) == 0 {
Expand Down Expand Up @@ -614,6 +643,14 @@ func resourceRedisCacheRead(d *pluginsdk.ResourceData, meta interface{}) error {
}
}

redisIdentity, err := flattenRedisIdentity(resp.Identity)
if err != nil {
return fmt.Errorf("flattening `identity`: %+v", err)
}
if err := d.Set("identity", redisIdentity); err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

d.Set("name", id.RediName)
d.Set("resource_group_name", id.ResourceGroup)
d.Set("location", location.NormalizeNilable(resp.Location))
Expand Down Expand Up @@ -989,3 +1026,47 @@ func flattenRedisPatchSchedules(schedule redis.PatchSchedule) []interface{} {
func getRedisConnectionString(redisHostName string, sslPort int32, accessKey string, enableSslPort bool) string {
return fmt.Sprintf("%s:%d,password=%s,ssl=%t,abortConnect=False", redisHostName, sslPort, accessKey, enableSslPort)
}

func expandRedisIdentity(input []interface{}) (*redis.ManagedServiceIdentity, error) {
expanded, err := identity.ExpandSystemAndUserAssignedMap(input)
if err != nil {
return nil, err
}

out := redis.ManagedServiceIdentity{
Type: redis.ManagedServiceIdentityType(expanded.Type),
}
if expanded.Type == identity.TypeUserAssigned || expanded.Type == identity.TypeSystemAssignedUserAssigned {
out.UserAssignedIdentities = make(map[string]*redis.UserAssignedIdentity)
for k := range expanded.IdentityIds {
out.UserAssignedIdentities[k] = &redis.UserAssignedIdentity{
// intentionally empty
}
}
}
return &out, nil
}

func flattenRedisIdentity(input *redis.ManagedServiceIdentity) (*[]interface{}, error) {
var transform *identity.SystemAndUserAssignedMap

if input != nil {
transform = &identity.SystemAndUserAssignedMap{
Type: identity.Type(input.Type),
IdentityIds: make(map[string]identity.UserAssignedIdentityDetails),
}
if input.PrincipalID != nil {
transform.PrincipalId = input.PrincipalID.String()
}
if input.TenantID != nil {
transform.TenantId = input.TenantID.String()
}
for k, v := range input.UserAssignedIdentities {
transform.IdentityIds[k] = identity.UserAssignedIdentityDetails{
ClientId: utils.String(v.ClientID.String()),
PrincipalId: utils.String(v.PrincipalID.String()),
}
}
}
return identity.FlattenSystemAndUserAssignedMap(transform)
}
96 changes: 96 additions & 0 deletions internal/services/redis/redis_cache_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,28 @@ func TestAccRedisCache_redisConfiguration(t *testing.T) {
})
}

func TestAccRedisCache_identity(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_redis_cache", "test")
r := RedisCacheResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.systemAssignedIdentity(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.userAssignedIdentity(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (t RedisCacheResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.CacheID(state.ID)
if err != nil {
Expand Down Expand Up @@ -1211,6 +1233,80 @@ resource "azurerm_redis_cache" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, maxMemoryPolicy)
}

func (RedisCacheResource) systemAssignedIdentity(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 = "Standard"
enable_non_ssl_port = false
redis_configuration {
}
identity {
type = "SystemAssigned"
}
tags = {
environment = "production"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)

}

func (RedisCacheResource) userAssignedIdentity(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
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_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 = "Standard"
enable_non_ssl_port = false
redis_configuration {
}
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.test.id]
}
tags = {
environment = "production"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString, 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
Expand Down
12 changes: 12 additions & 0 deletions website/docs/r/redis_cache.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ The following arguments are supported:

* `enable_non_ssl_port` - (Optional) Enable the non-SSL port (6379) - disabled by default.

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

* `minimum_tls_version` - (Optional) The minimum TLS version. Defaults to `1.0`.

* `patch_schedule` - (Optional) A list of `patch_schedule` blocks as defined below.
Expand Down Expand Up @@ -91,6 +93,16 @@ The following arguments are supported:

---

An `identity` block supports the following:

* `type` - (Required) Specifies the type of Managed Service Identity that should be configured on this Batch Account. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned` (to enable both).

* `identity_ids` - (Optional) A list of User Assigned Managed Identity IDs to be assigned to this Batch Account.

~> **NOTE:** This is required when `type` is set to `UserAssigned` or `SystemAssigned, UserAssigned`.

---

A `redis_configuration` block supports the following:

* `aof_backup_enabled` - (Optional) Enable or disable AOF persistence for this Redis Cache.
Expand Down

0 comments on commit eddd5b2

Please sign in to comment.