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

New Resource: azurerm_key_vault_managed_hardware_security_module_role_definition and azurerm_key_vault_managed_hardware_security_module_role_assignment for managed HSM #22332

Merged
merged 5 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
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
19 changes: 12 additions & 7 deletions internal/services/keyvault/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type Client struct {
ManagementClient *dataplane.BaseClient
VaultsClient *vaults.VaultsClient

MHSMSDClient *dataplane.HSMSecurityDomainClient
MHSMRoleClient *dataplane.RoleDefinitionsClient
MHSMSDClient *dataplane.HSMSecurityDomainClient
MHSMRoleClient *dataplane.RoleDefinitionsClient
MHSMRoleAssignmentsClient *dataplane.RoleAssignmentsClient
}

func NewClient(o *common.ClientOptions) *Client {
Expand All @@ -36,11 +37,15 @@ func NewClient(o *common.ClientOptions) *Client {

o.ConfigureClient(&vaultsClient.Client, o.ResourceManagerAuthorizer)

mhsmRoleAssignClient := dataplane.NewRoleAssignmentsClient()
o.ConfigureClient(&mhsmRoleAssignClient.Client, o.ManagedHSMAuthorizer)

return &Client{
ManagedHsmClient: &managedHsmClient,
ManagementClient: &managementClient,
VaultsClient: &vaultsClient,
MHSMSDClient: &sdClient,
MHSMRoleClient: &mhsmRoleDefineClient,
ManagedHsmClient: &managedHsmClient,
ManagementClient: &managementClient,
VaultsClient: &vaultsClient,
MHSMSDClient: &sdClient,
MHSMRoleClient: &mhsmRoleDefineClient,
MHSMRoleAssignmentsClient: &mhsmRoleAssignClient,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ func resourceKeyVaultManagedHardwareSecurityModule() *pluginsdk.Resource {
"public_network_access_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
//Computed: true,
Default: true,
ForceNew: true,
},
Expand Down Expand Up @@ -435,7 +434,7 @@ func securityDomainDownload(ctx context.Context, cli *client.Client, vaultBaseUr
keyID, _ := parse.ParseNestedItemID(certIDStr)
certRes, err := keyClient.GetCertificate(ctx, keyID.KeyVaultBaseUrl, keyID.Name, keyID.Version)
if err != nil {
return "", fmt.Errorf("retreiving key %s: %v", certID, err)
return "", fmt.Errorf("retrieving key %s: %v", certID, err)
}
if certRes.Cer == nil {
return "", fmt.Errorf("got nil key for %s", certID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func TestAccKeyVaultManagedHardwareSecurityModule(t *testing.T) {
"update": testAccKeyVaultManagedHardwareSecurityModule_requiresImport,
"complete": testAccKeyVaultManagedHardwareSecurityModule_complete,
"download": testAccKeyVaultManagedHardwareSecurityModule_download,
"role_define": testAccKeyVaultManagedHardwareSecurityModule_roleDefinition,
"role_assign": testAccKeyVaultManagedHardwareSecurityModule_roleAssignment,
},
})
}
Expand Down Expand Up @@ -76,6 +78,50 @@ func testAccKeyVaultManagedHardwareSecurityModule_download(t *testing.T) {
})
}

func testAccKeyVaultManagedHardwareSecurityModule_roleDefinition(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_hardware_security_module_role_definition", "test")
r := KeyVaultMHSMRoleDefinitionResource{}

data.ResourceSequentialTest(t, r, []acceptance.TestStep{
{
Config: r.withRoleDefinition(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.withRoleDefinitionUpdate(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func testAccKeyVaultManagedHardwareSecurityModule_roleAssignment(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_hardware_security_module_role_assignment", "test")
r := KeyVaultRoleAssignmentResource{}

data.ResourceSequentialTest(t, r, []acceptance.TestStep{
{
Config: r.withRoleAssignment(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.withBuiltInRoleAssignment(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func testAccKeyVaultManagedHardwareSecurityModule_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_hardware_security_module", "test")
r := KeyVaultManagedHardwareSecurityModuleResource{}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package keyvault

import (
"context"
"fmt"
"regexp"
"time"

"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roledefinitions"

"github.com/hashicorp/terraform-provider-azurerm/internal/locks"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/utils"
"github.com/tombuildsstuff/kermit/sdk/keyvault/7.4/keyvault"
)

type ManagedHSMRoleAssignmentModel struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

shoudln't these all be

Suggested change
type ManagedHSMRoleAssignmentModel struct {
type KeyVaultManagedHSMRoleAssignmentModel struct {

VaultBaseUrl string `tfschema:"vault_base_url"`
Name string `tfschema:"name"`
Scope string `tfschema:"scope"`
RoleDefinitionId string `tfschema:"role_definition_id"`
PrincipalId string `tfschema:"principal_id"`
ResourceId string `tfschema:"resource_id"`
}

type KeyVaultRoleAssignmentResource struct{}

var _ sdk.Resource = (*KeyVaultRoleAssignmentResource)(nil)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var _ sdk.Resource = (*KeyVaultRoleAssignmentResource)(nil)
var _ sdk.Resource = KeyVaultRoleAssignmentResource{}

Copy link
Collaborator

Choose a reason for hiding this comment

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

@wuxu92 you missed this


func (m KeyVaultRoleAssignmentResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"vault_base_url": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"scope": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(/|/keys|/keys/.+)$`), "scope should be one of `/`, `/keys', `/keys/<key_name>`"),
},

"role_definition_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: roledefinitions.ValidateScopedRoleDefinitionID,
},

"principal_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
}
}

func (m KeyVaultRoleAssignmentResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"resource_id": {
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

func (m KeyVaultRoleAssignmentResource) ModelObject() interface{} {
return &ManagedHSMRoleAssignmentModel{}
}

func (m KeyVaultRoleAssignmentResource) ResourceType() string {
return "azurerm_key_vault_managed_hardware_security_module_role_assignment"
}

func (m KeyVaultRoleAssignmentResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) (err error) {
client := meta.Client.KeyVault.MHSMRoleAssignmentsClient

var model ManagedHSMRoleAssignmentModel
if err := meta.Decode(&model); err != nil {
return err
}

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.NewMHSMNestedItemID(model.VaultBaseUrl, model.Scope, parse.RoleAssignmentType, model.Name)
if err != nil {
return err
}

existing, err := client.Get(ctx, model.VaultBaseUrl, model.Scope, model.Name)
if !utils.ResponseWasNotFound(existing.Response) {
if err != nil {
return fmt.Errorf("retrieving %s: %v", id.ID(), err)
}
return meta.ResourceRequiresImport(m.ResourceType(), id)
}

var param keyvault.RoleAssignmentCreateParameters
param.Properties = &keyvault.RoleAssignmentProperties{
PrincipalID: pointer.FromString(model.PrincipalId),
RoleDefinitionID: pointer.FromString(model.RoleDefinitionId),
}
_, err = client.Create(ctx, model.VaultBaseUrl, model.Scope, model.Name, param)
if err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

lines can be combined

return fmt.Errorf("creating %s: %v", id.ID(), err)
}

meta.SetID(id)
return nil
},
}
}

func (m KeyVaultRoleAssignmentResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
client := meta.Client.KeyVault.MHSMRoleAssignmentsClient

id, err := parse.MHSMNestedItemID(meta.ResourceData.Id())
if err != nil {
return err
}

result, err := client.Get(ctx, id.VaultBaseUrl, id.Scope, id.Name)
if err != nil {
if utils.ResponseWasNotFound(result.Response) {
return meta.MarkAsGone(id)
}
return err
}

var model ManagedHSMRoleAssignmentModel
if err := meta.Decode(&model); err != nil {
return err
}

prop := result.Properties
model.Name = pointer.From(result.Name)
model.VaultBaseUrl = id.VaultBaseUrl
model.Scope = id.Scope

model.RoleDefinitionId = pointer.ToString(prop.RoleDefinitionID)
model.PrincipalId = pointer.ToString(prop.PrincipalID)
model.ResourceId = pointer.ToString(result.ID)

return meta.Encode(&model)
},
}
}

func (m KeyVaultRoleAssignmentResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
id, err := parse.MHSMNestedItemID(meta.ResourceData.Id())
if err != nil {
return err
}

meta.Logger.Infof("deleting %s", id)

locks.ByName(id.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")
defer locks.UnlockByName(id.VaultBaseUrl, "azurerm_key_vault_managed_hardware_security_module")
if _, err := meta.Client.KeyVault.MHSMRoleAssignmentsClient.Delete(ctx, id.VaultBaseUrl, id.Scope, id.Name); err != nil {
return fmt.Errorf("deleting %s: %v", id.ID(), err)
}
return nil
},
}
}

func (m KeyVaultRoleAssignmentResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return validate.MHSMNestedItemId
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package keyvault_test

import (
"context"
"fmt"

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"

"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type KeyVaultRoleAssignmentResource struct{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this be something like

Suggested change
type KeyVaultRoleAssignmentResource struct{}
type KeyVaultManagedHSMRoleAssignmentResource struct{}


// real test nested in TestAccKeyVaultManagedHardwareSecurityModule, only provide Exists logic here
func (k KeyVaultRoleAssignmentResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.MHSMNestedItemID(state.ID)
if err != nil {
return nil, err
}
resp, err := client.KeyVault.MHSMRoleAssignmentsClient.Get(ctx, id.VaultBaseUrl, id.Scope, id.Name)
if err != nil {
return nil, fmt.Errorf("retrieving Type %s: %+v", id, err)
}
return utils.Bool(resp.Properties != nil), nil
}

func (k KeyVaultRoleAssignmentResource) withRoleAssignment(data acceptance.TestData) string {
roleDef := KeyVaultMHSMRoleDefinitionResource{}.withRoleDefinition(data)

return fmt.Sprintf(`


%s

locals {
assignmentTestName = "1e243909-064c-6ac3-84e9-1c8bf8d6ad52"
}

data "azurerm_key_vault_managed_hardware_security_module_role_definition" "test" {
name = azurerm_key_vault_managed_hardware_security_module_role_definition.test.name
vault_base_url = azurerm_key_vault_managed_hardware_security_module.test.hsm_uri
}

resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "test" {
vault_base_url = azurerm_key_vault_managed_hardware_security_module.test.hsm_uri
name = local.assignmentTestName
scope = "/keys"
role_definition_id = data.azurerm_key_vault_managed_hardware_security_module_role_definition.test.resource_manager_id
principal_id = data.azurerm_client_config.current.object_id
}
`, roleDef)
}

func (k KeyVaultRoleAssignmentResource) withBuiltInRoleAssignment(data acceptance.TestData) string {
roleDef := k.withRoleAssignment(data)

return fmt.Sprintf(`


%s

locals {
assignmentOfficerName = "706c03c7-69ad-33e5-2796-b3380d3a6e1a"
}

data "azurerm_key_vault_managed_hardware_security_module_role_definition" "officer" {
vault_base_url = azurerm_key_vault_managed_hardware_security_module.test.hsm_uri
name = "515eb02d-2335-4d2d-92f2-b1cbdf9c3778"
}

resource "azurerm_key_vault_managed_hardware_security_module_role_assignment" "officer" {
vault_base_url = azurerm_key_vault_managed_hardware_security_module.test.hsm_uri
name = local.assignmentOfficerName
scope = "/keys"
role_definition_id = data.azurerm_key_vault_managed_hardware_security_module_role_definition.officer.resource_manager_id
principal_id = data.azurerm_client_config.current.object_id
}
`, roleDef)
}
Loading