diff --git a/internal/services/compute/helpers.go b/internal/services/compute/helpers.go index 7e6cb37174e1..e36dbd5dcf16 100644 --- a/internal/services/compute/helpers.go +++ b/internal/services/compute/helpers.go @@ -37,6 +37,23 @@ func flattenSubResourcesToIDs(input *[]compute.SubResource) []interface{} { return ids } +func flattenSubResourcesToStringIDs(input *[]compute.SubResource) []string { + ids := make([]string, 0) + if input == nil { + return ids + } + + for _, v := range *input { + if v.ID == nil { + continue + } + + ids = append(ids, *v.ID) + } + + return ids +} + func sortSharedImageVersions(values []compute.GalleryImageVersion) ([]compute.GalleryImageVersion, []error) { errors := make([]error, 0) sort.Slice(values, func(i, j int) bool { diff --git a/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source.go b/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source.go new file mode 100644 index 000000000000..7126321eb6a2 --- /dev/null +++ b/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source.go @@ -0,0 +1,342 @@ +package compute + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" + computeValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" + "github.com/tombuildsstuff/kermit/sdk/compute/2022-08-01/compute" +) + +type OrchestratedVirtualMachineScaleSetDataSource struct{} + +var _ sdk.DataSource = OrchestratedVirtualMachineScaleSetDataSource{} + +type OrchestratedVirtualMachineScaleSetDataSourceModel struct { + Name string `tfschema:"name"` + ResourceGroup string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + NetworkInterface []VirtualMachineScaleSetNetworkInterface `tfschema:"network_interface"` + Identity []identity.ModelUserAssigned `tfschema:"identity"` +} + +type VirtualMachineScaleSetNetworkInterface struct { + Name string `tfschema:"name"` + IPConfiguration []VirtualMachineScaleSetNetworkInterfaceIPConfiguration `tfschema:"ip_configuration"` + DNSServers []string `tfschema:"dns_servers"` + AcceleratedNetworkingEnabled bool `tfschema:"accelerated_networking_enabled"` + IPForwardingEnabled bool `tfschema:"ip_forwarding_enabled"` + NetworkSecurityGroupId string `tfschema:"network_security_group_id"` + Primary bool `tfschema:"primary"` +} + +type VirtualMachineScaleSetNetworkInterfaceIPConfiguration struct { + Name string `tfschema:"name"` + ApplicationGatewayBackendAddressPoolIds []string `tfschema:"application_gateway_backend_address_pool_ids"` + ApplicationSecurityGroupIds []string `tfschema:"application_security_group_ids"` + LoadBalancerBackendAddressPoolIds []string `tfschema:"load_balancer_backend_address_pool_ids"` + Primary bool `tfschema:"primary"` + PublicIPAddress []VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddress `tfschema:"public_ip_address"` + SubnetId string `tfschema:"subnet_id"` + Version string `tfschema:"version"` +} + +type VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddress struct { + Name string `tfschema:"name"` + DomainNameLabel string `tfschema:"domain_name_label"` + IdleTimeoutInMinutes int `tfschema:"idle_timeout_in_minutes"` + IPTag []VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddressIPTag `tfschema:"ip_tag"` + PublicIpPrefixId string `tfschema:"public_ip_prefix_id"` + SkuName string `tfschema:"sku_name"` + Version string `tfschema:"version"` +} + +type VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddressIPTag struct { + Tag string `tfschema:"tag"` + Type string `tfschema:"type"` +} + +func (r OrchestratedVirtualMachineScaleSetDataSource) ModelObject() interface{} { + return &OrchestratedVirtualMachineScaleSetDataSourceModel{} +} + +func (r OrchestratedVirtualMachineScaleSetDataSource) ResourceType() string { + return "azurerm_orchestrated_virtual_machine_scale_set" +} + +func (r OrchestratedVirtualMachineScaleSetDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: computeValidate.VirtualMachineName, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + } +} + +func (r OrchestratedVirtualMachineScaleSetDataSource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "location": commonschema.LocationComputed(), + + "network_interface": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "ip_configuration": virtualMachineScaleSetIPConfigurationSchemaForDataSource(), + + "dns_servers": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "accelerated_networking_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "ip_forwarding_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "network_security_group_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "primary": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + }, + }, + }, + + "identity": commonschema.UserAssignedIdentityComputed(), + } +} + +func (r OrchestratedVirtualMachineScaleSetDataSource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Compute.VMScaleSetClient + subscriptionId := metadata.Client.Account.SubscriptionId + + var orchestratedVMSS OrchestratedVirtualMachineScaleSetDataSourceModel + if err := metadata.Decode(&orchestratedVMSS); err != nil { + return err + } + + id := parse.NewVirtualMachineScaleSetID(subscriptionId, orchestratedVMSS.ResourceGroup, orchestratedVMSS.Name) + + existing, err := client.Get(ctx, id.ResourceGroup, id.Name, compute.ExpandTypesForGetVMScaleSetsUserData) + if err != nil { + if utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("%s not found", id) + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + orchestratedVMSS.Location = location.NormalizeNilable(existing.Location) + + if profile := existing.VirtualMachineProfile; profile != nil { + if nwProfile := profile.NetworkProfile; nwProfile != nil { + orchestratedVMSS.NetworkInterface = flattenVirtualMachineScaleSetNetworkInterface(nwProfile.NetworkInterfaceConfigurations) + } + } + + userIdentity, err := flattenOrchestratedVirtualMachineScaleSetIdentityToModel(existing.Identity) + if err != nil { + return err + } + orchestratedVMSS.Identity = userIdentity + + metadata.SetID(id) + + return metadata.Encode(&orchestratedVMSS) + }, + } +} + +func flattenVirtualMachineScaleSetNetworkInterface(input *[]compute.VirtualMachineScaleSetNetworkConfiguration) []VirtualMachineScaleSetNetworkInterface { + if input == nil { + return []VirtualMachineScaleSetNetworkInterface{} + } + + networkInterfaces := make([]VirtualMachineScaleSetNetworkInterface, 0) + for _, v := range *input { + var name, networkSecurityGroupId string + if v.Name != nil { + name = *v.Name + } + if v.NetworkSecurityGroup != nil && v.NetworkSecurityGroup.ID != nil { + networkSecurityGroupId = *v.NetworkSecurityGroup.ID + } + var acceleratedNetworkingEnabled, ipForwardingEnabled, primary bool + if v.EnableAcceleratedNetworking != nil { + acceleratedNetworkingEnabled = *v.EnableAcceleratedNetworking + } + if v.EnableIPForwarding != nil { + ipForwardingEnabled = *v.EnableIPForwarding + } + if v.Primary != nil { + primary = *v.Primary + } + + var dnsServers []string + if settings := v.DNSSettings; settings != nil { + dnsServers = *v.DNSSettings.DNSServers + } + + networkInterfaces = append(networkInterfaces, VirtualMachineScaleSetNetworkInterface{ + Name: name, + NetworkSecurityGroupId: networkSecurityGroupId, + AcceleratedNetworkingEnabled: acceleratedNetworkingEnabled, + IPForwardingEnabled: ipForwardingEnabled, + Primary: primary, + DNSServers: dnsServers, + IPConfiguration: flattenOrchestratedVirtualMachineScaleSetNetworkInterfaceIPConfiguration(v.IPConfigurations), + }) + } + + return networkInterfaces +} + +func flattenOrchestratedVirtualMachineScaleSetNetworkInterfaceIPConfiguration(input *[]compute.VirtualMachineScaleSetIPConfiguration) []VirtualMachineScaleSetNetworkInterfaceIPConfiguration { + if input == nil { + return []VirtualMachineScaleSetNetworkInterfaceIPConfiguration{} + } + + ipConfigurations := make([]VirtualMachineScaleSetNetworkInterfaceIPConfiguration, 0) + for _, v := range *input { + var name, subnetId string + if v.Name != nil { + name = *v.Name + } + if v.Subnet != nil && v.Subnet.ID != nil { + subnetId = *v.Subnet.ID + } + + var primary bool + if v.Primary != nil { + primary = *v.Primary + } + + ipConfigurations = append(ipConfigurations, VirtualMachineScaleSetNetworkInterfaceIPConfiguration{ + Name: name, + SubnetId: subnetId, + Primary: primary, + PublicIPAddress: flattenOrchestratedVirtualMachineScaleSetPublicIPAddress(v.PublicIPAddressConfiguration), + ApplicationGatewayBackendAddressPoolIds: flattenSubResourcesToStringIDs(v.ApplicationGatewayBackendAddressPools), + ApplicationSecurityGroupIds: flattenSubResourcesToStringIDs(v.ApplicationSecurityGroups), + LoadBalancerBackendAddressPoolIds: flattenSubResourcesToStringIDs(v.LoadBalancerBackendAddressPools), + }) + } + + return ipConfigurations +} + +func flattenOrchestratedVirtualMachineScaleSetPublicIPAddress(input *compute.VirtualMachineScaleSetPublicIPAddressConfiguration) []VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddress { + if input == nil { + return []VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddress{} + } + + ipTags := make([]VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddressIPTag, 0) + if input.IPTags != nil { + for _, rawTag := range *input.IPTags { + var tag, tagType string + + if rawTag.IPTagType != nil { + tagType = *rawTag.IPTagType + } + + if rawTag.Tag != nil { + tag = *rawTag.Tag + } + + ipTags = append(ipTags, VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddressIPTag{ + Tag: tag, + Type: tagType, + }) + } + } + + var domainNameLabel, name, publicIPPrefixId, version string + if input.DNSSettings != nil && input.DNSSettings.DomainNameLabel != nil { + domainNameLabel = *input.DNSSettings.DomainNameLabel + } + + if input.Name != nil { + name = *input.Name + } + + if input.PublicIPPrefix != nil && input.PublicIPPrefix.ID != nil { + publicIPPrefixId = *input.PublicIPPrefix.ID + } + + if input.PublicIPAddressVersion != "" { + version = string(input.PublicIPAddressVersion) + } + + var idleTimeoutInMinutes int + if input.IdleTimeoutInMinutes != nil { + idleTimeoutInMinutes = int(*input.IdleTimeoutInMinutes) + } + + return []VirtualMachineScaleSetNetworkInterfaceIPConfigurationPublicIPAddress{{ + Name: name, + DomainNameLabel: domainNameLabel, + IdleTimeoutInMinutes: idleTimeoutInMinutes, + IPTag: ipTags, + PublicIpPrefixId: publicIPPrefixId, + Version: version, + }} +} + +func flattenOrchestratedVirtualMachineScaleSetIdentityToModel(input *compute.VirtualMachineScaleSetIdentity) ([]identity.ModelUserAssigned, error) { + if input == nil { + return nil, nil + } + + identityIds := make(map[string]identity.UserAssignedIdentityDetails, 0) + for k, v := range input.UserAssignedIdentities { + if v != nil { + identityIds[k] = identity.UserAssignedIdentityDetails{ + ClientId: v.ClientID, + PrincipalId: v.PrincipalID, + } + } + } + + tmp := identity.UserAssignedMap{ + Type: identity.Type(input.Type), + IdentityIds: identityIds, + } + + output, err := identity.FlattenUserAssignedMapToModel(&tmp) + if err != nil { + return nil, fmt.Errorf("expanding `identity`: %+v", err) + } + + return *output, nil +} diff --git a/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source_test.go b/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source_test.go new file mode 100644 index 000000000000..82e8e530416f --- /dev/null +++ b/internal/services/compute/orchestrated_virtual_machine_scale_set_data_source_test.go @@ -0,0 +1,37 @@ +package compute_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type OrchestratedVirtualMachineScaleSetDataSource struct{} + +func TestAccOrchestratedVMSSDataSource_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_orchestrated_virtual_machine_scale_set", "test") + d := OrchestratedVirtualMachineScaleSetDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("location").HasValue(data.Locations.Primary), + check.That(data.ResourceName).Key("network_interface.#").HasValue("1"), + ), + }, + }) +} + +func (OrchestratedVirtualMachineScaleSetDataSource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data azurerm_orchestrated_virtual_machine_scale_set test { + name = azurerm_orchestrated_virtual_machine_scale_set.test.name + resource_group_name = azurerm_orchestrated_virtual_machine_scale_set.test.resource_group_name +} +`, OrchestratedVirtualMachineScaleSetResource{}.linuxInstances(data)) +} diff --git a/internal/services/compute/registration.go b/internal/services/compute/registration.go index 69741d56579e..2d2ae114fb33 100644 --- a/internal/services/compute/registration.go +++ b/internal/services/compute/registration.go @@ -78,7 +78,9 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { } func (r Registration) DataSources() []sdk.DataSource { - return []sdk.DataSource{} + return []sdk.DataSource{ + OrchestratedVirtualMachineScaleSetDataSource{}, + } } func (r Registration) Resources() []sdk.Resource { diff --git a/website/docs/d/orchestrated_virtual_machine_scale_set.html.markdown b/website/docs/d/orchestrated_virtual_machine_scale_set.html.markdown new file mode 100644 index 000000000000..ceb255d2d0fe --- /dev/null +++ b/website/docs/d/orchestrated_virtual_machine_scale_set.html.markdown @@ -0,0 +1,119 @@ +--- +subcategory: "Compute" +layout: "azurerm" +page_title: "Azure Resource Manager: Data Source: azurerm_orchestrated_virtual_machine_scale_set" +description: |- + Gets information about an existing Orchestrated Virtual Machine Scale Set. +--- + +# Data Source: azurerm_orchestrated_virtual_machine_scale_set + +Use this data source to access information about an existing Orchestrated Virtual Machine Scale Set. + +## Example Usage + +```hcl +data "azurerm_orchestrated_virtual_machine_scale_set" "example" { + name = "existing" + resource_group_name = "existing" +} + +output "id" { + value = data.azurerm_orchestrated_virtual_machine_scale_set.example.id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name of this Orchestrated Virtual Machine Scale Set. + +* `resource_group_name` - (Required) The name of the Resource Group where the Orchestrated Virtual Machine Scale Set exists. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Virtual Machine Scale Set. + +* `location` - The Azure Region in which this Orchestrated Virtual Machine Scale Set exists. + +* `identity` - A `identity` block as defined below. + +* `network_interface` - A list of `network_interface` blocks as defined below. + +--- + +An `identity` block exports the following: + +* `type` - The type of Managed Service Identity that is configured on this Orchestrated Virtual Machine Scale Set. + +* `principal_id` - The Principal ID of the System Assigned Managed Service Identity that is configured on this Orchestrated Virtual Machine Scale Set. + +* `tenant_id` - The Tenant ID of the System Assigned Managed Service Identity that is configured on this Orchestrated Virtual Machine Scale Set. + +* `identity_ids` - The list of User Assigned Managed Identity IDs assigned to this Orchestrated Virtual Machine Scale Set. + +--- + +`network_interface` exports the following: + +* `name` - The name of the network interface configuration. + +* `primary` - Whether network interfaces created from the network interface configuration will be the primary NIC of the VM. + +* `ip_configuration` - An `ip_configuration` block as documented below. + +* `accelerated_networking_enabled` - Is accelerated networking enabled? + +* `dns_servers` - An array of the DNS servers in use. + +* `ip_forwarding_enabled` - Is IP forwarding enabled? + +* `network_security_group_id` - The identifier for the network security group. + +`ip_configuration` exports the following: + +* `name` - The name of the IP configuration. + +* `subnet_id` - The the identifier of the subnet. + +* `application_gateway_backend_address_pool_ids` - An array of references to backend address pools of application gateways. + +* `load_balancer_backend_address_pool_ids` - An array of references to backend address pools of load balancers. + +* `load_balancer_inbound_nat_rules_ids` - An array of references to inbound NAT pools for load balancers. + +* `primary` - If this ip_configuration is the primary one. + +* `application_security_group_ids` - The application security group IDs to use. + +* `public_ip_address` - The virtual machines scale set IP Configuration's PublicIPAddress configuration. The `public_ip_address` is documented below. + +`public_ip_address` exports the following: + +* `name` - The name of the public IP address configuration + +* `idle_timeout_in_minutes` - The idle timeout in minutes. + +* `domain_name_label` - The domain name label for the DNS settings. + +* `ip_tag` - A list of `ip_tag` blocks as defined below. + +* `public_ip_prefix_id` - The ID of the public IP prefix. + +* `version` - The Internet Protocol Version of the public IP address. + + +`ip_tag` exports the following: + +* `tag` - The IP Tag associated with the Public IP. + +* `type` - The Type of IP Tag. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the Orchestrated Virtual Machine Scale Set.