Skip to content

Commit

Permalink
new resource: azurerm_site_recovery_hyperv_network_mapping (#21788)
Browse files Browse the repository at this point in the history
  • Loading branch information
ziyeqf authored May 23, 2023
1 parent 4729085 commit 5c9c115
Show file tree
Hide file tree
Showing 20 changed files with 1,453 additions and 0 deletions.
6 changes: 6 additions & 0 deletions internal/services/recoveryservices/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicesbackup/2023-02-01/protectionpolicies"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationfabrics"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationnetworkmappings"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationnetworks"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationpolicies"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationprotecteditems"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationprotectioncontainermappings"
Expand Down Expand Up @@ -45,6 +46,7 @@ type Client struct {
NetworkMappingClient *replicationnetworkmappings.ReplicationNetworkMappingsClient
ReplicationProtectedItemsClient *replicationprotecteditems.ReplicationProtectedItemsClient
ReplicationRecoveryPlansClient *replicationrecoveryplans.ReplicationRecoveryPlansClient
ReplicationNetworksClient *replicationnetworks.ReplicationNetworksClient
}

func NewClient(o *common.ClientOptions) *Client {
Expand Down Expand Up @@ -108,6 +110,9 @@ func NewClient(o *common.ClientOptions) *Client {
replicationRecoveryPlanClient := replicationrecoveryplans.NewReplicationRecoveryPlansClientWithBaseURI(o.ResourceManagerEndpoint)
o.ConfigureClient(&replicationRecoveryPlanClient.Client, o.ResourceManagerAuthorizer)

replicationNetworksClient := replicationnetworks.NewReplicationNetworksClientWithBaseURI(o.ResourceManagerEndpoint)
o.ConfigureClient(&replicationNetworksClient.Client, o.ResourceManagerAuthorizer)

return &Client{
ProtectableItemsClient: &protectableItemsClient,
ProtectedItemsClient: &protectedItemsClient,
Expand All @@ -129,5 +134,6 @@ func NewClient(o *common.ClientOptions) *Client {
NetworkMappingClient: &networkMappingClient,
ReplicationProtectedItemsClient: &replicationMigrationItemsClient,
ReplicationRecoveryPlansClient: &replicationRecoveryPlanClient,
ReplicationNetworksClient: &replicationNetworksClient,
}
}
1 change: 1 addition & 0 deletions internal/services/recoveryservices/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (r Registration) Resources() []sdk.Resource {
ReplicationPolicyHyperVResource{},
HyperVSiteResource{},
HyperVReplicationPolicyAssociationResource{},
HyperVNetworkMappingResource{},
VMWareReplicationPolicyResource{},
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
package recoveryservices

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservices/2022-10-01/vaults"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationfabrics"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationnetworkmappings"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationnetworks"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
)

const HyperVNetworkMappingRecoveryFabricName = "Microsoft Azure"

type HyperVNetworkMappingResource struct{}

type HyperVNetworkMappingModel struct {
Name string `tfschema:"name"`
VaultId string `tfschema:"recovery_vault_id"`
SCVMMname string `tfschema:"source_system_center_virtual_machine_manager_name"`
NetworkName string `tfschema:"source_network_name"`
TargetNetworkId string `tfschema:"target_network_id"`
}

var _ sdk.Resource = HyperVNetworkMappingResource{}

func (s HyperVNetworkMappingResource) Arguments() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"recovery_vault_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: vaults.ValidateVaultID,
},

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

"source_network_name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
DiffSuppressFunc: suppress.CaseDifference,
},

"target_network_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
DiffSuppressFunc: suppress.CaseDifference,
},
}
}

func (s HyperVNetworkMappingResource) Attributes() map[string]*schema.Schema {
return map[string]*schema.Schema{}
}

func (s HyperVNetworkMappingResource) ModelObject() interface{} {
return &HyperVNetworkMappingModel{}
}

func (s HyperVNetworkMappingResource) ResourceType() string {
return "azurerm_site_recovery_hyperv_network_mapping"
}

func (s HyperVNetworkMappingResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return replicationnetworkmappings.ValidateReplicationNetworkMappingID
}

func (s HyperVNetworkMappingResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
var plan HyperVNetworkMappingModel
if err := metadata.Decode(&plan); err != nil {
return fmt.Errorf("decoding %+v", err)
}

client := metadata.Client.RecoveryServices.NetworkMappingClient
fabricClient := metadata.Client.RecoveryServices.FabricClient
networksClient := metadata.Client.RecoveryServices.ReplicationNetworksClient

vaultId, err := replicationnetworkmappings.ParseVaultID(plan.VaultId)
if err != nil {
return fmt.Errorf("parsing vault id: %+v", err)
}

fabricId, err := fetchFabricIdByFriendlyName(ctx, fabricClient, vaultId.ID(), plan.SCVMMname)
if err != nil {
return fmt.Errorf("fetching fabric id: %+v", err)
}

networkId, err := fetchReplicationNetworkIdByFriendlyName(ctx, networksClient, fabricId, plan.NetworkName)
if err != nil {
return fmt.Errorf("fetching network id: %+v", err)
}

parsedNetworkId, err := replicationnetworkmappings.ParseReplicationNetworkID(networkId)
if err != nil {
return fmt.Errorf("parsing network id: %+v", err)
}

id := replicationnetworkmappings.NewReplicationNetworkMappingID(parsedNetworkId.SubscriptionId, parsedNetworkId.ResourceGroupName, parsedNetworkId.VaultName, parsedNetworkId.ReplicationFabricName, parsedNetworkId.ReplicationNetworkName, plan.Name)
existing, err := client.Get(ctx, id)
if err != nil {
if !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking presence of network mapping: %+v", err)
}
}

if !response.WasNotFound(existing.HttpResponse) {
return tf.ImportAsExistsError("azurerm_site_recovery_hyperv_network_mapping", id.ID())
}

err = client.CreateThenPoll(ctx, id, replicationnetworkmappings.CreateNetworkMappingInput{
Properties: replicationnetworkmappings.CreateNetworkMappingInputProperties{
RecoveryNetworkId: plan.TargetNetworkId,
RecoveryFabricName: pointer.To(HyperVNetworkMappingRecoveryFabricName),
FabricSpecificDetails: replicationnetworkmappings.VMmToAzureCreateNetworkMappingInput{},
},
})
if err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

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

func (s HyperVNetworkMappingResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
id, err := replicationnetworkmappings.ParseReplicationNetworkMappingID(metadata.ResourceData.Id())
if err != nil {
return fmt.Errorf("parsing id: %+v", err)
}

client := metadata.Client.RecoveryServices.NetworkMappingClient

resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %v", id, err)
}

vaultId := replicationnetworkmappings.NewVaultID(id.SubscriptionId, id.ResourceGroupName, id.VaultName)
state := HyperVNetworkMappingModel{
Name: id.ReplicationNetworkMappingName,
VaultId: vaultId.ID(),
}

if model := resp.Model; model != nil {
if props := model.Properties; props != nil {
state.TargetNetworkId = pointer.From(props.RecoveryNetworkId)
state.SCVMMname = pointer.From(props.PrimaryFabricFriendlyName)
state.NetworkName = pointer.From(props.PrimaryNetworkFriendlyName)
}
}

return metadata.Encode(&state)
},
}
}

func (s HyperVNetworkMappingResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
id, err := replicationnetworkmappings.ParseReplicationNetworkMappingID(metadata.ResourceData.Id())
if err != nil {
return fmt.Errorf("parsing id: %+v", err)
}

client := metadata.Client.RecoveryServices.NetworkMappingClient

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

return nil
},
}
}

func fetchFabricIdByFriendlyName(ctx context.Context, fabricClient *replicationfabrics.ReplicationFabricsClient, vaultId, friendlyName string) (string, error) {
parsedVaultId, err := replicationfabrics.ParseVaultID(vaultId)
if err != nil {
return "", fmt.Errorf("parsing vault id: %+v", err)
}

fabrics, err := fabricClient.ListComplete(ctx, *parsedVaultId)
if err != nil {
return "", fmt.Errorf("listing fabrics: %+v", err)
}

for _, fabric := range fabrics.Items {
if fabric.Properties != nil && fabric.Properties.FriendlyName != nil && *fabric.Properties.FriendlyName == friendlyName && fabric.Id != nil {
return handleAzureSdkForGoBug2824(*fabric.Id), nil
}
}

return "", fmt.Errorf("fabric not found")
}

func fetchReplicationNetworkIdByFriendlyName(ctx context.Context, networksClient *replicationnetworks.ReplicationNetworksClient, fabricId, friendlyName string) (string, error) {
parsedFabricId, err := replicationnetworks.ParseReplicationFabricID(fabricId)
if err != nil {
return "", fmt.Errorf("parsing fabric id: %+v", err)
}

networks, err := networksClient.ListByReplicationFabricsComplete(ctx, *parsedFabricId)
if err != nil {
return "", fmt.Errorf("listing networks: %+v", err)
}

for _, network := range networks.Items {
if network.Properties != nil && network.Properties.FriendlyName != nil && *network.Properties.FriendlyName == friendlyName {
return handleAzureSdkForGoBug2824(*network.Id), nil
}
}

return "", fmt.Errorf("replication network not found")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package recoveryservices_test

import (
"context"
"fmt"
"os"
"testing"

"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2022-10-01/replicationnetworkmappings"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type SiteRecoveryHyperVNetworkMappingResource struct{}

func TestAccSiteRecoveryHyperVNetworkMapping_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_site_recovery_hyperv_network_mapping", "test")
r := SiteRecoveryHyperVNetworkMappingResource{}

vaultId := os.Getenv("ARM_TEST_HYPERV_VAULT_ID")
vmmName := os.Getenv("ARM_TEST_HYPERV_VMM_NAME")
networkName := os.Getenv("ARM_TEST_HYPERV_VMM_NETWORK_NAME")

if vaultId == "" || vmmName == "" || networkName == "" {
t.Skip("Skipping as ARM_TEST_HYPERV_VAULT_ID, ARM_TEST_HYPERV_VMM_NAME or ARM_TEST_HYPERV_VMM_NETWORK_NAME not set")
return
}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data, vaultId, vmmName, networkName),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (SiteRecoveryHyperVNetworkMappingResource) basic(data acceptance.TestData, vaultId, vmmName, networkName string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "target" {
name = "acctestRG-recovery-%[1]d-1"
location = "%[2]s"
}
resource "azurerm_virtual_network" "target" {
name = "network-%[1]d"
resource_group_name = azurerm_resource_group.target.name
address_space = ["192.168.2.0/24"]
location = azurerm_resource_group.target.location
}
resource "azurerm_site_recovery_hyperv_network_mapping" "test" {
name = "mapping-%[1]d"
recovery_vault_id = "%[3]s"
source_system_center_virtual_machine_manager_name = "%[4]s"
source_network_name = "%[5]s"
target_network_id = azurerm_virtual_network.target.id
}
`, data.RandomInteger, data.Locations.Primary, vaultId, vmmName, networkName)
}

func (t SiteRecoveryHyperVNetworkMappingResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := replicationnetworkmappings.ParseReplicationNetworkMappingID(state.ID)
if err != nil {
return nil, err
}

resp, err := clients.RecoveryServices.NetworkMappingClient.Get(ctx, *id)
if err != nil {
return nil, fmt.Errorf("reading Recovery Service Network Mapping %q: %+v", id, err)
}
if resp.Model == nil {
return nil, fmt.Errorf("retrieving Recovery Service Network Mapping %q: `model` was nil", id)
}

return utils.Bool(resp.Model.Id != nil), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ func (h HyperVReplicationPolicyAssociationResource) Arguments() map[string]*plug
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"hyperv_site_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: replicationfabrics.ValidateReplicationFabricID,
},

"policy_id": {
Type: pluginsdk.TypeString,
Required: true,
Expand Down
Loading

0 comments on commit 5c9c115

Please sign in to comment.