diff --git a/internal/services/network/client/client.go b/internal/services/network/client/client.go index f6b6cec912d3..a0cb12f4ce81 100644 --- a/internal/services/network/client/client.go +++ b/internal/services/network/client/client.go @@ -26,6 +26,7 @@ type Client struct { IPGroupsClient *network.IPGroupsClient LocalNetworkGatewaysClient *network.LocalNetworkGatewaysClient ManagersClient *network.ManagersClient + ManagerConnectivityConfigurationsClient *network.ConnectivityConfigurationsClient ManagerManagementGroupConnectionsClient *network.ManagementGroupNetworkManagerConnectionsClient ManagerNetworkGroupsClient *network.GroupsClient ManagerScopeConnectionsClient *network.ScopeConnectionsClient @@ -132,6 +133,9 @@ func NewClient(o *common.ClientOptions) *Client { ManagersClient := network.NewManagersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ManagersClient.Client, o.ResourceManagerAuthorizer) + ManagerConnectivityConfigurationsClient := network.NewConnectivityConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ManagerConnectivityConfigurationsClient.Client, o.ResourceManagerAuthorizer) + ManagerScopeConnectionsClient := network.NewScopeConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ManagerScopeConnectionsClient.Client, o.ResourceManagerAuthorizer) @@ -282,6 +286,7 @@ func NewClient(o *common.ClientOptions) *Client { IPGroupsClient: &IpGroupsClient, LocalNetworkGatewaysClient: &LocalNetworkGatewaysClient, ManagersClient: &ManagersClient, + ManagerConnectivityConfigurationsClient: &ManagerConnectivityConfigurationsClient, ManagerManagementGroupConnectionsClient: &ManagerManagementGroupConnectionsClient, ManagerNetworkGroupsClient: &ManagerNetworkGroupsClient, ManagerScopeConnectionsClient: &ManagerScopeConnectionsClient, diff --git a/internal/services/network/network_manager_connectivity_configuration_resource.go b/internal/services/network/network_manager_connectivity_configuration_resource.go new file mode 100644 index 000000000000..178e47f56fb2 --- /dev/null +++ b/internal/services/network/network_manager_connectivity_configuration_resource.go @@ -0,0 +1,444 @@ +package network + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" + "github.com/tombuildsstuff/kermit/sdk/network/2022-07-01/network" +) + +type ManagerConnectivityConfigurationModel struct { + Name string `tfschema:"name"` + NetworkManagerId string `tfschema:"network_manager_id"` + AppliesToGroups []ConnectivityGroupItemModel `tfschema:"applies_to_group"` + ConnectivityTopology network.ConnectivityTopology `tfschema:"connectivity_topology"` + DeleteExistingPeeringEnabled bool `tfschema:"delete_existing_peering_enabled"` + Description string `tfschema:"description"` + Hub []HubModel `tfschema:"hub"` + GlobalMeshEnabled bool `tfschema:"global_mesh_enabled"` +} + +type ConnectivityGroupItemModel struct { + GroupConnectivity network.GroupConnectivity `tfschema:"group_connectivity"` + GlobalMeshEnabled bool `tfschema:"global_mesh_enabled"` + NetworkGroupId string `tfschema:"network_group_id"` + UseHubGateway bool `tfschema:"use_hub_gateway"` +} + +type HubModel struct { + ResourceId string `tfschema:"resource_id"` + ResourceType string `tfschema:"resource_type"` +} + +type ManagerConnectivityConfigurationResource struct{} + +var _ sdk.ResourceWithUpdate = ManagerConnectivityConfigurationResource{} + +func (r ManagerConnectivityConfigurationResource) ResourceType() string { + return "azurerm_network_manager_connectivity_configuration" +} + +func (r ManagerConnectivityConfigurationResource) ModelObject() interface{} { + return &ManagerConnectivityConfigurationModel{} +} + +func (r ManagerConnectivityConfigurationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.NetworkManagerConnectivityConfigurationID +} + +func (r ManagerConnectivityConfigurationResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "network_manager_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NetworkManagerID, + }, + + "applies_to_group": { + Type: pluginsdk.TypeList, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "group_connectivity": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.GroupConnectivityNone), + string(network.GroupConnectivityDirectlyConnected), + }, false), + }, + + "global_mesh_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "network_group_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "use_hub_gateway": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + }, + }, + }, + + "connectivity_topology": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.ConnectivityTopologyHubAndSpoke), + string(network.ConnectivityTopologyMesh), + }, false), + }, + + "delete_existing_peering_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "hub": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "resource_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "resource_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "global_mesh_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + } +} + +func (r ManagerConnectivityConfigurationResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r ManagerConnectivityConfigurationResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model ManagerConnectivityConfigurationModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.Network.ManagerConnectivityConfigurationsClient + networkManagerId, err := parse.NetworkManagerID(model.NetworkManagerId) + if err != nil { + return err + } + + id := parse.NewNetworkManagerConnectivityConfigurationID(networkManagerId.SubscriptionId, networkManagerId.ResourceGroup, networkManagerId.Name, model.Name) + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName) + if err != nil && !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + + if !utils.ResponseWasNotFound(existing.Response) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + conf := &network.ConnectivityConfiguration{ + ConnectivityConfigurationProperties: &network.ConnectivityConfigurationProperties{ + AppliesToGroups: expandConnectivityGroupItemModel(model.AppliesToGroups), + ConnectivityTopology: model.ConnectivityTopology, + DeleteExistingPeering: expandDeleteExistingPeering(model.DeleteExistingPeeringEnabled), + IsGlobal: expandConnectivityConfIsGlobal(model.GlobalMeshEnabled), + Hubs: expandHubModel(model.Hub), + }, + } + + if model.Description != "" { + conf.ConnectivityConfigurationProperties.Description = &model.Description + } + + if _, err := client.CreateOrUpdate(ctx, *conf, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r ManagerConnectivityConfigurationResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerConnectivityConfigurationsClient + + id, err := parse.NetworkManagerConnectivityConfigurationID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model ManagerConnectivityConfigurationModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := existing.ConnectivityConfigurationProperties + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + if metadata.ResourceData.HasChange("applies_to_group") { + properties.AppliesToGroups = expandConnectivityGroupItemModel(model.AppliesToGroups) + } + + if metadata.ResourceData.HasChange("connectivity_topology") { + properties.ConnectivityTopology = model.ConnectivityTopology + } + + if metadata.ResourceData.HasChange("delete_existing_peering_enabled") { + properties.DeleteExistingPeering = expandDeleteExistingPeering(model.DeleteExistingPeeringEnabled) + } + + if metadata.ResourceData.HasChange("description") { + properties.Description = utils.String(model.Description) + } + + if metadata.ResourceData.HasChange("hub") { + properties.Hubs = expandHubModel(model.Hub) + } + + if metadata.ResourceData.HasChange("global_mesh_enabled") { + properties.IsGlobal = expandConnectivityConfIsGlobal(model.GlobalMeshEnabled) + } + + if _, err := client.CreateOrUpdate(ctx, existing, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r ManagerConnectivityConfigurationResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerConnectivityConfigurationsClient + + id, err := parse.NetworkManagerConnectivityConfigurationID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName) + if err != nil { + if utils.ResponseWasNotFound(existing.Response) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := existing.ConnectivityConfigurationProperties + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + state := ManagerConnectivityConfigurationModel{ + Name: id.ConnectivityConfigurationName, + NetworkManagerId: parse.NewNetworkManagerID(id.SubscriptionId, id.ResourceGroup, id.NetworkManagerName).ID(), + AppliesToGroups: flattenConnectivityGroupItemModel(properties.AppliesToGroups), + ConnectivityTopology: properties.ConnectivityTopology, + DeleteExistingPeeringEnabled: flattenDeleteExistingPeering(properties.DeleteExistingPeering), + GlobalMeshEnabled: flattenConnectivityConfIsGlobal(properties.IsGlobal), + Hub: flattenHubModel(properties.Hubs), + } + + if properties.Description != nil { + state.Description = *properties.Description + } + + return metadata.Encode(&state) + }, + } +} + +func (r ManagerConnectivityConfigurationResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerConnectivityConfigurationsClient + + id, err := parse.NetworkManagerConnectivityConfigurationID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName, utils.Bool(true)) + if err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandDeleteExistingPeering(input bool) network.DeleteExistingPeering { + if input { + return network.DeleteExistingPeeringTrue + } + return network.DeleteExistingPeeringFalse +} + +func expandConnectivityConfIsGlobal(input bool) network.IsGlobal { + if input { + return network.IsGlobalTrue + } + return network.IsGlobalFalse +} + +func expandConnectivityGroupItemModel(inputList []ConnectivityGroupItemModel) *[]network.ConnectivityGroupItem { + var outputList []network.ConnectivityGroupItem + for _, v := range inputList { + input := v + output := network.ConnectivityGroupItem{ + GroupConnectivity: input.GroupConnectivity, + IsGlobal: expandConnectivityConfIsGlobal(input.GlobalMeshEnabled), + NetworkGroupID: utils.String(input.NetworkGroupId), + UseHubGateway: expandUseHubGateWay(input.UseHubGateway), + } + + outputList = append(outputList, output) + } + + return &outputList +} + +func expandUseHubGateWay(input bool) network.UseHubGateway { + if input { + return network.UseHubGatewayTrue + } + return network.UseHubGatewayFalse +} + +func expandHubModel(inputList []HubModel) *[]network.Hub { + var outputList []network.Hub + for _, v := range inputList { + input := v + output := network.Hub{ + ResourceID: utils.String(input.ResourceId), + ResourceType: utils.String(input.ResourceType), + } + + outputList = append(outputList, output) + } + + return &outputList +} + +func flattenDeleteExistingPeering(input network.DeleteExistingPeering) bool { + return input == network.DeleteExistingPeeringTrue +} + +func flattenConnectivityConfIsGlobal(input network.IsGlobal) bool { + return input == network.IsGlobalTrue +} + +func flattenConnectivityGroupItemModel(inputList *[]network.ConnectivityGroupItem) []ConnectivityGroupItemModel { + var outputList []ConnectivityGroupItemModel + if inputList == nil { + return outputList + } + + for _, input := range *inputList { + output := ConnectivityGroupItemModel{ + GroupConnectivity: input.GroupConnectivity, + UseHubGateway: flattenUseHubGateWay(input.UseHubGateway), + GlobalMeshEnabled: flattenConnectivityConfIsGlobal(input.IsGlobal), + } + + if input.NetworkGroupID != nil { + output.NetworkGroupId = *input.NetworkGroupID + } + + outputList = append(outputList, output) + } + + return outputList +} + +func flattenUseHubGateWay(input network.UseHubGateway) bool { + return input == network.UseHubGatewayTrue +} + +func flattenHubModel(inputList *[]network.Hub) []HubModel { + var outputList []HubModel + if inputList == nil { + return outputList + } + + for _, input := range *inputList { + output := HubModel{} + + if input.ResourceID != nil { + output.ResourceId = *input.ResourceID + } + + if input.ResourceType != nil { + output.ResourceType = *input.ResourceType + } + + outputList = append(outputList, output) + } + + return outputList +} diff --git a/internal/services/network/network_manager_connectivity_configuration_resource_test.go b/internal/services/network/network_manager_connectivity_configuration_resource_test.go new file mode 100644 index 000000000000..df9f538e9b60 --- /dev/null +++ b/internal/services/network/network_manager_connectivity_configuration_resource_test.go @@ -0,0 +1,275 @@ +package network_test + +import ( + "context" + "fmt" + "testing" + + "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/services/network/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type ManagerConnectivityConfigurationResource struct{} + +func testAccNetworkManagerConnectivityConfiguration_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_connectivity_configuration", "test") + r := ManagerConnectivityConfigurationResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccNetworkManagerConnectivityConfiguration_basicTopologyMesh(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_connectivity_configuration", "test") + r := ManagerConnectivityConfigurationResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basicTopologyMesh(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccNetworkManagerConnectivityConfiguration_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_connectivity_configuration", "test") + r := ManagerConnectivityConfigurationResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func testAccNetworkManagerConnectivityConfiguration_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_connectivity_configuration", "test") + r := ManagerConnectivityConfigurationResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccNetworkManagerConnectivityConfiguration_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_connectivity_configuration", "test") + r := ManagerConnectivityConfigurationResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r ManagerConnectivityConfigurationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.NetworkManagerConnectivityConfigurationID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Network.ManagerConnectivityConfigurationsClient + resp, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + return utils.Bool(resp.ConnectivityConfigurationProperties != nil), nil +} + +func (r ManagerConnectivityConfigurationResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctest-nmng-%d" + location = "%s" +} + +data "azurerm_subscription" "current" { +} + +resource "azurerm_network_manager" "test" { + name = "acctest-nm-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + scope { + subscription_ids = [data.azurerm_subscription.current.id] + } + scope_accesses = ["Connectivity"] +} + +resource "azurerm_network_manager_network_group" "test" { + name = "acctest-nmng-%d" + network_manager_id = azurerm_network_manager.test.id +} + +resource "azurerm_virtual_network" "test" { + name = "acctest-vnet-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.0.0.0/16"] + flow_timeout_in_minutes = 10 +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + +func (r ManagerConnectivityConfigurationResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_manager_connectivity_configuration" "test" { + name = "acctest-nmcc-%d" + network_manager_id = azurerm_network_manager.test.id + connectivity_topology = "HubAndSpoke" + applies_to_group { + group_connectivity = "None" + network_group_id = azurerm_network_manager_network_group.test.id + } + hub { + resource_id = azurerm_virtual_network.test.id + resource_type = "Microsoft.Network/virtualNetworks" + } +} +`, template, data.RandomInteger) +} + +func (r ManagerConnectivityConfigurationResource) basicTopologyMesh(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_manager_connectivity_configuration" "test" { + name = "acctest-nmcc-%d" + network_manager_id = azurerm_network_manager.test.id + connectivity_topology = "Mesh" + applies_to_group { + group_connectivity = "None" + network_group_id = azurerm_network_manager_network_group.test.id + } +} +`, template, data.RandomInteger) +} + +func (r ManagerConnectivityConfigurationResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_manager_connectivity_configuration" "import" { + name = azurerm_network_manager_connectivity_configuration.test.name + network_manager_id = azurerm_network_manager_connectivity_configuration.test.network_manager_id + connectivity_topology = azurerm_network_manager_connectivity_configuration.test.connectivity_topology + applies_to_group { + group_connectivity = azurerm_network_manager_connectivity_configuration.test.applies_to_group.0.group_connectivity + network_group_id = azurerm_network_manager_connectivity_configuration.test.applies_to_group.0.network_group_id + } + hub { + resource_id = azurerm_network_manager_connectivity_configuration.test.hub.0.resource_id + resource_type = azurerm_network_manager_connectivity_configuration.test.hub.0.resource_type + } +} +`, config) +} + +func (r ManagerConnectivityConfigurationResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_manager_network_group" "test2" { + name = "acctest-nmng2-%d" + network_manager_id = azurerm_network_manager.test.id +} + +resource "azurerm_network_manager_connectivity_configuration" "test" { + name = "acctest-nmcc-%[2]d" + network_manager_id = azurerm_network_manager.test.id + connectivity_topology = "HubAndSpoke" + delete_existing_peering_enabled = false + global_mesh_enabled = false + description = "test connectivity configuration" + applies_to_group { + group_connectivity = "None" + network_group_id = azurerm_network_manager_network_group.test.id + global_mesh_enabled = false + use_hub_gateway = false + } + applies_to_group { + group_connectivity = "DirectlyConnected" + network_group_id = azurerm_network_manager_network_group.test2.id + global_mesh_enabled = true + use_hub_gateway = true + } + hub { + resource_id = azurerm_virtual_network.test.id + resource_type = "Microsoft.Network/virtualNetworks" + } +} +`, template, data.RandomInteger) +} + +func (r ManagerConnectivityConfigurationResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_manager_connectivity_configuration" "test" { + name = "acctest-nmcc-%d" + network_manager_id = azurerm_network_manager.test.id + connectivity_topology = "HubAndSpoke" + description = "test" + global_mesh_enabled = true + applies_to_group { + group_connectivity = "DirectlyConnected" + network_group_id = azurerm_network_manager_network_group.test.id + } + hub { + resource_id = azurerm_virtual_network.test.id + resource_type = "Microsoft.Network/virtualNetworks" + } +} +`, template, data.RandomInteger) +} diff --git a/internal/services/network/network_manager_resource_test.go b/internal/services/network/network_manager_resource_test.go index ce8ed3424c0e..6f8096c90174 100644 --- a/internal/services/network/network_manager_resource_test.go +++ b/internal/services/network/network_manager_resource_test.go @@ -55,6 +55,13 @@ func TestAccNetworkManager(t *testing.T) { "basic": testAccNetworkManagerStaticMember_basic, "requiresImport": testAccNetworkManagerStaticMember_requiresImport, }, + "ConnectivityConfiguration": { + "basic": testAccNetworkManagerConnectivityConfiguration_basic, + "basicTopologyMesh": testAccNetworkManagerConnectivityConfiguration_basicTopologyMesh, + "complete": testAccNetworkManagerConnectivityConfiguration_complete, + "update": testAccNetworkManagerConnectivityConfiguration_update, + "requiresImport": testAccNetworkManagerConnectivityConfiguration_requiresImport, + }, } for group, m := range testCases { diff --git a/internal/services/network/network_manager_static_member_resource.go b/internal/services/network/network_manager_static_member_resource.go index 642e44eded3a..9c83125e9558 100644 --- a/internal/services/network/network_manager_static_member_resource.go +++ b/internal/services/network/network_manager_static_member_resource.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/utils" - "github.com/tombuildsstuff/kermit/sdk/network/2022-05-01/network" + "github.com/tombuildsstuff/kermit/sdk/network/2022-07-01/network" ) type ManagerStaticMemberModel struct { diff --git a/internal/services/network/parse/network_manager_connectivity_configuration.go b/internal/services/network/parse/network_manager_connectivity_configuration.go new file mode 100644 index 000000000000..301bfb3026ff --- /dev/null +++ b/internal/services/network/parse/network_manager_connectivity_configuration.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type NetworkManagerConnectivityConfigurationId struct { + SubscriptionId string + ResourceGroup string + NetworkManagerName string + ConnectivityConfigurationName string +} + +func NewNetworkManagerConnectivityConfigurationID(subscriptionId, resourceGroup, networkManagerName, connectivityConfigurationName string) NetworkManagerConnectivityConfigurationId { + return NetworkManagerConnectivityConfigurationId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + NetworkManagerName: networkManagerName, + ConnectivityConfigurationName: connectivityConfigurationName, + } +} + +func (id NetworkManagerConnectivityConfigurationId) String() string { + segments := []string{ + fmt.Sprintf("Connectivity Configuration Name %q", id.ConnectivityConfigurationName), + fmt.Sprintf("Network Manager Name %q", id.NetworkManagerName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Network Manager Connectivity Configuration", segmentsStr) +} + +func (id NetworkManagerConnectivityConfigurationId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/networkManagers/%s/connectivityConfigurations/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.NetworkManagerName, id.ConnectivityConfigurationName) +} + +// NetworkManagerConnectivityConfigurationID parses a NetworkManagerConnectivityConfiguration ID into an NetworkManagerConnectivityConfigurationId struct +func NetworkManagerConnectivityConfigurationID(input string) (*NetworkManagerConnectivityConfigurationId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := NetworkManagerConnectivityConfigurationId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.NetworkManagerName, err = id.PopSegment("networkManagers"); err != nil { + return nil, err + } + if resourceId.ConnectivityConfigurationName, err = id.PopSegment("connectivityConfigurations"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/network/parse/network_manager_connectivity_configuration_test.go b/internal/services/network/parse/network_manager_connectivity_configuration_test.go new file mode 100644 index 000000000000..56a7b1215e15 --- /dev/null +++ b/internal/services/network/parse/network_manager_connectivity_configuration_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = NetworkManagerConnectivityConfigurationId{} + +func TestNetworkManagerConnectivityConfigurationIDFormatter(t *testing.T) { + actual := NewNetworkManagerConnectivityConfigurationID("12345678-1234-9876-4563-123456789012", "resGroup1", "manager1", "conf1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/conf1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestNetworkManagerConnectivityConfigurationID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *NetworkManagerConnectivityConfigurationId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing NetworkManagerName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", + Error: true, + }, + + { + // missing value for NetworkManagerName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/", + Error: true, + }, + + { + // missing ConnectivityConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/", + Error: true, + }, + + { + // missing value for ConnectivityConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/conf1", + Expected: &NetworkManagerConnectivityConfigurationId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + NetworkManagerName: "manager1", + ConnectivityConfigurationName: "conf1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/NETWORKMANAGERS/MANAGER1/CONNECTIVITYCONFIGURATIONS/CONF1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := NetworkManagerConnectivityConfigurationID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.NetworkManagerName != v.Expected.NetworkManagerName { + t.Fatalf("Expected %q but got %q for NetworkManagerName", v.Expected.NetworkManagerName, actual.NetworkManagerName) + } + if actual.ConnectivityConfigurationName != v.Expected.ConnectivityConfigurationName { + t.Fatalf("Expected %q but got %q for ConnectivityConfigurationName", v.Expected.ConnectivityConfigurationName, actual.ConnectivityConfigurationName) + } + } +} diff --git a/internal/services/network/registration.go b/internal/services/network/registration.go index 8a43c47b0823..cfc92f9e7a8b 100644 --- a/internal/services/network/registration.go +++ b/internal/services/network/registration.go @@ -34,6 +34,7 @@ func (r Registration) DataSources() []sdk.DataSource { func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ + ManagerConnectivityConfigurationResource{}, ManagerManagementGroupConnectionResource{}, ManagerNetworkGroupResource{}, ManagerResource{}, diff --git a/internal/services/network/resourceids.go b/internal/services/network/resourceids.go index 5071f61d5ae7..d7fd0f93afe6 100644 --- a/internal/services/network/resourceids.go +++ b/internal/services/network/resourceids.go @@ -113,6 +113,7 @@ package network // Network Manager //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NetworkManager -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NetworkManagerConnectivityConfiguration -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/conf1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NetworkManagerNetworkGroup -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/networkGroups/group1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NetworkManagerScopeConnection -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/connection1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=NetworkManagerStaticMember -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/networkGroups/group1/staticMembers/member1 diff --git a/internal/services/network/validate/network_manager_connectivity_configuration_id.go b/internal/services/network/validate/network_manager_connectivity_configuration_id.go new file mode 100644 index 000000000000..643c6b6dfb3d --- /dev/null +++ b/internal/services/network/validate/network_manager_connectivity_configuration_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" +) + +func NetworkManagerConnectivityConfigurationID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.NetworkManagerConnectivityConfigurationID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/network/validate/network_manager_connectivity_configuration_id_test.go b/internal/services/network/validate/network_manager_connectivity_configuration_id_test.go new file mode 100644 index 000000000000..bdef85e4db4f --- /dev/null +++ b/internal/services/network/validate/network_manager_connectivity_configuration_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestNetworkManagerConnectivityConfigurationID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing NetworkManagerName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/", + Valid: false, + }, + + { + // missing value for NetworkManagerName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/", + Valid: false, + }, + + { + // missing ConnectivityConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/", + Valid: false, + }, + + { + // missing value for ConnectivityConfigurationName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/connectivityConfigurations/conf1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/NETWORKMANAGERS/MANAGER1/CONNECTIVITYCONFIGURATIONS/CONF1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := NetworkManagerConnectivityConfigurationID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/network_manager_connectivity_configuration.html.markdown b/website/docs/r/network_manager_connectivity_configuration.html.markdown new file mode 100644 index 000000000000..29648d643439 --- /dev/null +++ b/website/docs/r/network_manager_connectivity_configuration.html.markdown @@ -0,0 +1,128 @@ +--- +subcategory: "Network" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_network_manager_connectivity_configuration" +description: |- + Manages a Network Manager Connectivity Configuration. +--- + +# azurerm_network_manager_connectivity_configuration + +Manages a Network Manager Connectivity Configuration. + +-> **Note:** The `azurerm_network_manager_connectivity_configuration` deployment may modify or delete existing Network Peering resource. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +data "azurerm_subscription" "current" { +} + +resource "azurerm_network_manager" "example" { + name = "example-network-manager" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + scope { + subscription_ids = [data.azurerm_subscription.current.id] + } + scope_accesses = ["Connectivity", "SecurityAdmin"] + description = "example network manager" +} + +resource "azurerm_network_manager_network_group" "example" { + name = "example-group" + network_manager_id = azurerm_network_manager.example.id +} + +resource "azurerm_virtual_network" "example" { + name = "example-net" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + address_space = ["10.0.0.0/16"] + flow_timeout_in_minutes = 10 +} + +resource "azurerm_network_manager_connectivity_configuration" "example" { + name = "example-connectivity-conf" + network_manager_id = azurerm_network_manager.example.id + connectivity_topology = "HubAndSpoke" + applies_to_group { + group_connectivity = "DirectlyConnected" + network_group_id = azurerm_network_manager_network_group.example.id + } + hub { + resource_id = azurerm_virtual_network.example.id + resource_type = "Microsoft.Network/virtualNetworks" + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name which should be used for this Network Manager Connectivity Configuration. Changing this forces a new Network Manager Connectivity Configuration to be created. + +* `network_manager_id` - (Required) Specifies the ID of the Network Manager. Changing this forces a new Network Manager Connectivity Configuration to be created. + +* `applies_to_group` - (Required) An `applies_to_group` block as defined below. + +* `connectivity_topology` - (Required) Specifies the connectivity topology type. Possible values are `HubAndSpoke` and `Mesh`. + +* `delete_existing_peering_enabled` - (Optional) Indicates whether to remove current existing Virtual Network Peering in the Connectivity Configuration affected scope. Possible values are `true` and `false`. + +* `description` - (Optional) A description of the Connectivity Configuration. + +* `global_mesh_enabled` - (Optional) Indicates whether to global mesh is supported. Possible values are `true` and `false`. + +* `hub` - (Optional) A `hub` block as defined below. + +--- + +An `applies_to_group` block supports the following: + +* `group_connectivity` - (Required) Specifies the group connectivity type. Possible values are `None` and `DirectlyConnected`. + +* `network_group_id` - (Required) Specifies the resource ID of Network Group which the configuration applies to. + +* `global_mesh_enabled` - (Optional) Indicates whether to global mesh is supported for this group. Possible values are `true` and `false`. + +-> **NOTE:** A group can be global only if the `group_connectivity` is `DirectlyConnected`. + +* `use_hub_gateway` - (Optional) Indicates whether the hub gateway is used. Possible values are `true` and `false`. + +--- + +A `hub` block supports the following: + +* `resource_id` - (Required) Specifies the resource ID used as hub in Hub And Spoke topology. + +* `resource_type` - (Required) Specifies the resource Type used as hub in Hub And Spoke topology. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Network Manager Connectivity Configuration. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Network Manager Connectivity Configuration. +* `read` - (Defaults to 5 minutes) Used when retrieving the Network Manager Connectivity Configuration. +* `update` - (Defaults to 30 minutes) Used when updating the Network Manager Connectivity Configuration. +* `delete` - (Defaults to 30 minutes) Used when deleting the Network Manager Connectivity Configuration. + +## Import + +Network Manager Connectivity Configuration can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_network_manager_connectivity_configuration.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.Network/networkManagers/networkManager1/connectivityConfigurations/configuration1 +```