Skip to content

Commit

Permalink
azurerm_site_recovery_replicated_vm - adding property `recovery_loa…
Browse files Browse the repository at this point in the history
…d_balancer_backend_address_pool_ids` (#28398)

* add recovery_load_balancer_backend_address_pool_ids arg to site_recovery_replicated_vm resource

* Fix lint
  • Loading branch information
sreallymatt authored Jan 2, 2025
1 parent e450bfa commit 5bce84c
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2022-03-01/proximityplacementgroups"
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2023-04-02/disks"
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2024-03-01/virtualmachines"
"github.com/hashicorp/go-azure-sdk/resource-manager/network/2023-09-01/loadbalancers"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2024-04-01/replicationfabrics"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2024-04-01/replicationpolicies"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2024-04-01/replicationprotecteditems"
"github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservicessiterecovery/2024-04-01/replicationprotectioncontainers"
"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/clients"
Expand Down Expand Up @@ -307,7 +309,7 @@ func resourceSiteRecoveryReplicatedVM() *pluginsdk.Resource {
}

func networkInterfaceResource() *pluginsdk.Resource {
out := &pluginsdk.Resource{
return &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"source_network_interface_id": {
Type: pluginsdk.TypeString,
Expand Down Expand Up @@ -354,6 +356,15 @@ func networkInterfaceResource() *pluginsdk.Resource {
ValidateFunc: azure.ValidateResourceID,
},

"recovery_load_balancer_backend_address_pool_ids": {
Type: pluginsdk.TypeSet,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateFunc: loadbalancers.ValidateLoadBalancerBackendAddressPoolID,
},
},

"recovery_public_ip_address_id": {
Type: pluginsdk.TypeString,
Optional: true,
Expand All @@ -362,8 +373,6 @@ func networkInterfaceResource() *pluginsdk.Resource {
},
},
}

return out
}

func diskEncryptionResource() *pluginsdk.Resource {
Expand Down Expand Up @@ -561,19 +570,25 @@ func resourceSiteRecoveryReplicatedItemUpdateInternal(ctx context.Context, d *pl
testSubNetName := vmNicInput["failover_test_subnet_name"].(string)
testPublicIpAddressID := vmNicInput["failover_test_public_ip_address_id"].(string)

var recoveryLoadBalancerBackendPoolIds *[]string
if ids, ok := vmNicInput["recovery_load_balancer_backend_address_pool_ids"].(*schema.Set); ok && ids.Len() > 0 {
recoveryLoadBalancerBackendPoolIds = utils.ExpandStringSlice(ids.List())
}

nicId := findNicId(state, sourceNicId)
if nicId == nil {
return fmt.Errorf("updating replicated vm %s (vault %s): Trying to update NIC that is not known by Azure %s", name, vaultName, sourceNicId)
}
ipConfig := []replicationprotecteditems.IPConfigInputDetails{
{
RecoverySubnetName: &targetSubnetName,
RecoveryStaticIPAddress: &targetStaticIp,
RecoveryPublicIPAddressId: &recoveryPublicIPAddressID,
TfoStaticIPAddress: &testStaticIp,
TfoPublicIPAddressId: &testPublicIpAddressID,
TfoSubnetName: &testSubNetName,
IsPrimary: utils.Bool(true), // currently we can only set one IPconfig for a nic, so we dont need to expose this to users.
RecoverySubnetName: &targetSubnetName,
RecoveryStaticIPAddress: &targetStaticIp,
RecoveryLBBackendAddressPoolIds: recoveryLoadBalancerBackendPoolIds,
RecoveryPublicIPAddressId: &recoveryPublicIPAddressID,
TfoStaticIPAddress: &testStaticIp,
TfoPublicIPAddressId: &testPublicIpAddressID,
TfoSubnetName: &testSubNetName,
IsPrimary: utils.Bool(true), // currently we can only set one IPconfig for a nic, so we dont need to expose this to users.
},
}
vmNics = append(vmNics, replicationprotecteditems.VMNicInputDetails{
Expand Down Expand Up @@ -907,6 +922,9 @@ func resourceSiteRecoveryReplicatedItemRead(d *pluginsdk.ResourceData, meta inte
if ipConfig.RecoverySubnetName != nil {
nicOutput["target_subnet_name"] = *ipConfig.RecoverySubnetName
}
if ipConfig.RecoveryLBBackendAddressPoolIds != nil {
nicOutput["recovery_load_balancer_backend_address_pool_ids"] = schema.NewSet(schema.HashString, utils.FlattenStringSlice(ipConfig.RecoveryLBBackendAddressPoolIds))
}
if ipConfig.RecoveryPublicIPAddressId != nil {
nicOutput["recovery_public_ip_address_id"] = *ipConfig.RecoveryPublicIPAddressId
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ func TestAccSiteRecoveryReplicatedVm_zone2zone(t *testing.T) {
})
}

func TestAccSiteRecoveryReplicatedVm_zone2zoneWithLoadBalancerBackendPool(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_site_recovery_replicated_vm", "test")
r := SiteRecoveryReplicatedVmResource{}

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

func TestAccSiteRecoveryReplicatedVm_targetDiskEncryption(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_site_recovery_replicated_vm", "test")
r := SiteRecoveryReplicatedVmResource{}
Expand Down Expand Up @@ -1322,6 +1337,245 @@ resource "azurerm_site_recovery_replicated_vm" "test" {
`, data.RandomInteger, data.Locations.Primary, data.Locations.Secondary)
}

func (SiteRecoveryReplicatedVmResource) zone2zoneWithLoadBalancerBackendPool(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}
resource "azurerm_resource_group" "test1" {
name = "acctestRG-recovery-%[1]d-1"
location = "%[2]s"
}
resource "azurerm_resource_group" "test2" {
name = "acctestRG-recovery-%[1]d-2"
location = "%[2]s"
}
resource "azurerm_storage_account" "test" {
name = "acct%[1]d"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_virtual_network" "test1" {
name = "vnet-%[1]d-1"
resource_group_name = azurerm_resource_group.test1.name
address_space = ["192.168.1.0/24"]
location = azurerm_resource_group.test1.location
}
resource "azurerm_virtual_network" "test2" {
name = "vnet-%[1]d-2"
resource_group_name = azurerm_resource_group.test2.name
address_space = ["192.168.2.0/24"]
location = azurerm_resource_group.test2.location
}
resource "azurerm_subnet" "test1" {
name = "subnet-%[1]d-1"
resource_group_name = azurerm_resource_group.test1.name
virtual_network_name = azurerm_virtual_network.test1.name
address_prefixes = ["192.168.1.0/24"]
}
resource "azurerm_subnet" "test2" {
name = "subnet-%[1]d-1"
resource_group_name = azurerm_resource_group.test2.name
virtual_network_name = azurerm_virtual_network.test2.name
address_prefixes = ["192.168.2.0/24"]
}
resource "azurerm_public_ip" "test" {
name = "public-ip-%[1]d"
allocation_method = "Static"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
sku = "Standard"
zones = ["1", "2", "3"]
}
resource "azurerm_nat_gateway" "test" {
name = "nat-gateway-%[1]d"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
sku_name = "Standard"
idle_timeout_in_minutes = 10
}
resource "azurerm_nat_gateway_public_ip_association" "test" {
nat_gateway_id = azurerm_nat_gateway.test.id
public_ip_address_id = azurerm_public_ip.test.id
}
resource "azurerm_subnet_nat_gateway_association" "test" {
nat_gateway_id = azurerm_nat_gateway.test.id
subnet_id = azurerm_subnet.test1.id
}
resource "azurerm_lb" "test" {
name = "lb-%[1]d"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
sku = "Standard"
frontend_ip_configuration {
name = "lb-frontend-%[1]d"
subnet_id = azurerm_subnet.test1.id
zones = ["1", "2", "3"]
}
}
resource "azurerm_lb_backend_address_pool" "test" {
name = "lb-backend-address-pool-%[1]d"
loadbalancer_id = azurerm_lb.test.id
}
resource "azurerm_network_interface" "test" {
name = "vm-nic-%[1]d"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
ip_configuration {
name = "ip-config-%[1]d"
subnet_id = azurerm_subnet.test1.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_network_interface_backend_address_pool_association" "test" {
ip_configuration_name = azurerm_network_interface.test.ip_configuration[0].name
network_interface_id = azurerm_network_interface.test.id
backend_address_pool_id = azurerm_lb_backend_address_pool.test.id
}
resource "azurerm_virtual_machine" "test" {
name = "vm-%[1]d"
location = azurerm_resource_group.test1.location
resource_group_name = azurerm_resource_group.test1.name
vm_size = "Standard_B1s"
network_interface_ids = [azurerm_network_interface.test.id]
zones = ["1"]
storage_image_reference {
publisher = "OpenLogic"
offer = "CentOS"
sku = "7.5"
version = "latest"
}
storage_os_disk {
name = "disk-%[1]d"
os_type = "Linux"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Premium_LRS"
}
os_profile {
admin_username = "testadmin"
admin_password = "Password1234!"
computer_name = "vm-%[1]d"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
resource "azurerm_recovery_services_vault" "test" {
name = "recovery-vault-%[1]d"
location = azurerm_resource_group.test2.location
resource_group_name = azurerm_resource_group.test2.name
sku = "Standard"
}
resource "azurerm_site_recovery_fabric" "test" {
name = "fabric-%[1]d"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
location = azurerm_resource_group.test1.location
}
resource "azurerm_site_recovery_protection_container" "test1" {
name = "protection-container-%[1]d-1"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
recovery_fabric_name = azurerm_site_recovery_fabric.test.name
}
resource "azurerm_site_recovery_protection_container" "test2" {
name = "protection-container-%[1]d-2"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
recovery_fabric_name = azurerm_site_recovery_fabric.test.name
}
resource "azurerm_site_recovery_replication_policy" "test" {
name = "policy-%[1]d"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
recovery_point_retention_in_minutes = 24 * 60
application_consistent_snapshot_frequency_in_minutes = 4 * 60
}
resource "azurerm_site_recovery_protection_container_mapping" "test" {
name = "container-mapping-%[1]d"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
recovery_fabric_name = azurerm_site_recovery_fabric.test.name
recovery_source_protection_container_name = azurerm_site_recovery_protection_container.test1.name
recovery_target_protection_container_id = azurerm_site_recovery_protection_container.test2.id
recovery_replication_policy_id = azurerm_site_recovery_replication_policy.test.id
}
resource "azurerm_site_recovery_replicated_vm" "test" {
name = "vm-replication-%[1]d"
resource_group_name = azurerm_resource_group.test2.name
recovery_vault_name = azurerm_recovery_services_vault.test.name
source_recovery_fabric_name = azurerm_site_recovery_fabric.test.name
source_vm_id = azurerm_virtual_machine.test.id
recovery_replication_policy_id = azurerm_site_recovery_replication_policy.test.id
source_recovery_protection_container_name = azurerm_site_recovery_protection_container.test1.name
target_network_id = azurerm_virtual_network.test1.id
target_resource_group_id = azurerm_resource_group.test2.id
target_recovery_fabric_id = azurerm_site_recovery_fabric.test.id
target_recovery_protection_container_id = azurerm_site_recovery_protection_container.test2.id
target_zone = "2"
managed_disk {
disk_id = azurerm_virtual_machine.test.storage_os_disk[0].managed_disk_id
staging_storage_account_id = azurerm_storage_account.test.id
target_resource_group_id = azurerm_resource_group.test2.id
target_disk_type = "Premium_LRS"
target_replica_disk_type = "Premium_LRS"
}
network_interface {
source_network_interface_id = azurerm_network_interface.test.id
target_subnet_name = azurerm_subnet.test2.name
recovery_public_ip_address_id = azurerm_public_ip.test.id
recovery_load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.test.id]
}
depends_on = [
azurerm_site_recovery_protection_container_mapping.test,
azurerm_nat_gateway.test,
azurerm_subnet_nat_gateway_association.test,
azurerm_nat_gateway_public_ip_association.test,
]
}
`, data.RandomInteger, data.Locations.Primary)
}

func (SiteRecoveryReplicatedVmResource) targetDiskEncryption(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
6 changes: 4 additions & 2 deletions website/docs/r/site_recovery_replicated_vm.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,15 @@ A `network_interface` block supports the following:

* `target_static_ip` - (Optional) Static IP to assign when a failover is done.

* `target_subnet_name` - (Optional) Name of the subnet to to use when a failover is done.
* `target_subnet_name` - (Optional) Name of the subnet to use when a failover is done.

* `recovery_load_balancer_backend_address_pool_ids` - (Optional) A list of IDs of Load Balancer Backend Address Pools to use when a failover is done.

* `recovery_public_ip_address_id` - (Optional) Id of the public IP object to use when a failover is done.

* `failover_test_static_ip` - (Optional) Static IP to assign when a test failover is done.

* `failover_test_subnet_name` - (Optional) Name of the subnet to to use when a test failover is done.
* `failover_test_subnet_name` - (Optional) Name of the subnet to use when a test failover is done.

* `failover_test_public_ip_address_id` - (Optional) Id of the public IP object to use when a test failover is done.

Expand Down

0 comments on commit 5bce84c

Please sign in to comment.