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

managed_hsm_role_*_ids: use specific resource id to replace generic nested item id #25323

Merged
merged 3 commits into from
Mar 29, 2024
Merged
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ func (m KeyVaultManagedHSMRoleAssignmentResource) Create() sdk.ResourceFunc {
locks.ByName(model.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")
defer locks.UnlockByName(model.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")

id, err := parse.NewNestedItemID(model.VaultBaseUrl, model.Scope, parse.RoleAssignmentType, model.Name)
id, err := parse.NewManagedHSMRoleAssignmentID(model.VaultBaseUrl, model.Scope, model.Name)
if err != nil {
return err
}
@@ -140,7 +140,7 @@ func (m KeyVaultManagedHSMRoleAssignmentResource) Read() sdk.ResourceFunc {
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
client := meta.Client.ManagedHSMs.DataPlaneRoleAssignmentsClient

id, err := parse.NestedItemID(meta.ResourceData.Id())
id, err := parse.ManagedHSMRoleAssignmentID(meta.ResourceData.Id())
if err != nil {
return err
}
@@ -179,7 +179,7 @@ func (m KeyVaultManagedHSMRoleAssignmentResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
id, err := parse.NestedItemID(meta.ResourceData.Id())
id, err := parse.ManagedHSMRoleAssignmentID(meta.ResourceData.Id())
if err != nil {
return err
}
@@ -197,5 +197,5 @@ func (m KeyVaultManagedHSMRoleAssignmentResource) Delete() sdk.ResourceFunc {
}

func (m KeyVaultManagedHSMRoleAssignmentResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return validate.NestedItemId
return validate.ManagedHSMRoleAssignmentId
}
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ type KeyVaultManagedHSMRoleAssignmentResource struct{}

// real test nested in TestAccKeyVaultManagedHardwareSecurityModule, only provide Exists logic here
func (k KeyVaultManagedHSMRoleAssignmentResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.NestedItemID(state.ID)
id, err := parse.ManagedHSMRoleAssignmentID(state.ID)
if err != nil {
return nil, err
}
Original file line number Diff line number Diff line change
@@ -137,7 +137,7 @@ func (k KeyvaultMHSMRoleDefinitionDataSource) Read() sdk.ResourceFunc {
return err
}

id, err := parse.NewNestedItemID(model.VaultBaseUrl, roleDefinitionScope, parse.RoleDefinitionType, model.Name)
id, err := parse.NewManagedHSMRoleDefinitionID(model.VaultBaseUrl, roleDefinitionScope, model.Name)
if err != nil {
return err
}
Original file line number Diff line number Diff line change
@@ -170,7 +170,7 @@ func (k KeyVaultMHSMRoleDefinitionResource) Create() sdk.ResourceFunc {
locks.ByName(model.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")
defer locks.UnlockByName(model.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")

id, err := parse.NewNestedItemID(model.VaultBaseUrl, roleDefinitionScope, parse.RoleDefinitionType, model.Name)
id, err := parse.NewManagedHSMRoleDefinitionID(model.VaultBaseUrl, roleDefinitionScope, model.Name)
if err != nil {
return err
}
@@ -207,7 +207,7 @@ func (k KeyVaultMHSMRoleDefinitionResource) Read() sdk.ResourceFunc {
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
// import has no model data but only id
id, err := parse.NestedItemID(meta.ResourceData.Id())
id, err := parse.ManagedHSMRoleDefinitionID(meta.ResourceData.Id())
if err != nil {
return err
}
@@ -258,7 +258,7 @@ func (k KeyVaultMHSMRoleDefinitionResource) Update() sdk.ResourceFunc {
return err
}

id, err := parse.NewNestedItemID(model.VaultBaseUrl, roleDefinitionScope, parse.RoleDefinitionType, model.Name)
id, err := parse.NewManagedHSMRoleDefinitionID(model.VaultBaseUrl, roleDefinitionScope, model.Name)
if err != nil {
return err
}
@@ -297,7 +297,7 @@ func (k KeyVaultMHSMRoleDefinitionResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
id, err := parse.NestedItemID(meta.ResourceData.Id())
id, err := parse.ManagedHSMRoleDefinitionID(meta.ResourceData.Id())
if err != nil {
return err
}
@@ -314,7 +314,7 @@ func (k KeyVaultMHSMRoleDefinitionResource) Delete() sdk.ResourceFunc {
}

func (k KeyVaultMHSMRoleDefinitionResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return validate.NestedItemId
return validate.ManagedHSMRoleDefinitionId
}

func expandKeyVaultMHSMRolePermissions(perms []Permission) *[]keyvault.Permission {
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ type KeyVaultMHSMRoleDefinitionResource struct{}
// real test nested in TestAccKeyVaultManagedHardwareSecurityModule, only provide Exists logic here
func (k KeyVaultMHSMRoleDefinitionResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
baseURL := state.Attributes["vault_base_url"]
id, err := parse.NestedItemID(state.ID)
id, err := parse.ManagedHSMRoleDefinitionID(state.ID)
if err != nil {
return nil, err
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package parse

import (
"fmt"
"net/url"
"strings"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
)

var _ resourceids.Id = ManagedHSMRoleAssignmentId{}

type ManagedHSMRoleAssignmentId struct {
VaultBaseUrl string
Scope string
Name string
}

func NewManagedHSMRoleAssignmentID(hsmBaseUrl, scope string, name string) (*ManagedHSMRoleAssignmentId, error) {
keyVaultUrl, err := url.Parse(hsmBaseUrl)
if err != nil || hsmBaseUrl == "" {
return nil, fmt.Errorf("parsing managedHSM nested itemID %q: %+v", hsmBaseUrl, err)
}
// (@jackofallops) - Log Analytics service adds the port number to the API returns, so we strip it here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this relevant here? And if so, is it related to the log analytics service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The role definition and assignment ID shouldn't relate to the log analytics service. I've removed the stripping operation for both the role definition and assignment.

if hostParts := strings.Split(keyVaultUrl.Host, ":"); len(hostParts) > 1 {
keyVaultUrl.Host = hostParts[0]
}

return &ManagedHSMRoleAssignmentId{
VaultBaseUrl: keyVaultUrl.String(),
Scope: scope,
Name: name,
}, nil
}

func (n ManagedHSMRoleAssignmentId) ID() string {
// example: https://tharvey-keyvault.managedhsm.azure.net///RoleAssignment/uuid-idshifds-fks
segments := []string{
strings.TrimSuffix(n.VaultBaseUrl, "/"),
n.Scope,
"RoleAssignment",
n.Name,
}
return strings.TrimSuffix(strings.Join(segments, "/"), "/")
}

func (n ManagedHSMRoleAssignmentId) String() string {
return n.ID()
}

func ManagedHSMRoleAssignmentID(input string) (*ManagedHSMRoleAssignmentId, error) {
idURL, err := url.ParseRequestURI(input)
if err != nil {
return nil, fmt.Errorf("cannot parse Azure KeyVault Child Id: %s", err)
}

path := idURL.Path

path = strings.TrimPrefix(path, "/")
path = strings.TrimSuffix(path, "/")

nameSep := strings.LastIndex(path, "/")
if nameSep <= 0 {
return nil, fmt.Errorf("no name speparate exist in %s", input)
}
scope, name := path[:nameSep], path[nameSep+1:]

typeSep := strings.LastIndex(scope, "/")
if typeSep <= 0 {
return nil, fmt.Errorf("no type speparate exist in %s", input)
}
scope, typ := scope[:typeSep], scope[typeSep+1:]
if typ != "RoleAssignment" {
return nil, fmt.Errorf("invalid type %s, must be 'RoleAssignment'", typ)
}

childId := ManagedHSMRoleAssignmentId{
VaultBaseUrl: fmt.Sprintf("%s://%s/", idURL.Scheme, idURL.Host),
Scope: scope,
Name: name,
}

return &childId, nil
}
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@ import (
"testing"
)

func TestNewMHSMNestedItemID(t *testing.T) {
mhsmType := RoleDefinitionType
func TestNewManagedHSMRoleAssignmentID(t *testing.T) {
mhsmType := "RoleDefinition"
cases := []struct {
Scenario string
keyVaultBaseUrl string
@@ -42,7 +42,7 @@ func TestNewMHSMNestedItemID(t *testing.T) {
},
}
for idx, tc := range cases {
id, err := NewNestedItemID(tc.keyVaultBaseUrl, tc.Scope, mhsmType, tc.Name)
id, err := NewManagedHSMRoleDefinitionID(tc.keyVaultBaseUrl, tc.Scope, tc.Name)
if err != nil {
if !tc.ExpectError {
t.Fatalf("Got error for New Resource ID '%s': %+v", tc.keyVaultBaseUrl, err)
@@ -56,11 +56,11 @@ func TestNewMHSMNestedItemID(t *testing.T) {
}
}

func TestParseMHSMNestedItemID(t *testing.T) {
typ := RoleDefinitionType
func TestParseManagedHSMRoleAssignmentID(t *testing.T) {
typ := "RoleDefinition"
cases := []struct {
Input string
Expected NestedItemId
Expected ManagedHSMRoleDefinitionId
ExpectError bool
}{
{
@@ -70,7 +70,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: fmt.Sprintf("https://my-keyvault.managedhsm.azure.net///%s/test", typ),
ExpectError: true,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "test",
VaultBaseUrl: "https://my-keyvault.managedhsm.azure.net/",
Scope: "/",
@@ -79,7 +79,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: fmt.Sprintf("https://my-keyvault.managedhsm.azure.net///%s/bird", typ),
ExpectError: true,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "bird",
VaultBaseUrl: "https://my-keyvault.managedhsm.azure.net/",
Scope: "/",
@@ -88,7 +88,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: fmt.Sprintf("https://my-keyvault.managedhsm.azure.net///%s/bird", typ),
ExpectError: false,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "bird",
VaultBaseUrl: "https://my-keyvault.managedhsm.azure.net/",
Scope: "/",
@@ -97,7 +97,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: fmt.Sprintf("https://my-keyvault.managedhsm.azure.net//keys/%s/world", typ),
ExpectError: false,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "world",
VaultBaseUrl: "https://my-keyvault.managedhsm.azure.net/",
Scope: "/keys",
@@ -106,7 +106,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: fmt.Sprintf("https://my-keyvault.managedhsm.azure.net//keys/%s/fdf067c93bbb4b22bff4d8b7a9a56217", typ),
ExpectError: true,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "fdf067c93bbb4b22bff4d8b7a9a56217",
VaultBaseUrl: "https://my-keyvault.managedhsm.azure.net/",
Scope: "/keys",
@@ -115,7 +115,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
{
Input: "https://kvhsm23030816100222.managedhsm.azure.net///RoleDefinition/862d4d5e-bf01-11ed-a49d-00155d61ee9e",
ExpectError: true,
Expected: NestedItemId{
Expected: ManagedHSMRoleDefinitionId{
Name: "862d4d5e-bf01-11ed-a49d-00155d61ee9e",
VaultBaseUrl: "https://kvhsm23030816100222.managedhsm.azure.net/",
Scope: "/",
@@ -124,7 +124,7 @@ func TestParseMHSMNestedItemID(t *testing.T) {
}

for idx, tc := range cases {
secretId, err := NestedItemID(tc.Input)
roleId, err := ManagedHSMRoleDefinitionID(tc.Input)
if err != nil {
if tc.ExpectError {
continue
@@ -133,24 +133,24 @@ func TestParseMHSMNestedItemID(t *testing.T) {
t.Fatalf("Got error for ID '%s': %+v", tc.Input, err)
}

if secretId == nil {
if roleId == nil {
t.Fatalf("Expected a SecretID to be parsed for ID '%s', got nil.", tc.Input)
}

if tc.Expected.VaultBaseUrl != secretId.VaultBaseUrl {
t.Fatalf("Expected %d 'KeyVaultBaseUrl' to be '%s', got '%s' for ID '%s'", idx, tc.Expected.VaultBaseUrl, secretId.VaultBaseUrl, tc.Input)
if tc.Expected.VaultBaseUrl != roleId.VaultBaseUrl {
t.Fatalf("Expected %d 'KeyVaultBaseUrl' to be '%s', got '%s' for ID '%s'", idx, tc.Expected.VaultBaseUrl, roleId.VaultBaseUrl, tc.Input)
}

if tc.Expected.Name != secretId.Name {
t.Fatalf("Expected 'Name' to be '%s', got '%s' for ID '%s'", tc.Expected.Name, secretId.Name, tc.Input)
if tc.Expected.Name != roleId.Name {
t.Fatalf("Expected 'Name' to be '%s', got '%s' for ID '%s'", tc.Expected.Name, roleId.Name, tc.Input)
}

if tc.Expected.Scope != secretId.Scope {
t.Fatalf("Expected 'Scope' to be '%s', got '%s' for ID '%s'", tc.Expected.Scope, secretId.Scope, tc.Input)
if tc.Expected.Scope != roleId.Scope {
t.Fatalf("Expected 'Scope' to be '%s', got '%s' for ID '%s'", tc.Expected.Scope, roleId.Scope, tc.Input)
}

if tc.Input != secretId.ID() {
t.Fatalf("Expected 'ID()' to be '%s', got '%s'", tc.Input, secretId.ID())
if tc.Input != roleId.ID() {
t.Fatalf("Expected 'ID()' to be '%s', got '%s'", tc.Input, roleId.ID())
}
}
}
Loading