diff --git a/internal/services/network/client/client.go b/internal/services/network/client/client.go index 7d1d8f67be91..a808a8833404 100644 --- a/internal/services/network/client/client.go +++ b/internal/services/network/client/client.go @@ -28,6 +28,7 @@ type Client struct { ManagersClient *network.ManagersClient ManagerManagementGroupConnectionsClient *network.ManagementGroupNetworkManagerConnectionsClient ManagerNetworkGroupsClient *network.GroupsClient + ManagerScopeConnectionsClient *network.ScopeConnectionsClient ManagerSubscriptionConnectionsClient *network.SubscriptionNetworkManagerConnectionsClient NatRuleClient *network.NatRulesClient PointToSiteVpnGatewaysClient *network.P2sVpnGatewaysClient @@ -130,6 +131,9 @@ func NewClient(o *common.ClientOptions) *Client { ManagersClient := network.NewManagersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ManagersClient.Client, o.ResourceManagerAuthorizer) + ManagerScopeConnectionsClient := network.NewScopeConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&ManagerScopeConnectionsClient.Client, o.ResourceManagerAuthorizer) + ManagerManagementGroupConnectionsClient := network.NewManagementGroupNetworkManagerConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ManagerManagementGroupConnectionsClient.Client, o.ResourceManagerAuthorizer) @@ -276,6 +280,7 @@ func NewClient(o *common.ClientOptions) *Client { ManagersClient: &ManagersClient, ManagerManagementGroupConnectionsClient: &ManagerManagementGroupConnectionsClient, ManagerNetworkGroupsClient: &ManagerNetworkGroupsClient, + ManagerScopeConnectionsClient: &ManagerScopeConnectionsClient, ManagerSubscriptionConnectionsClient: &ManagerSubscriptionConnectionsClient, NatRuleClient: &NatRuleClient, PointToSiteVpnGatewaysClient: &pointToSiteVpnGatewaysClient, diff --git a/internal/services/network/network_manager_resource_test.go b/internal/services/network/network_manager_resource_test.go index c5bcb0825912..ae3ec3beaa48 100644 --- a/internal/services/network/network_manager_resource_test.go +++ b/internal/services/network/network_manager_resource_test.go @@ -45,6 +45,12 @@ func TestAccNetworkManager(t *testing.T) { "update": testAccNetworkManagerManagementGroupConnection_update, "requiresImport": testAccNetworkManagerManagementGroupConnection_requiresImport, }, + "ScopeConnection": { + "basic": testAccNetworkManagerScopeConnection_basic, + "complete": testAccNetworkManagerScopeConnection_complete, + "update": testAccNetworkManagerScopeConnection_update, + "requiresImport": testAccNetworkManagerScopeConnection_requiresImport, + }, } for group, m := range testCases { diff --git a/internal/services/network/network_manager_scope_connection_resource.go b/internal/services/network/network_manager_scope_connection_resource.go new file mode 100644 index 000000000000..adde15c17b90 --- /dev/null +++ b/internal/services/network/network_manager_scope_connection_resource.go @@ -0,0 +1,259 @@ +package network + +import ( + "context" + "fmt" + "time" + + "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-05-01/network" +) + +type ManagerScopeConnectionModel struct { + Name string `tfschema:"name"` + NetworkManagerId string `tfschema:"network_manager_id"` + ConnectionState string `tfschema:"connection_state"` + Description string `tfschema:"description"` + ResourceId string `tfschema:"target_scope_id"` + TenantId string `tfschema:"tenant_id"` +} + +type ManagerScopeConnectionResource struct{} + +var _ sdk.ResourceWithUpdate = ManagerScopeConnectionResource{} + +func (r ManagerScopeConnectionResource) ResourceType() string { + return "azurerm_network_manager_scope_connection" +} + +func (r ManagerScopeConnectionResource) ModelObject() interface{} { + return &ManagerScopeConnectionModel{} +} + +func (r ManagerScopeConnectionResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.NetworkManagerScopeConnectionID +} + +func (r ManagerScopeConnectionResource) 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, + }, + + "target_scope_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "tenant_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, + }, + + "description": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + } +} + +func (r ManagerScopeConnectionResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + + "connection_state": { + Type: pluginsdk.TypeString, + Computed: true, + }, + } +} + +func (r ManagerScopeConnectionResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model ManagerScopeConnectionModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.Network.ManagerScopeConnectionsClient + networkManagerId, err := parse.NetworkManagerID(model.NetworkManagerId) + if err != nil { + return err + } + + id := parse.NewNetworkManagerScopeConnectionID(networkManagerId.SubscriptionId, networkManagerId.ResourceGroup, networkManagerId.Name, model.Name) + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName) + 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) + } + + scopeConnection := &network.ScopeConnection{ + ScopeConnectionProperties: &network.ScopeConnectionProperties{}, + } + + if model.Description != "" { + scopeConnection.ScopeConnectionProperties.Description = &model.Description + } + + if model.ResourceId != "" { + scopeConnection.ScopeConnectionProperties.ResourceID = &model.ResourceId + } + + if model.TenantId != "" { + scopeConnection.ScopeConnectionProperties.TenantID = &model.TenantId + } + + if _, err := client.CreateOrUpdate(ctx, *scopeConnection, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r ManagerScopeConnectionResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerScopeConnectionsClient + + id, err := parse.NetworkManagerScopeConnectionID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model ManagerScopeConnectionModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := existing.ScopeConnectionProperties + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + if metadata.ResourceData.HasChange("description") { + if model.Description != "" { + properties.Description = &model.Description + } + } + + if metadata.ResourceData.HasChange("target_scope_id") { + if model.ResourceId != "" { + properties.ResourceID = &model.ResourceId + } + } + + if metadata.ResourceData.HasChange("tenant_id") { + if model.TenantId != "" { + properties.TenantID = &model.TenantId + } + } + + if _, err := client.CreateOrUpdate(ctx, existing, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r ManagerScopeConnectionResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerScopeConnectionsClient + + id, err := parse.NetworkManagerScopeConnectionID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName) + if err != nil { + if utils.ResponseWasNotFound(existing.Response) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + properties := existing.ScopeConnectionProperties + if properties == nil { + return fmt.Errorf("retrieving %s: properties was nil", id) + } + + state := ManagerScopeConnectionModel{ + Name: id.ScopeConnectionName, + NetworkManagerId: parse.NewNetworkManagerID(id.SubscriptionId, id.ResourceGroup, id.NetworkManagerName).ID(), + } + + state.ConnectionState = string(properties.ConnectionState) + + if properties.Description != nil { + state.Description = *properties.Description + } + + if properties.ResourceID != nil { + state.ResourceId = *properties.ResourceID + } + + if properties.TenantID != nil { + state.TenantId = *properties.TenantID + } + + return metadata.Encode(&state) + }, + } +} + +func (r ManagerScopeConnectionResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Network.ManagerScopeConnectionsClient + + id, err := parse.NetworkManagerScopeConnectionID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} diff --git a/internal/services/network/network_manager_scope_connection_resource_test.go b/internal/services/network/network_manager_scope_connection_resource_test.go new file mode 100644 index 000000000000..b4c92c5f4e34 --- /dev/null +++ b/internal/services/network/network_manager_scope_connection_resource_test.go @@ -0,0 +1,183 @@ +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 ManagerScopeConnectionResource struct{} + +func testAccNetworkManagerScopeConnection_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_scope_connection", "test") + r := ManagerScopeConnectionResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccNetworkManagerScopeConnection_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_scope_connection", "test") + r := ManagerScopeConnectionResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func testAccNetworkManagerScopeConnection_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_scope_connection", "test") + r := ManagerScopeConnectionResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccNetworkManagerScopeConnection_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_network_manager_scope_connection", "test") + r := ManagerScopeConnectionResource{} + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(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(), + }) +} + +func (r ManagerScopeConnectionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.NetworkManagerScopeConnectionID(state.ID) + if err != nil { + return nil, err + } + + client := clients.Network.ManagerScopeConnectionsClient + resp, err := client.Get(ctx, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName) + 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.ScopeConnectionProperties != nil), nil +} + +func (r ManagerScopeConnectionResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-nm-%d" + location = "%s" +} + +data "azurerm_client_config" "current" { +} + +data "azurerm_subscription" "current" { +} + +resource "azurerm_network_manager" "test" { + name = "acctest-networkmanager-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + scope { + subscription_ids = [data.azurerm_subscription.current.id] + } + scope_accesses = ["SecurityAdmin"] +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r ManagerScopeConnectionResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_network_manager_scope_connection" "test" { + name = "acctest-nsc-%d" + network_manager_id = azurerm_network_manager.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + target_scope_id = data.azurerm_subscription.current.id +} +`, template, data.RandomInteger) +} + +func (r ManagerScopeConnectionResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` + %s + +resource "azurerm_network_manager_scope_connection" "import" { + name = azurerm_network_manager_scope_connection.test.name + network_manager_id = azurerm_network_manager_scope_connection.test.network_manager_id + tenant_id = azurerm_network_manager_scope_connection.test.tenant_id + target_scope_id = azurerm_network_manager_scope_connection.test.target_scope_id +} +`, config) +} + +func (r ManagerScopeConnectionResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_network_manager_scope_connection" "test" { + name = "acctest-nsc-%d" + network_manager_id = azurerm_network_manager.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + target_scope_id = data.azurerm_subscription.current.id + description = "complete" +} +`, template, data.RandomInteger) +} + +func (r ManagerScopeConnectionResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` + %s + +resource "azurerm_network_manager_scope_connection" "test" { + name = "acctest-nsc-%d" + network_manager_id = azurerm_network_manager.test.id + tenant_id = data.azurerm_client_config.current.tenant_id + target_scope_id = data.azurerm_subscription.current.id + description = "update" +} +`, template, data.RandomInteger) +} diff --git a/internal/services/network/parse/network_manager_scope_connection.go b/internal/services/network/parse/network_manager_scope_connection.go new file mode 100644 index 000000000000..faacf86ab204 --- /dev/null +++ b/internal/services/network/parse/network_manager_scope_connection.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 NetworkManagerScopeConnectionId struct { + SubscriptionId string + ResourceGroup string + NetworkManagerName string + ScopeConnectionName string +} + +func NewNetworkManagerScopeConnectionID(subscriptionId, resourceGroup, networkManagerName, scopeConnectionName string) NetworkManagerScopeConnectionId { + return NetworkManagerScopeConnectionId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + NetworkManagerName: networkManagerName, + ScopeConnectionName: scopeConnectionName, + } +} + +func (id NetworkManagerScopeConnectionId) String() string { + segments := []string{ + fmt.Sprintf("Scope Connection Name %q", id.ScopeConnectionName), + 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 Scope Connection", segmentsStr) +} + +func (id NetworkManagerScopeConnectionId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/networkManagers/%s/scopeConnections/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.NetworkManagerName, id.ScopeConnectionName) +} + +// NetworkManagerScopeConnectionID parses a NetworkManagerScopeConnection ID into an NetworkManagerScopeConnectionId struct +func NetworkManagerScopeConnectionID(input string) (*NetworkManagerScopeConnectionId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := NetworkManagerScopeConnectionId{ + 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.ScopeConnectionName, err = id.PopSegment("scopeConnections"); 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_scope_connection_test.go b/internal/services/network/parse/network_manager_scope_connection_test.go new file mode 100644 index 000000000000..f6bc7915ef43 --- /dev/null +++ b/internal/services/network/parse/network_manager_scope_connection_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 = NetworkManagerScopeConnectionId{} + +func TestNetworkManagerScopeConnectionIDFormatter(t *testing.T) { + actual := NewNetworkManagerScopeConnectionID("12345678-1234-9876-4563-123456789012", "resGroup1", "manager1", "connection1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/connection1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestNetworkManagerScopeConnectionID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *NetworkManagerScopeConnectionId + }{ + + { + // 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 ScopeConnectionName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/", + Error: true, + }, + + { + // missing value for ScopeConnectionName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/connection1", + Expected: &NetworkManagerScopeConnectionId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + NetworkManagerName: "manager1", + ScopeConnectionName: "connection1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/NETWORKMANAGERS/MANAGER1/SCOPECONNECTIONS/CONNECTION1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := NetworkManagerScopeConnectionID(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.ScopeConnectionName != v.Expected.ScopeConnectionName { + t.Fatalf("Expected %q but got %q for ScopeConnectionName", v.Expected.ScopeConnectionName, actual.ScopeConnectionName) + } + } +} diff --git a/internal/services/network/registration.go b/internal/services/network/registration.go index 434d9c45b38a..de8e0b033e47 100644 --- a/internal/services/network/registration.go +++ b/internal/services/network/registration.go @@ -37,6 +37,7 @@ func (r Registration) Resources() []sdk.Resource { ManagerManagementGroupConnectionResource{}, ManagerNetworkGroupResource{}, ManagerResource{}, + ManagerScopeConnectionResource{}, ManagerSubscriptionConnectionResource{}, PrivateEndpointApplicationSecurityGroupAssociationResource{}, RouteMapResource{}, diff --git a/internal/services/network/resourceids.go b/internal/services/network/resourceids.go index c575a7550f6d..2e934f327568 100644 --- a/internal/services/network/resourceids.go +++ b/internal/services/network/resourceids.go @@ -114,4 +114,5 @@ 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=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=NetworkManagerSubscriptionConnection -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Network/networkManagerConnections/connection1 diff --git a/internal/services/network/validate/network_manager_scope_connection_id.go b/internal/services/network/validate/network_manager_scope_connection_id.go new file mode 100644 index 000000000000..de9fcc0e4593 --- /dev/null +++ b/internal/services/network/validate/network_manager_scope_connection_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 NetworkManagerScopeConnectionID(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.NetworkManagerScopeConnectionID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/network/validate/network_manager_scope_connection_id_test.go b/internal/services/network/validate/network_manager_scope_connection_id_test.go new file mode 100644 index 000000000000..87a97bf0a9f7 --- /dev/null +++ b/internal/services/network/validate/network_manager_scope_connection_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 TestNetworkManagerScopeConnectionID(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 ScopeConnectionName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/", + Valid: false, + }, + + { + // missing value for ScopeConnectionName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Network/networkManagers/manager1/scopeConnections/connection1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.NETWORK/NETWORKMANAGERS/MANAGER1/SCOPECONNECTIONS/CONNECTION1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := NetworkManagerScopeConnectionID(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_scope_connection.html.markdown b/website/docs/r/network_manager_scope_connection.html.markdown new file mode 100644 index 000000000000..a3fa36095357 --- /dev/null +++ b/website/docs/r/network_manager_scope_connection.html.markdown @@ -0,0 +1,85 @@ +--- +subcategory: "Network" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_network_manager_scope_connection" +description: |- + Manages a Network Manager Scope Connection. +--- + +# azurerm_network_manager_scope_connection + +Manages a Network Manager Scope Connection which may cross tenants. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +data "azurerm_client_config" "current" { +} + +data "azurerm_subscription" "current" { +} + +data "azurerm_subscription" "alt" { + subscription_id = "00000000-0000-0000-0000-000000000000" +} + +resource "azurerm_network_manager" "example" { + name = "example-networkmanager" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + scope { + subscription_ids = [data.azurerm_subscription.current.id] + } + scope_accesses = ["SecurityAdmin"] +} + +resource "azurerm_network_manager_scope_connection" "example" { + name = "example-nsc" + network_manager_id = azurerm_network_manager.example.id + tenant_id = data.azurerm_client_config.current.tenant_id + target_scope_id = data.azurerm_subscription.alt.id + description = "example" +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name which should be used for this Network Manager Scope Connection. Changing this forces a new Network Manager Scope Connection to be created. + +* `network_manager_id` - (Required) Specifies the ID of the Network Manager Scope Connection. Changing this forces a new Network Manager Scope Connection to be created. + +* `target_scope_id` - (Required) Specifies the Resource ID of the target scope which the Network Manager is connected to. It should be either Subscription ID or Management Group ID. + +* `tenant_id` - (Required) Specifies the Tenant ID of the Resource which the Network Manager is connected to. + +* `description` - (Optional) A description of the Network Manager Scope Connection. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Network Manager Scope Connection. + +## 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 Scope Connection. +* `read` - (Defaults to 5 minutes) Used when retrieving the Network Manager Scope Connection. +* `update` - (Defaults to 30 minutes) Used when updating the Network Manager Scope Connection. +* `delete` - (Defaults to 30 minutes) Used when deleting the Network Manager Scope Connection. + +## Import + +Network Manager Scope Connection can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_network_manager_scope_connection.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.Network/networkManagers/networkManager1/scopeConnections/scopeConnection1 +```