diff --git a/internal/services/network/bastion_host_data_source.go b/internal/services/network/bastion_host_data_source.go index 8ac92c934c56e..927f4aa14877c 100644 --- a/internal/services/network/bastion_host_data_source.go +++ b/internal/services/network/bastion_host_data_source.go @@ -89,6 +89,11 @@ func dataSourceBastionHost() *pluginsdk.Resource { Computed: true, }, + "session_recording_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + "dns_name": { Type: pluginsdk.TypeString, Computed: true, @@ -136,6 +141,7 @@ func dataSourceBastionHostRead(d *pluginsdk.ResourceData, meta interface{}) erro d.Set("ip_connect_enabled", props.EnableIPConnect) d.Set("shareable_link_enabled", props.EnableShareableLink) d.Set("tunneling_enabled", props.EnableTunneling) + d.Set("session_recording_enabled", props.EnableSessionRecording) copyPasteEnabled := true if props.DisableCopyPaste != nil { diff --git a/internal/services/network/bastion_host_data_source_test.go b/internal/services/network/bastion_host_data_source_test.go index 8cd8bbd4e38da..c7ab367c939ac 100644 --- a/internal/services/network/bastion_host_data_source_test.go +++ b/internal/services/network/bastion_host_data_source_test.go @@ -29,6 +29,7 @@ func TestAccBastionHostDataSource_basic(t *testing.T) { check.That(data.ResourceName).Key("file_copy_enabled").Exists(), check.That(data.ResourceName).Key("ip_connect_enabled").Exists(), check.That(data.ResourceName).Key("shareable_link_enabled").Exists(), + check.That(data.ResourceName).Key("session_recording_enabled").Exists(), check.That(data.ResourceName).Key("ip_configuration.0.name").Exists(), check.That(data.ResourceName).Key("ip_configuration.0.subnet_id").Exists(), check.That(data.ResourceName).Key("ip_configuration.0.public_ip_address_id").Exists(), diff --git a/internal/services/network/bastion_host_resource.go b/internal/services/network/bastion_host_resource.go index aeb1158bde88e..2add825f1185b 100644 --- a/internal/services/network/bastion_host_resource.go +++ b/internal/services/network/bastion_host_resource.go @@ -28,6 +28,7 @@ var skuWeight = map[string]int8{ "Developer": 1, "Basic": 2, "Standard": 3, + "Premium": 4, } func resourceBastionHost() *pluginsdk.Resource { @@ -140,6 +141,12 @@ func resourceBastionHost() *pluginsdk.Resource { Default: false, }, + "session_recording_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "virtual_network_id": { Type: pluginsdk.TypeString, Optional: true, @@ -184,29 +191,34 @@ func resourceBastionHostCreate(d *pluginsdk.ResourceData, meta interface{}) erro kerberosEnabled := d.Get("kerberos_enabled").(bool) shareableLinkEnabled := d.Get("shareable_link_enabled").(bool) tunnelingEnabled := d.Get("tunneling_enabled").(bool) + sessionRecordingEnabled := d.Get("session_recording_enabled").(bool) + + if scaleUnits > 2 && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`scale_units` only can be changed when `sku` is `Standard` or `Premium`. `scale_units` is always `2` when `sku` is `Basic`") + } - if scaleUnits > 2 && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`scale_units` only can be changed when `sku` is `Standard`. `scale_units` is always `2` when `sku` is `Basic`") + if fileCopyEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`file_copy_enabled` is only supported when `sku` is `Standard` or `Premium`") } - if fileCopyEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`file_copy_enabled` is only supported when `sku` is `Standard`") + if ipConnectEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`ip_connect_enabled` is only supported when `sku` is `Standard` or `Premium`") } - if ipConnectEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`ip_connect_enabled` is only supported when `sku` is `Standard`") + if kerberosEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`kerberos_enabled` is only supported when `sku` is `Standard` or `Premium`") } - if kerberosEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`kerberos_enabled` is only supported when `sku` is `Standard`") + if shareableLinkEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`shareable_link_enabled` is only supported when `sku` is `Standard` or `Premium`") } - if shareableLinkEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`shareable_link_enabled` is only supported when `sku` is `Standard`") + if tunnelingEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`tunneling_enabled` is only supported when `sku` is `Standard` or `Premium`") } - if tunnelingEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`tunneling_enabled` is only supported when `sku` is `Standard`") + if sessionRecordingEnabled && sku != bastionhosts.BastionHostSkuNamePremium { + return fmt.Errorf("`session_recording_enabled` is only supported when `sku` is `Premium`") } existing, err := client.Get(ctx, id) @@ -256,6 +268,10 @@ func resourceBastionHostCreate(d *pluginsdk.ResourceData, meta interface{}) erro parameters.Properties.EnableTunneling = pointer.To(tunnelingEnabled) } + if sessionRecordingEnabled { + parameters.Properties.EnableSessionRecording = pointer.To(sessionRecordingEnabled) + } + if v, ok := d.GetOk("virtual_network_id"); ok { if sku != bastionhosts.BastionHostSkuNameDeveloper { return fmt.Errorf("`virtual_network_id` is only supported when `sku` is `Developer`") @@ -315,44 +331,52 @@ func resourceBastionHostUpdate(d *pluginsdk.ResourceData, meta interface{}) erro if d.HasChange("file_copy_enabled") { fileCopyEnabled := d.Get("file_copy_enabled").(bool) - if fileCopyEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`file_copy_enabled` is only supported when `sku` is `Standard`") + if fileCopyEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`file_copy_enabled` is only supported when `sku` is `Standard` or `Premium`") } payload.Properties.EnableFileCopy = pointer.To(fileCopyEnabled) } if d.HasChange("ip_connect_enabled") { ipConnectEnabled := d.Get("ip_connect_enabled").(bool) - if ipConnectEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`ip_connect_enabled` is only supported when `sku` is `Standard`") + if ipConnectEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`ip_connect_enabled` is only supported when `sku` is `Standard` or `Premium`") } payload.Properties.EnableIPConnect = pointer.To(ipConnectEnabled) } if d.HasChange("scale_units") { scaleUnits := d.Get("scale_units").(int) - if scaleUnits > 2 && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`scale_units` only can be changed when `sku` is `Standard`. `scale_units` is always `2` when `sku` is `Basic`") + if scaleUnits > 2 && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`scale_units` only can be changed when `sku` is `Standard` or `Premium`. `scale_units` is always `2` when `sku` is `Basic`") } payload.Properties.ScaleUnits = pointer.To(int64(scaleUnits)) } if d.HasChange("shareable_link_enabled") { shareableLinkEnabled := d.Get("shareable_link_enabled").(bool) - if shareableLinkEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`shareable_link_enabled` is only supported when `sku` is `Standard`") + if shareableLinkEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`shareable_link_enabled` is only supported when `sku` is `Standard` or `Premium`") } payload.Properties.EnableShareableLink = pointer.To(shareableLinkEnabled) } if d.HasChange("tunneling_enabled") { tunnelingEnabled := d.Get("tunneling_enabled").(bool) - if tunnelingEnabled && sku == bastionhosts.BastionHostSkuNameBasic { - return fmt.Errorf("`tunneling_enabled` is only supported when `sku` is `Standard`") + if tunnelingEnabled && (sku != bastionhosts.BastionHostSkuNameStandard && sku != bastionhosts.BastionHostSkuNamePremium) { + return fmt.Errorf("`tunneling_enabled` is only supported when `sku` is `Standard` or `Premium`") } payload.Properties.EnableTunneling = pointer.To(tunnelingEnabled) } + if d.HasChange("session_recording_enabled") { + sessionRecordingEnabled := d.Get("session_recording_enabled").(bool) + if sessionRecordingEnabled && sku == bastionhosts.BastionHostSkuNamePremium { + return fmt.Errorf("`session_recording_enabled` is only supported when `sku` is `Premium`") + } + payload.Properties.EnableSessionRecording = pointer.To(sessionRecordingEnabled) + } + if d.HasChange("tags") { payload.Tags = tags.Expand(d.Get("tags").(map[string]interface{})) @@ -405,6 +429,7 @@ func resourceBastionHostRead(d *pluginsdk.ResourceData, meta interface{}) error d.Set("kerberos_enabled", props.EnableKerberos) d.Set("shareable_link_enabled", props.EnableShareableLink) d.Set("tunneling_enabled", props.EnableTunneling) + d.Set("session_recording_enabled", props.EnableSessionRecording) virtualNetworkId := "" if vnet := props.VirtualNetwork; vnet != nil { diff --git a/internal/services/network/bastion_host_resource_test.go b/internal/services/network/bastion_host_resource_test.go index d28cd3eccedfc..f0adc58f988f1 100644 --- a/internal/services/network/bastion_host_resource_test.go +++ b/internal/services/network/bastion_host_resource_test.go @@ -138,6 +138,21 @@ func TestAccBastionHost_developerSku(t *testing.T) { }) } +func TestAccBastionHost_premiumSku(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_bastion_host", "test") + r := BastionHostResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.premiumSku(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (BastionHostResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := bastionhosts.ParseBastionHostID(state.ID) if err != nil { @@ -445,3 +460,57 @@ resource "azurerm_bastion_host" "test" { } `, data.RandomInteger, data.Locations.Ternary, data.RandomString, data.RandomString) } + +func (BastionHostResource) premiumSku(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-bastion-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestVNet%s" + address_space = ["192.168.1.0/24"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "AzureBastionSubnet" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["192.168.1.224/27"] +} + +resource "azurerm_public_ip" "test" { + name = "acctestBastionPIP%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_bastion_host" "test" { + name = "acctestBastion%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Premium" + file_copy_enabled = true + ip_connect_enabled = true + kerberos_enabled = true + shareable_link_enabled = true + tunneling_enabled = true + session_recording_enabled = true + + ip_configuration { + name = "ip-configuration" + subnet_id = azurerm_subnet.test.id + public_ip_address_id = azurerm_public_ip.test.id + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger, data.RandomString) +} diff --git a/website/docs/r/bastion_host.html.markdown b/website/docs/r/bastion_host.html.markdown index a19ba1120c285..74512c8c2e06b 100644 --- a/website/docs/r/bastion_host.html.markdown +++ b/website/docs/r/bastion_host.html.markdown @@ -72,7 +72,7 @@ The following arguments are supported: ~> **Note:** `file_copy_enabled` is only supported when `sku` is `Standard`. -* `sku` - (Optional) The SKU of the Bastion Host. Accepted values are `Developer`, `Basic` and `Standard`. Defaults to `Basic`. +* `sku` - (Optional) The SKU of the Bastion Host. Accepted values are `Developer`, `Basic`, `Standard` and `Premium`. Defaults to `Basic`. ~> **Note** Downgrading the SKU will force a new resource to be created. @@ -98,6 +98,10 @@ The following arguments are supported: ~> **Note:** `tunneling_enabled` is only supported when `sku` is `Standard`. +* `session_recording_enabled` - (Optional) Is Session Recording feature enabled for the Bastion Host. Defaults to `false`. + +~> **Note:** `session_recording_enabled` is only supported when `sku` is `Premium`. + * `virtual_network_id` - (Optional) The ID of the Virtual Network for the Developer Bastion Host. Changing this forces a new resource to be created. * `tags` - (Optional) A mapping of tags to assign to the resource.