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_mobile_network_attached_data_network; new datasource: azurerm_mobile_network_attached_data_network #22168

Merged
merged 12 commits into from
Jul 21, 2023
9 changes: 9 additions & 0 deletions internal/services/mobilenetwork/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package client
import (
"fmt"

"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/attacheddatanetwork"
"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/datanetwork"
"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/mobilenetwork"
"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/packetcorecontrolplane"
Expand All @@ -28,6 +29,7 @@ type Client struct {
SIMPolicyClient *simpolicy.SIMPolicyClient
PacketCoreControlPlaneClient *packetcorecontrolplane.PacketCoreControlPlaneClient
PacketCoreDataPlaneClient *packetcoredataplane.PacketCoreDataPlaneClient
AttachedDataNetworkClient *attacheddatanetwork.AttachedDataNetworkClient
}

func NewClient(o *common.ClientOptions) (*Client, error) {
Expand Down Expand Up @@ -85,6 +87,12 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(packetCoreDataPlaneClient.Client, o.Authorizers.ResourceManager)

attachedDataNetworkClient, err := attacheddatanetwork.NewAttachedDataNetworkClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Attached Data Network Client: %+v", err)
}
o.Configure(attachedDataNetworkClient.Client, o.Authorizers.ResourceManager)

return &Client{
MobileNetworkClient: mobileNetworkClient,
DataNetworkClient: dataNetworkClient,
Expand All @@ -95,5 +103,6 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
SIMPolicyClient: simPolicyClient,
PacketCoreControlPlaneClient: packetCoreControlPlaneClient,
PacketCoreDataPlaneClient: packetCoreDataPlaneClient,
AttachedDataNetworkClient: attachedDataNetworkClient,
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
package mobilenetwork

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/attacheddatanetwork"
"github.com/hashicorp/go-azure-sdk/resource-manager/mobilenetwork/2022-11-01/packetcoredataplane"
"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/validation"
)

type AttachedDataNetworkDataSource struct{}

type AttachedDataNetworkDataSourceModel struct {
MobileNetworkDataNetworkName string `tfschema:"mobile_network_data_network_name"`
MobileNetworkPacketCoreDataPlaneId string `tfschema:"mobile_network_packet_core_data_plane_id"`
DnsAddresses []string `tfschema:"dns_addresses"`
Location string `tfschema:"location"`
NaptConfiguration []NaptConfigurationDataSourceModel `tfschema:"network_address_port_translation"`
Tags map[string]interface{} `tfschema:"tags"`
UserEquipmentAddressPoolPrefix []string `tfschema:"user_equipment_address_pool_prefixes"`
UserEquipmentStaticAddressPoolPrefix []string `tfschema:"user_equipment_static_address_pool_prefixes"`
UserPlaneAccessIPv4Address string `tfschema:"user_plane_access_ipv4_address"`
UserPlaneAccessIPv4Gateway string `tfschema:"user_plane_access_ipv4_gateway"`
UserPlaneAccessIPv4Subnet string `tfschema:"user_plane_access_ipv4_subnet"`
UserPlaneAccessName string `tfschema:"user_plane_access_name"`
}

type NaptConfigurationDataSourceModel struct {
PinholeLimits int64 `tfschema:"pinhole_maximum_number"`
IcmpPinholeTimeout int64 `tfschema:"icmp_pinhole_timeout_in_seconds"`
TcpPinholeTimeout int64 `tfschema:"tcp_pinhole_timeout_in_seconds"`
UdpPinholeTimeout int64 `tfschema:"udp_pinhole_timeout_in_seconds"`
PortRange []PortRangeDataSourceModel `tfschema:"port_range"`
TcpReuseMinimumHoldTime int64 `tfschema:"tcp_port_reuse_minimum_hold_time_in_seconds"`
UdpReuseMinimumHoldTime int64 `tfschema:"udp_port_reuse_minimum_hold_time_in_seconds"`
}

type PortRangeDataSourceModel struct {
Maximum int64 `tfschema:"maximum"`
Minimum int64 `tfschema:"minimum"`
}

var _ sdk.DataSource = AttachedDataNetworkDataSource{}

func (r AttachedDataNetworkDataSource) ResourceType() string {
return "azurerm_mobile_network_attached_data_network"
}

func (r AttachedDataNetworkDataSource) ModelObject() interface{} {
return &AttachedDataNetworkDataSourceModel{}
}

func (r AttachedDataNetworkDataSource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return attacheddatanetwork.ValidateAttachedDataNetworkID
}

func (r AttachedDataNetworkDataSource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"mobile_network_data_network_name": {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shall we rename this to name, or do we want to rename the corresponding field in the model from Name to MobileNetworkDataNetworkName to be consistent?

Copy link
Member

Choose a reason for hiding this comment

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

This was marked resolved but I don't see any discussion or explanation, can we have some context?

Copy link
Member

Choose a reason for hiding this comment

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

On reflection, given that this needs to be the same value as the corresponding Mobile Network Data Network's name value, we should in fact use the ID for that here and extract the name from it. This ensures the value is consistent, provides additional validation, and the value can be pulled from a reference allowing an explicit dependency between the resources.

Suggested change
"mobile_network_data_network_name": {
"mobile_network_data_network_id": {

Copy link
Contributor Author

@ziyeqf ziyeqf Jun 29, 2023

Choose a reason for hiding this comment

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

The reason to use name instead of id is the id format of azurerm_mobile_network_attached_data_network does not contain mobileNetworkName, the response/model does not contain it either... Then we cannot set the id in read func

attached_data_network_id:/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MobileNetwork/packetCoreControlPlanes/{packetCoreControlPlaneName}/packetCoreDataPlanes/{packetCoreDataPlaneName}/attachedDataNetworks/{attachedDataNetworkName}

data_network_id: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.MobileNetwork/mobileNetworks/{mobileNetworkName}/dataNetworks/{dataNetworkName}

Copy link
Contributor Author

@ziyeqf ziyeqf Jun 29, 2023

Choose a reason for hiding this comment

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

Screenshot 2023-06-29 at 11 15 13

Here is the dependency between these resources, black arrow means dependency, yellow arrow means id path. As there is a N:1 relation between azurerm_mobile_network_site and azurerm_mobile_network_packet_core_control_plane, itt seems there is no way get the id of azurerm_mobile_network_data_network. WDYT?

Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"mobile_network_packet_core_data_plane_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: packetcoredataplane.ValidatePacketCoreDataPlaneID,
},
}
}

func (r AttachedDataNetworkDataSource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{

"location": commonschema.LocationComputed(),

"dns_addresses": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"network_address_port_translation": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"pinhole_maximum_number": {
Type: pluginsdk.TypeInt,
Optional: true,
},

"icmp_pinhole_timeout_in_seconds": {
Type: pluginsdk.TypeInt,
Computed: true,
},

"tcp_pinhole_timeout_in_seconds": {
Type: pluginsdk.TypeInt,
Computed: true,
},

"udp_pinhole_timeout_in_seconds": {
Type: pluginsdk.TypeInt,
Computed: true,
},

"port_range": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"maximum": {
Type: pluginsdk.TypeInt,
Computed: true,
},

"minimum": {
Type: pluginsdk.TypeInt,
Computed: true,
},
},
},
},

"tcp_port_reuse_minimum_hold_time_in_seconds": {
Type: pluginsdk.TypeInt,
Computed: true,
},

"udp_port_reuse_minimum_hold_time_in_seconds": {
Type: pluginsdk.TypeInt,
Computed: true,
},
},
},
},

"user_plane_access_name": {
Type: pluginsdk.TypeString,
Computed: true,
},

"user_plane_access_ipv4_address": {
Type: pluginsdk.TypeString,
Computed: true,
},

"user_plane_access_ipv4_subnet": {
Type: pluginsdk.TypeString,
Computed: true,
},

"user_plane_access_ipv4_gateway": {
Type: pluginsdk.TypeString,
Computed: true,
},

"user_equipment_address_pool_prefixes": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"user_equipment_static_address_pool_prefixes": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"tags": commonschema.TagsDataSource(),
}
}

func (r AttachedDataNetworkDataSource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
var inputModel AttachedDataNetworkModel
if err := metadata.Decode(&inputModel); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

client := metadata.Client.MobileNetwork.AttachedDataNetworkClient

packetCoreDataPlaneId, err := packetcoredataplane.ParsePacketCoreDataPlaneID(inputModel.MobileNetworkPacketCoreDataPlaneId)
if err != nil {
return err
}

id := attacheddatanetwork.NewAttachedDataNetworkID(packetCoreDataPlaneId.SubscriptionId, packetCoreDataPlaneId.ResourceGroupName, packetCoreDataPlaneId.PacketCoreControlPlaneName, packetCoreDataPlaneId.PacketCoreDataPlaneName, inputModel.MobileNetworkDataNetworkName)
if err != nil {
return err
}

resp, err := client.Get(ctx, id)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", id, err)
}

state := AttachedDataNetworkDataSourceModel{
MobileNetworkDataNetworkName: id.AttachedDataNetworkName,
MobileNetworkPacketCoreDataPlaneId: packetcoredataplane.NewPacketCoreDataPlaneID(id.SubscriptionId, id.ResourceGroupName, id.PacketCoreControlPlaneName, id.PacketCoreDataPlaneName).ID(),
}

if model := resp.Model; model != nil {
props := model.Properties

state.Location = location.Normalize(model.Location)
state.DnsAddresses = props.DnsAddresses
state.NaptConfiguration = flattenDataSourceNaptConfiguration(props.NaptConfiguration)
state.UserEquipmentAddressPoolPrefix = pointer.From(props.UserEquipmentAddressPoolPrefix)
state.UserEquipmentStaticAddressPoolPrefix = pointer.From(props.UserEquipmentStaticAddressPoolPrefix)
state.UserPlaneAccessIPv4Address = pointer.From(props.UserPlaneDataInterface.IPv4Address)
state.UserPlaneAccessIPv4Gateway = pointer.From(props.UserPlaneDataInterface.IPv4Gateway)
state.UserPlaneAccessIPv4Subnet = pointer.From(props.UserPlaneDataInterface.IPv4Subnet)
state.UserPlaneAccessName = pointer.From(props.UserPlaneDataInterface.Name)
state.Tags = tags.Flatten(model.Tags)
}

metadata.SetID(id)

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

func flattenDataSourceNaptConfiguration(input *attacheddatanetwork.NaptConfiguration) []NaptConfigurationDataSourceModel {
if input == nil {
return []NaptConfigurationDataSourceModel{}
}
output := NaptConfigurationDataSourceModel{}

output.PinholeLimits = pointer.From(input.PinholeLimits)

if input.PinholeTimeouts != nil {
output.IcmpPinholeTimeout = pointer.From(input.PinholeTimeouts.Icmp)
output.TcpPinholeTimeout = pointer.From(input.PinholeTimeouts.Tcp)
output.UdpPinholeTimeout = pointer.From(input.PinholeTimeouts.Udp)
}

output.PortRange = flattenDataSourcePortRange(input.PortRange)

if input.PortReuseHoldTime != nil {
output.TcpReuseMinimumHoldTime = pointer.From(input.PortReuseHoldTime.Tcp)
output.UdpReuseMinimumHoldTime = pointer.From(input.PortReuseHoldTime.Udp)
}

return []NaptConfigurationDataSourceModel{output}
}

func flattenDataSourcePortRange(input *attacheddatanetwork.PortRange) []PortRangeDataSourceModel {
if input == nil {
return []PortRangeDataSourceModel{}
}

output := PortRangeDataSourceModel{}

output.Maximum = pointer.From(input.MaxPort)

output.Minimum = pointer.From(input.MinPort)

return []PortRangeDataSourceModel{output}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package mobilenetwork_test

import (
"fmt"
"testing"

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

type MobileNetworkAttachedDataNetworkDataSource struct{}

func TestAccMobileNetworkAttachedDataNetworkDataSource_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_mobile_network_attached_data_network", "test")
d := MobileNetworkAttachedDataNetworkDataSource{}
data.DataSourceTest(t, []acceptance.TestStep{
{
Config: d.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key(`location`).Exists(),
check.That(data.ResourceName).Key(`dns_addresses.0`).HasValue("1.1.1.1"),
check.That(data.ResourceName).Key(`user_equipment_address_pool_prefixes.0`).HasValue("2.4.1.0/24"),
check.That(data.ResourceName).Key(`user_equipment_static_address_pool_prefixes.0`).HasValue("2.4.2.0/24"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.pinhole_maximum_number`).HasValue("65536"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.icmp_pinhole_timeout_in_seconds`).HasValue("30"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.tcp_pinhole_timeout_in_seconds`).HasValue("100"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.udp_pinhole_timeout_in_seconds`).HasValue("39"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.port_range.0.maximum`).HasValue("49999"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.port_range.0.minimum`).HasValue("1024"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.tcp_port_reuse_minimum_hold_time_in_seconds`).HasValue("120"),
check.That(data.ResourceName).Key(`network_address_port_translation.0.udp_port_reuse_minimum_hold_time_in_seconds`).HasValue("60"),
check.That(data.ResourceName).Key(`user_plane_access_name`).HasValue("test"),
check.That(data.ResourceName).Key(`user_plane_access_ipv4_address`).HasValue("10.204.141.4"),
check.That(data.ResourceName).Key(`user_plane_access_ipv4_gateway`).HasValue("10.204.141.1"),
check.That(data.ResourceName).Key(`user_plane_access_ipv4_subnet`).HasValue("10.204.141.0/24"),
check.That(data.ResourceName).Key(`tags.%`).HasValue("1"),
),
},
})
}

func (r MobileNetworkAttachedDataNetworkDataSource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

data "azurerm_mobile_network_attached_data_network" "test" {
mobile_network_data_network_name = azurerm_mobile_network_attached_data_network.test.mobile_network_data_network_name
mobile_network_packet_core_data_plane_id = azurerm_mobile_network_attached_data_network.test.mobile_network_packet_core_data_plane_id
}
`, MobileNetworkAttachedDataNetworkResource{}.complete(data))
}
Loading
Loading