diff --git a/internal/services/gateway/data_gateway_role_assignments.go b/internal/services/gateway/data_gateway_role_assignments.go index 3edde4d8..463129b5 100644 --- a/internal/services/gateway/data_gateway_role_assignments.go +++ b/internal/services/gateway/data_gateway_role_assignments.go @@ -71,31 +71,6 @@ func (d *dataSourceGatewayRoleAssignments) Schema(ctx context.Context, _ datasou MarkdownDescription: "The type of the principal.", Computed: true, }, - "details": schema.SingleNestedAttribute{ - MarkdownDescription: "The principal details.", - Computed: true, - CustomType: supertypes.NewSingleNestedObjectTypeOf[principalDetailsModel](ctx), - Attributes: map[string]schema.Attribute{ - "user_principal_name": schema.StringAttribute{ - MarkdownDescription: "The user principal name.", - Computed: true, - }, - "group_type": schema.StringAttribute{ - MarkdownDescription: "The group type.", - Computed: true, - }, - "app_id": schema.StringAttribute{ - MarkdownDescription: "The Service Principal's Microsoft Entra App ID.", - Computed: true, - CustomType: customtypes.UUIDType{}, - }, - "parent_principal_id": schema.StringAttribute{ - MarkdownDescription: "The parent principal ID of Service Principal Profile.", - Computed: true, - CustomType: customtypes.UUIDType{}, - }, - }, - }, }, }, }, diff --git a/internal/services/gateway/data_gateway_role_assignments_test.go b/internal/services/gateway/data_gateway_role_assignments_test.go index 633247ae..4c40d6e0 100644 --- a/internal/services/gateway/data_gateway_role_assignments_test.go +++ b/internal/services/gateway/data_gateway_role_assignments_test.go @@ -52,7 +52,6 @@ func TestUnit_GatewayRoleAssignmentsDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.role", (*string)(entity.Role)), resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.display_name", entity.Principal.DisplayName), resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.type", (*string)(entity.Principal.Type)), - // Additional nested details checks can be added here. ), }, })) @@ -60,7 +59,7 @@ func TestUnit_GatewayRoleAssignmentsDataSource(t *testing.T) { func TestAcc_GatewayRoleAssignmentsDataSource(t *testing.T) { // For acceptance testing, assume a well-known gateway is provided. - gateway := testhelp.WellKnown()["GatewayDS"].(map[string]any) + gateway := testhelp.WellKnown()["GatewayVirtualNetwork"].(map[string]any) gatewayID := gateway["id"].(string) resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ diff --git a/internal/services/gateway/data_on_premises_gateway.go b/internal/services/gateway/data_on_premises_gateway.go index 80113217..7c9d6871 100644 --- a/internal/services/gateway/data_on_premises_gateway.go +++ b/internal/services/gateway/data_on_premises_gateway.go @@ -132,10 +132,9 @@ func (d *dataSourceOnPremisesGateway) Configure(_ context.Context, req datasourc } d.pConfigData = pConfigData - d.client = (*fabcore.GatewaysClient)(fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient()) + d.client = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() } -// Read refreshes the Terraform state with the latest data. func (d *dataSourceOnPremisesGateway) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { tflog.Debug(ctx, "READ", map[string]any{ "action": "start", @@ -209,15 +208,13 @@ func (d *dataSourceOnPremisesGateway) getByDisplayName(ctx context.Context, mode } for _, gw := range gateways { - if OnPremisesGateway, ok := gw.(*fabcore.OnPremisesGateway); ok { - if *OnPremisesGateway.DisplayName == model.DisplayName.ValueString() { - model.set(ctx, *OnPremisesGateway) + if onPremisesGateway, ok := gw.(*fabcore.OnPremisesGateway); ok { + if *onPremisesGateway.DisplayName == model.DisplayName.ValueString() { + model.set(ctx, *onPremisesGateway) return nil } } } - var diags diag.Diagnostics - diags.AddError(common.ErrorReadHeader, "no on-premises gateway with display name found") - return diags + return diag.Diagnostics{diag.NewErrorDiagnostic(common.ErrorReadHeader, "on-premises gateway not found")} } diff --git a/internal/services/gateway/data_on_premises_gateway_personal.go b/internal/services/gateway/data_on_premises_gateway_personal.go index 1ab88879..88141650 100644 --- a/internal/services/gateway/data_on_premises_gateway_personal.go +++ b/internal/services/gateway/data_on_premises_gateway_personal.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" @@ -38,9 +39,8 @@ func (d *dataSourceOnPremisesGatewayPersonal) Schema(ctx context.Context, _ data MarkdownDescription: "Retrieve an on-premises gateway in its 'personal' form (ID, public key, type, version).", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ + Required: true, MarkdownDescription: "The gateway ID.", - Optional: true, - Computed: true, CustomType: customtypes.UUIDType{}, }, "version": schema.StringAttribute{ @@ -73,7 +73,6 @@ func (d *dataSourceOnPremisesGatewayPersonal) Configure(ctx context.Context, req } pConfigData, ok := req.ProviderData.(*pconfig.ProviderData) - if !ok { resp.Diagnostics.AddError( common.ErrorDataSourceConfigType, @@ -82,27 +81,33 @@ func (d *dataSourceOnPremisesGatewayPersonal) Configure(ctx context.Context, req return } d.pConfigData = pConfigData - d.client = (*fabcore.GatewaysClient)(fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient()) + d.client = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() } func (d *dataSourceOnPremisesGatewayPersonal) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var data datasourceOnPremisesGatewayPersonalModel + tflog.Debug(ctx, "READ", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "READ", map[string]any{ + "config": req.Config, + }) + var data datasourceOnPremisesGatewayPersonalModel if resp.Diagnostics.Append(req.Config.Get(ctx, &data)...); resp.Diagnostics.HasError() { return } - if data.ID.ValueString() == "" { - resp.Diagnostics.AddError( - "Missing ID", - "An ID is required to look up a personal on-premises gateway.", - ) + timeout, diags := data.Timeouts.Read(ctx, d.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { return } - gatewayResp, errResp := d.client.GetGateway(ctx, data.ID.ValueString(), nil) - if errResp != nil { - resp.Diagnostics.AddError("GetGateway failed", errResp.Error()) + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + gatewayResp, err := d.client.GetGateway(ctx, data.ID.ValueString(), nil) + if err != nil { + resp.Diagnostics.AddError("GetGateway failed", err.Error()) return } @@ -112,14 +117,17 @@ func (d *dataSourceOnPremisesGatewayPersonal) Read(ctx context.Context, req data return } - gateway := datasourceOnPremisesGatewayPersonalModel{} - diags := gateway.set(ctx, *realGw) + data.set(ctx, *realGw) if diags.HasError() { resp.Diagnostics.Append(diags...) return } - if diags := resp.State.Set(ctx, gateway); diags.HasError() { + tflog.Debug(ctx, "READ", map[string]any{ + "action": "end", + }) + + if diags := resp.State.Set(ctx, data); diags.HasError() { resp.Diagnostics.Append(diags...) return } diff --git a/internal/services/gateway/data_on_premises_gateway_personal_test.go b/internal/services/gateway/data_on_premises_gateway_personal_test.go index 8baaf584..cd11d063 100644 --- a/internal/services/gateway/data_on_premises_gateway_personal_test.go +++ b/internal/services/gateway/data_on_premises_gateway_personal_test.go @@ -49,10 +49,19 @@ func TestUnit_OnPremisesGatewayPersonalDataSource(t *testing.T) { testDataSourceOnPremisesPersonalHeader, map[string]any{}, ), - // "Missing ID" error is raised in Read when data.ID is empty. - ExpectError: regexp.MustCompile(`Missing ID`), + ExpectError: regexp.MustCompile(`The argument "id" is required`), }, - // Step 3: Invalid UUID string should trigger an error. + // Step 3: Read by id - not found + { + Config: at.CompileConfig( + testDataSourceOnPremisesItemHeader, + map[string]any{ + "id": testhelp.RandomUUID(), + }, + ), + ExpectError: regexp.MustCompile(common.ErrorReadHeader), + }, + // Step 4: Invalid UUID string should trigger an error. { Config: at.CompileConfig( testDataSourceOnPremisesPersonalHeader, @@ -62,7 +71,7 @@ func TestUnit_OnPremisesGatewayPersonalDataSource(t *testing.T) { ), ExpectError: regexp.MustCompile(`invalid uuid`), }, - // Step 4: Valid read test using the entity's ID. + // Step 5: Valid read test using the entity's ID. { Config: at.CompileConfig( testDataSourceOnPremisesPersonalHeader, @@ -80,34 +89,3 @@ func TestUnit_OnPremisesGatewayPersonalDataSource(t *testing.T) { }, )) } - -func TestAcc_OnPremisesGatewayPersonalDataSource(t *testing.T) { - entity := testhelp.WellKnown()["OnPremisesGatewayPersonal"].(map[string]any) - entityID := entity["id"].(string) - entityDescription := entity["description"].(string) - - resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ - { - Config: at.CompileConfig( - testDataSourceOnPremisesPersonalHeader, - map[string]any{ - "id": entityID, - }, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(testDataSourceOnPremisesPersonalFQN, "id", entityID), - resource.TestCheckResourceAttr(testDataSourceOnPremisesPersonalFQN, "description", entityDescription), - ), - }, - // read by id - not found - { - Config: at.CompileConfig( - testDataSourceOnPremisesPersonalHeader, - map[string]any{ - "id": testhelp.RandomUUID(), - }, - ), - ExpectError: regexp.MustCompile(common.ErrorReadHeader), - }, - })) -} diff --git a/internal/services/gateway/data_on_premises_gateway_test.go b/internal/services/gateway/data_on_premises_gateway_test.go index b8f78570..1853ba3d 100644 --- a/internal/services/gateway/data_on_premises_gateway_test.go +++ b/internal/services/gateway/data_on_premises_gateway_test.go @@ -131,36 +131,3 @@ func TestUnit_OnPremisesGatewayDataSource(t *testing.T) { }, })) } - -func TestAcc_OnPremisesGatewayDataSource(t *testing.T) { - entity := testhelp.WellKnown()["OnPremisesGateway"].(map[string]any) - entityID := entity["id"].(string) - entityDisplayName := entity["displayName"].(string) - entityDescription := entity["description"].(string) - - resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ - { - Config: at.CompileConfig( - testDataSourceOnPremisesItemHeader, - map[string]any{ - "id": entityID, - }, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(testDataSourceOnPremisesItemFabricFQN, "id", entityID), - resource.TestCheckResourceAttr(testDataSourceOnPremisesItemFabricFQN, "display_name", entityDisplayName), - resource.TestCheckResourceAttr(testDataSourceOnPremisesItemFabricFQN, "description", entityDescription), - ), - }, - // read by id - not found - { - Config: at.CompileConfig( - testDataSourceOnPremisesItemHeader, - map[string]any{ - "id": testhelp.RandomUUID(), - }, - ), - ExpectError: regexp.MustCompile(common.ErrorReadHeader), - }, - })) -} diff --git a/internal/services/gateway/data_on_premises_gateways_test.go b/internal/services/gateway/data_on_premises_gateways_test.go index 91ed3699..39544eb2 100644 --- a/internal/services/gateway/data_on_premises_gateways_test.go +++ b/internal/services/gateway/data_on_premises_gateways_test.go @@ -52,23 +52,3 @@ func TestUnit_OnPremisesGatewaysDataSource(t *testing.T) { }, )) } - -func TestAcc_OnPremisesGatewaysDataSource(t *testing.T) { - resource.ParallelTest(t, testhelp.NewTestAccCase( - t, - nil, - nil, - []resource.TestStep{ - // read - { - Config: at.CompileConfig( - testDataSourceOnPremisesGatewaysHeader, - map[string]any{}, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(testDataSourceOnPremisesGatewaysFQN, "values.0.id"), - ), - }, - }, - )) -} diff --git a/internal/services/gateway/data_virtual_network_gateway.go b/internal/services/gateway/data_virtual_network_gateway.go index f7ceed07..75b585d5 100644 --- a/internal/services/gateway/data_virtual_network_gateway.go +++ b/internal/services/gateway/data_virtual_network_gateway.go @@ -130,7 +130,7 @@ func (d *dataSourceVirtualNetworkGateway) Configure(_ context.Context, req datas } d.pConfigData = pConfigData - d.client = (*fabcore.GatewaysClient)(fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient()) + d.client = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() } // Read refreshes the Terraform state with the latest data. @@ -190,10 +190,13 @@ func (d *dataSourceVirtualNetworkGateway) getByID(ctx context.Context, model *da if gw, ok := respGet.GatewayClassification.(*fabcore.VirtualNetworkGateway); ok { model.set(ctx, *gw) return nil - } else { - var diags diag.Diagnostics - diags.AddError(common.ErrorReadHeader, "expected gateway to be an on-premises gateway") - return diags + } + + return diag.Diagnostics{ + diag.NewErrorDiagnostic( + common.ErrorReadHeader, + "expected gateway to be a virtual network gateway", + ), } } @@ -215,7 +218,5 @@ func (d *dataSourceVirtualNetworkGateway) getByDisplayName(ctx context.Context, } } - var diags diag.Diagnostics - diags.AddError(common.ErrorReadHeader, "expected gateway to be an on-premises gateway") - return diags + return diag.Diagnostics{diag.NewErrorDiagnostic(common.ErrorReadHeader, "virtual network gateway not found")} } diff --git a/internal/services/gateway/data_virtual_network_gateway_test.go b/internal/services/gateway/data_virtual_network_gateway_test.go index 3e331f93..f3974a34 100644 --- a/internal/services/gateway/data_virtual_network_gateway_test.go +++ b/internal/services/gateway/data_virtual_network_gateway_test.go @@ -133,10 +133,9 @@ func TestUnit_VirtualNetworkGatewayDataSource(t *testing.T) { } func TestAcc_VirtualNetworkGatewayDataSource(t *testing.T) { - entity := testhelp.WellKnown()["VirtualNetworkGateway"].(map[string]any) + entity := testhelp.WellKnown()["GatewayVirtualNetwork"].(map[string]any) entityID := entity["id"].(string) entityDisplayName := entity["displayName"].(string) - entityDescription := entity["description"].(string) resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ { @@ -149,7 +148,13 @@ func TestAcc_VirtualNetworkGatewayDataSource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testDataSourceVirtualNetworkFQN, "id", entityID), resource.TestCheckResourceAttr(testDataSourceVirtualNetworkFQN, "display_name", entityDisplayName), - resource.TestCheckResourceAttr(testDataSourceVirtualNetworkFQN, "description", entityDescription), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "inactivity_minutes_before_sleep"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "capacity_id"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "number_of_member_gateways"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "virtual_network_azure_resource.subscription_id"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "virtual_network_azure_resource.resource_group_name"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "virtual_network_azure_resource.virtual_network_name"), + resource.TestCheckResourceAttrSet(testDataSourceVirtualNetworkFQN, "virtual_network_azure_resource.subnet_name"), ), }, // read by id - not found diff --git a/internal/services/gateway/fake_test.go b/internal/services/gateway/fake_test.go index ea701fb8..64f8149d 100644 --- a/internal/services/gateway/fake_test.go +++ b/internal/services/gateway/fake_test.go @@ -46,7 +46,7 @@ func NewRandomGatewayRoleAssignments() fabcore.GatewayRoleAssignments { }, { ID: azto.Ptr(assignmentID1), - Role: azto.Ptr(fabcore.GatewayRoleAdmin), + Role: azto.Ptr(fabcore.GatewayRoleConnectionCreator), Principal: &fabcore.Principal{ ID: azto.Ptr(principalID1), Type: azto.Ptr(fabcore.PrincipalTypeUser), diff --git a/internal/services/gateway/models_gateway_data_role_assignment.go b/internal/services/gateway/models_gateway_data_role_assignment.go index b8354c15..d4e37c47 100644 --- a/internal/services/gateway/models_gateway_data_role_assignment.go +++ b/internal/services/gateway/models_gateway_data_role_assignment.go @@ -27,7 +27,7 @@ func (to *dataSourceGatewayRoleAssignmentsModel) setValues(ctx context.Context, for _, entity := range from { var entityModel gatewayRoleAssignmentModel - if diags := entityModel.set(ctx, entity); diags.HasError() { + if diags := entityModel.set(entity); diags.HasError() { return diags } @@ -37,58 +37,16 @@ func (to *dataSourceGatewayRoleAssignmentsModel) setValues(ctx context.Context, return to.Values.Set(ctx, slice) } -// maybe use common infra from WS type gatewayRoleAssignmentModel struct { - ID customtypes.UUID `tfsdk:"id"` - Role types.String `tfsdk:"role"` - DisplayName types.String `tfsdk:"display_name"` - Type types.String `tfsdk:"type"` - Details supertypes.SingleNestedObjectValueOf[principalDetailsModel] `tfsdk:"details"` + ID customtypes.UUID `tfsdk:"id"` + Role types.String `tfsdk:"role"` + DisplayName types.String `tfsdk:"display_name"` + Type types.String `tfsdk:"type"` } -func (to *gatewayRoleAssignmentModel) set(ctx context.Context, from fabcore.GatewayRoleAssignment) diag.Diagnostics { +func (to *gatewayRoleAssignmentModel) set(from fabcore.GatewayRoleAssignment) diag.Diagnostics { to.ID = customtypes.NewUUIDPointerValue(from.ID) to.Role = types.StringPointerValue((*string)(from.Role)) - // Initialize the details model and set its values. - detailsModel := &principalDetailsModel{} - detailsModel.set(from.Principal, to) - - if diags := to.Details.Set(ctx, detailsModel); diags.HasError() { - return diags - } - - // Set common attributes from the principal. - to.DisplayName = types.StringPointerValue(from.Principal.DisplayName) - to.Type = types.StringPointerValue((*string)(from.Principal.Type)) return nil } - -type principalDetailsModel struct { - UserPrincipalName types.String `tfsdk:"user_principal_name"` - GroupType types.String `tfsdk:"group_type"` - AppID customtypes.UUID `tfsdk:"app_id"` - ParentPrincipalID customtypes.UUID `tfsdk:"parent_principal_id"` -} - -func (to *principalDetailsModel) set(from *fabcore.Principal, roleAssignment *gatewayRoleAssignmentModel) { - to.UserPrincipalName = types.StringNull() - to.GroupType = types.StringNull() - to.AppID = customtypes.NewUUIDNull() - to.ParentPrincipalID = customtypes.NewUUIDNull() - - // Set the DisplayName and Type on the role assignment (if not already set). - roleAssignment.DisplayName = types.StringPointerValue(from.DisplayName) - roleAssignment.Type = types.StringPointerValue((*string)(from.Type)) - - switch *from.Type { - case fabcore.PrincipalTypeUser: - to.UserPrincipalName = types.StringPointerValue(from.UserDetails.UserPrincipalName) - case fabcore.PrincipalTypeGroup: - to.GroupType = types.StringPointerValue((*string)(from.GroupDetails.GroupType)) - case fabcore.PrincipalTypeServicePrincipal: - to.AppID = customtypes.NewUUIDPointerValue(from.ServicePrincipalDetails.AADAppID) - case fabcore.PrincipalTypeServicePrincipalProfile: - to.ParentPrincipalID = customtypes.NewUUIDPointerValue(from.ServicePrincipalProfileDetails.ParentPrincipal.ID) - } -} diff --git a/internal/services/gateway/models_resource_gateway_role_assignment.go b/internal/services/gateway/models_gateway_resource_role_assignment.go similarity index 100% rename from internal/services/gateway/models_resource_gateway_role_assignment.go rename to internal/services/gateway/models_gateway_resource_role_assignment.go diff --git a/internal/services/gateway/resource_gateway_role_assignments_test.go b/internal/services/gateway/resource_gateway_role_assignments_test.go index 67203452..d066cafc 100644 --- a/internal/services/gateway/resource_gateway_role_assignments_test.go +++ b/internal/services/gateway/resource_gateway_role_assignments_test.go @@ -31,7 +31,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { map[string]any{ "principal_id": "00000000-0000-0000-0000-000000000000", "principal_type": "User", - "role": "Member", + "role": "ConnectionCreator", }, ), ExpectError: regexp.MustCompile(`The argument "gateway_id" is required, but no definition was found.`), @@ -44,7 +44,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { map[string]any{ "gateway_id": "00000000-0000-0000-0000-000000000000", "principal_type": "User", - "role": "Member", + "role": "Admin", }, ), ExpectError: regexp.MustCompile(`The argument "principal_id" is required, but no definition was found.`), @@ -57,7 +57,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { map[string]any{ "gateway_id": "00000000-0000-0000-0000-000000000000", "principal_id": "00000000-0000-0000-0000-000000000000", - "role": "Member", + "role": "ConnectionCreator", }, ), ExpectError: regexp.MustCompile(`The argument "principal_type" is required, but no definition was found.`), @@ -70,7 +70,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { map[string]any{ "gateway_id": "00000000-0000-0000-0000-000000000000", "principal_id": "00000000-0000-0000-0000-000000000000", - "principal_type": "User", + "principal_type": "Admin", }, ), ExpectError: regexp.MustCompile(`The argument "role" is required, but no definition was found.`), @@ -84,7 +84,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { "gateway_id": "invalid uuid", "principal_id": "00000000-0000-0000-0000-000000000000", "principal_type": "User", - "role": "Member", + "role": "ConnectionCreator", }, ), ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), @@ -98,7 +98,7 @@ func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { "gateway_id": "00000000-0000-0000-0000-000000000000", "principal_id": "invalid uuid", "principal_type": "User", - "role": "Member", + "role": "ConnectionCreator", }, ), ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), @@ -146,7 +146,7 @@ func TestUnit_GatewayRoleAssignmentResource_ImportState(t *testing.T) { func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { // Assume a well-known gateway is defined in the test environment. - gateway := testhelp.WellKnown()["Gateway"].(map[string]any) + gateway := testhelp.WellKnown()["GatewayVirtualNetwork"].(map[string]any) gatewayID := gateway["id"].(string) // Assume a known principal is available. @@ -164,14 +164,14 @@ func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { "gateway_id": gatewayID, "principal_id": principalID, "principal_type": principalType, - "role": "Member", + "role": "ConnectionCreator", }, ), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "gateway_id", gatewayID), resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_id", principalID), resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_type", principalType), - resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "Member"), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "ConnectionCreator"), ), }, // Update and Read @@ -183,14 +183,14 @@ func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { "gateway_id": gatewayID, "principal_id": principalID, "principal_type": principalType, - "role": "Viewer", + "role": "Admin", }, ), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "gateway_id", gatewayID), resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_id", principalID), resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_type", principalType), - resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "Viewer"), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "Admin"), ), }, })) diff --git a/internal/services/gateway/resource_virtual_network_gateway_test.go b/internal/services/gateway/resource_virtual_network_gateway_test.go index f84080f2..688f22d1 100644 --- a/internal/services/gateway/resource_virtual_network_gateway_test.go +++ b/internal/services/gateway/resource_virtual_network_gateway_test.go @@ -14,7 +14,9 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/microsoft/terraform-provider-fabric/internal/common" "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" "github.com/microsoft/terraform-provider-fabric/internal/testhelp" "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" ) @@ -135,8 +137,16 @@ func TestUnit_VirtualNetworkGatewayResource_ImportState(t *testing.T) { testConfig := at.CompileConfig( testResourceVirtualNetworkGatewayHeader, map[string]any{ - "display_name": *entity.DisplayName, - // Other required attributes will be populated from state. + "display_name": *entity.DisplayName, + "capacity_id": *entity.CapacityID, + "inactivity_minutes_before_sleep": 30, + "number_of_member_gateways": 3, + "virtual_network_azure_resource": map[string]any{ + "subscription_id": "123e4567-e89b-12d3-a456-426614174001", + "resource_group_name": "test-rg", + "virtual_network_name": "test-vnet", + "subnet_name": "test-subnet", + }, }, ) @@ -237,17 +247,26 @@ func TestUnit_VirtualNetworkGatewayResource_CRUD(t *testing.T) { fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ - // // Error: Attempting to create a duplicate gateway (existing entity). - // { - // ResourceName: testResourceVirtualNetworkGatewayFQN, - // Config: at.CompileConfig( - // testResourceVirtualNetworkGatewayHeader, - // map[string]any{ - // "display_name": *entityExist.DisplayName, - // }, - // ), - // ExpectError: regexp.MustCompile(common.ErrorCreateHeader), - // }, + // Error: Attempting to create a duplicate gateway (existing entity). + { + ResourceName: testResourceVirtualNetworkGatewayFQN, + Config: at.CompileConfig( + testResourceVirtualNetworkGatewayHeader, + map[string]any{ + "display_name": *entityExist.DisplayName, + "capacity_id": *entityBefore.CapacityID, + "inactivity_minutes_before_sleep": 30, + "number_of_member_gateways": 3, + "virtual_network_azure_resource": map[string]any{ + "subscription_id": "123e4567-e89b-12d3-a456-426614174001", + "resource_group_name": "test-rg", + "virtual_network_name": "test-vnet", + "subnet_name": "test-subnet", + }, + }, + ), + ExpectError: regexp.MustCompile(common.ErrorCreateHeader), + }, // Create and Read { ResourceName: testResourceVirtualNetworkGatewayFQN, @@ -268,7 +287,13 @@ func TestUnit_VirtualNetworkGatewayResource_CRUD(t *testing.T) { ), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPtr(testResourceVirtualNetworkGatewayFQN, "display_name", entityBefore.DisplayName), - resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "capacity_id", *entityBefore.CapacityID), + resource.TestCheckResourceAttrPtr(testResourceVirtualNetworkGatewayFQN, "capacity_id", entityBefore.CapacityID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "inactivity_minutes_before_sleep", "30"), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "number_of_member_gateways", "3"), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subscription_id", "123e4567-e89b-12d3-a456-426614174001"), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.resource_group_name", "test-rg"), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.virtual_network_name", "test-vnet"), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subnet_name", "test-subnet"), ), }, // Update and Read @@ -277,15 +302,117 @@ func TestUnit_VirtualNetworkGatewayResource_CRUD(t *testing.T) { Config: at.CompileConfig( testResourceVirtualNetworkGatewayHeader, map[string]any{ - "display_name": *entityBefore.DisplayName, - "capacity_id": *entityAfter.CapacityID, + "display_name": *entityAfter.DisplayName, + "capacity_id": *entityAfter.CapacityID, + "inactivity_minutes_before_sleep": int(*entityAfter.InactivityMinutesBeforeSleep), + "number_of_member_gateways": int(*entityAfter.NumberOfMemberGateways), + "virtual_network_azure_resource": map[string]any{ + "subscription_id": *entityAfter.VirtualNetworkAzureResource.SubscriptionID, + "resource_group_name": *entityAfter.VirtualNetworkAzureResource.ResourceGroupName, + "virtual_network_name": *entityAfter.VirtualNetworkAzureResource.VirtualNetworkName, + "subnet_name": *entityAfter.VirtualNetworkAzureResource.SubnetName, + }, }, ), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPtr(testResourceVirtualNetworkGatewayFQN, "display_name", entityBefore.DisplayName), - resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "capacity_id", *entityAfter.CapacityID), + resource.TestCheckResourceAttrPtr(testResourceVirtualNetworkGatewayFQN, "display_name", entityAfter.DisplayName), + resource.TestCheckResourceAttrPtr(testResourceVirtualNetworkGatewayFQN, "capacity_id", entityAfter.CapacityID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*entityAfter.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "number_of_member_gateways", strconv.Itoa(int(*entityAfter.NumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subscription_id", *entityAfter.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.resource_group_name", *entityAfter.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.virtual_network_name", *entityAfter.VirtualNetworkAzureResource.VirtualNetworkName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subnet_name", *entityAfter.VirtualNetworkAzureResource.SubnetName), ), }, }, )) } + +func TestAcc_VirtualNetworkGatewayResource_CRUD(t *testing.T) { + // Get well-known test values + capacity := testhelp.WellKnown()["Capacity"].(map[string]any) + capacityID := capacity["id"].(string) + + // Generate random names for testing + entityCreateDisplayName := testhelp.RandomName() + entityUpdateDisplayName := testhelp.RandomName() + + initialVirtualNetworkAzureResource := testhelp.WellKnown()["VirtualNetworkInitial"].(map[string]any) + initSubscriptionID := initialVirtualNetworkAzureResource["subscriptionId"].(string) + initResourceGroupName := initialVirtualNetworkAzureResource["resourceGroupName"].(string) + initVirtualNetworkName := initialVirtualNetworkAzureResource["name"].(string) + initSubnetName := initialVirtualNetworkAzureResource["subnetName"].(string) + + entityInitialInactivityMinutesBeforeSleep := 30 + entityUpdateInactivityMinutesBeforeSleep := 60 + entityInitialNumberOfMemberGateways := int(testhelp.RandomInt32Range(gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)) + entityUpdateNumberOfMemberGateways := int(testhelp.RandomInt32Range(gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)) + + updateVirtualNetworkAzureResource := testhelp.WellKnown()["VirtualNetworkUpdate"].(map[string]any) + updateVirtualNetworkName := updateVirtualNetworkAzureResource["name"].(string) + updateResourceGroupName := updateVirtualNetworkAzureResource["resourceGroupName"].(string) + updateSubnetName := updateVirtualNetworkAzureResource["subnetName"].(string) + updateSubscriptionID := updateVirtualNetworkAzureResource["subscriptionId"].(string) + + resource.Test(t, testhelp.NewTestAccCase(t, &testResourceVirtualNetworkGatewayFQN, nil, []resource.TestStep{ + // Create and Read + { + ResourceName: testResourceVirtualNetworkGatewayFQN, + Config: at.CompileConfig( + testResourceVirtualNetworkGatewayHeader, + map[string]any{ + "display_name": entityCreateDisplayName, + "capacity_id": capacityID, + "inactivity_minutes_before_sleep": entityInitialInactivityMinutesBeforeSleep, + "number_of_member_gateways": entityInitialNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "subscription_id": initSubscriptionID, + "resource_group_name": initResourceGroupName, + "virtual_network_name": initVirtualNetworkName, + "subnet_name": initSubnetName, + }, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "display_name", entityCreateDisplayName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "capacity_id", capacityID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "inactivity_minutes_before_sleep", strconv.Itoa(entityInitialInactivityMinutesBeforeSleep)), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "number_of_member_gateways", strconv.Itoa(entityInitialNumberOfMemberGateways)), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subscription_id", initSubscriptionID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.resource_group_name", initResourceGroupName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.virtual_network_name", initVirtualNetworkName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subnet_name", initSubnetName), + ), + }, + // Update and Read + { + ResourceName: testResourceVirtualNetworkGatewayFQN, + Config: at.CompileConfig( + testResourceVirtualNetworkGatewayHeader, + map[string]any{ + "display_name": entityUpdateDisplayName, + "capacity_id": capacityID, + "inactivity_minutes_before_sleep": entityUpdateInactivityMinutesBeforeSleep, + "number_of_member_gateways": entityUpdateNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "subscription_id": updateSubscriptionID, + "resource_group_name": updateResourceGroupName, + "virtual_network_name": updateVirtualNetworkName, + "subnet_name": updateSubnetName, + }, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "display_name", entityUpdateDisplayName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "capacity_id", capacityID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "inactivity_minutes_before_sleep", strconv.Itoa(entityUpdateInactivityMinutesBeforeSleep)), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "number_of_member_gateways", strconv.Itoa(entityUpdateNumberOfMemberGateways)), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subscription_id", updateSubscriptionID), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.resource_group_name", updateResourceGroupName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.virtual_network_name", updateVirtualNetworkName), + resource.TestCheckResourceAttr(testResourceVirtualNetworkGatewayFQN, "virtual_network_azure_resource.subnet_name", updateSubnetName), + ), + }, + })) +} diff --git a/internal/testhelp/fakes/fabric_gateway.go b/internal/testhelp/fakes/fabric_gateway.go index e43bd73b..fb20f22f 100644 --- a/internal/testhelp/fakes/fabric_gateway.go +++ b/internal/testhelp/fakes/fabric_gateway.go @@ -10,6 +10,7 @@ import ( fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" fabfake "github.com/microsoft/fabric-sdk-go/fabric/fake" + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" "github.com/microsoft/terraform-provider-fabric/internal/testhelp" ) @@ -85,9 +86,6 @@ func (o *operationsGateway) Update(base fabcore.GatewayClassification, data fabc // Validate implements concreteEntityOperations. func (o *operationsGateway) Validate(newEntity fabcore.GatewayClassification, existing []fabcore.GatewayClassification) (statusCode int, err error) { for _, existingGateway := range existing { - if existingGateway.GetGateway().Type != newEntity.GetGateway().Type { - continue - } switch gateway := newEntity.(type) { case *fabcore.VirtualNetworkGateway: vng := existingGateway.(*fabcore.VirtualNetworkGateway) @@ -162,8 +160,8 @@ func NewRandomVirtualNetworkGateway() *fabcore.VirtualNetworkGateway { return &fabcore.VirtualNetworkGateway{ ID: to.Ptr(testhelp.RandomUUID()), DisplayName: to.Ptr(testhelp.RandomName()), - InactivityMinutesBeforeSleep: to.Ptr(testhelp.RandomInt32(100)), - NumberOfMemberGateways: to.Ptr(testhelp.RandomInt32(100)), + InactivityMinutesBeforeSleep: to.Ptr(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)), + NumberOfMemberGateways: to.Ptr(testhelp.RandomInt32Max(7)), CapacityID: to.Ptr(testhelp.RandomUUID()), Type: to.Ptr(fabcore.GatewayTypeVirtualNetwork), VirtualNetworkAzureResource: &fabcore.VirtualNetworkAzureResource{ @@ -179,7 +177,7 @@ func NewRandomOnPremisesGateway() *fabcore.OnPremisesGateway { return &fabcore.OnPremisesGateway{ ID: to.Ptr(testhelp.RandomUUID()), DisplayName: to.Ptr(testhelp.RandomName()), - NumberOfMemberGateways: to.Ptr(testhelp.RandomInt32(100)), + NumberOfMemberGateways: to.Ptr(testhelp.RandomInt32Max(7)), Type: to.Ptr(fabcore.GatewayTypeOnPremises), AllowCloudConnectionRefresh: to.Ptr(true), AllowCustomConnectors: to.Ptr(false), diff --git a/internal/testhelp/utils.go b/internal/testhelp/utils.go index e2ce3265..656e7db1 100644 --- a/internal/testhelp/utils.go +++ b/internal/testhelp/utils.go @@ -51,10 +51,18 @@ func RandomP12Cert(password string) []byte { return p12 } -func RandomInt32(max int32) int32 { +func RandomInt32Max(max int32) int32 { return rand.Int31n(max) } +func RandomInt32Range(min int32, max int32) int32 { + return min + rand.Int31n(max-min+1) +} + +func RandomElement[T any](slice []T) T { + return slice[rand.Intn(len(slice))] +} + func createP12Bundle(certPEMStr, privateKeyPEMStr, password string) ([]byte, error) { // Decode the private key PEM block block, _ := pem.Decode([]byte(privateKeyPEMStr)) diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 index 15c22db8..80dee62e 100644 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -474,8 +474,152 @@ function Set-FabricWorkspaceRoleAssignment { } } +function Set-FabricGatewayVirtualNetwork { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$DisplayName, + + [Parameter(Mandatory = $true)] + [string]$CapacityId, + + # Inactivity time (in minutes) before the gateway goes to auto-sleep. + # Allowed values: 30, 60, 90, 120, 150, 240, 360, 480, 720, 1440. + [Parameter(Mandatory = $true)] + [int]$InactivityMinutesBeforeSleep, + + # Number of member gateways (between 1 and 7). + [Parameter(Mandatory = $true)] + [int]$NumberOfMemberGateways, + + # Azure virtual network details: + [Parameter(Mandatory = $true)] + [string]$SubscriptionId, + + [Parameter(Mandatory = $true)] + [string]$ResourceGroupName, + + [Parameter(Mandatory = $true)] + [string]$VirtualNetworkName, + + [Parameter(Mandatory = $true)] + [string]$SubnetName + ) + + # Attempt to check for an existing gateway with the same display name. + $existingGateways = Invoke-FabricRest -Method 'GET' -Endpoint "gateways" + $result = $existingGateways.Response.value | Where-Object { $_.displayName -eq $DisplayName } + if (!$result) { + # Construct the payload for creating a Virtual Network gateway. + # Refer to the API documentation for details on the request format :contentReference[oaicite:1]{index=1} and the Virtual Network Azure Resource :contentReference[oaicite:2]{index=2}. + $payload = @{ + type = "VirtualNetwork" + displayName = $DisplayName + capacityId = $CapacityId + inactivityMinutesBeforeSleep = $InactivityMinutesBeforeSleep + numberOfMemberGateways = $NumberOfMemberGateways + virtualNetworkAzureResource = @{ + subscriptionId = $SubscriptionId + resourceGroupName = $ResourceGroupName + virtualNetworkName = $VirtualNetworkName + subnetName = $SubnetName + } + } + + Write-Log -Message "Creating Virtual Network Gateway: $DisplayName" -Level 'WARN' + $result = Invoke-FabricRest -Method 'POST' -Endpoint "gateways" -Payload $payload + } + + Write-Log -Message "Gateway Virtual Network - Name: $($result.displayName) / ID: $($result.id)" + return $result +} + + +function Set-AzureVirtualNetwork { + param( + [Parameter(Mandatory = $true)] + [string]$ResourceGroupName, + + [Parameter(Mandatory = $true)] + [string]$VNetName, + + [Parameter(Mandatory = $true)] + [string]$Location, + + [Parameter(Mandatory = $true)] + [string[]]$AddressPrefixes, + + [Parameter(Mandatory = $true)] + [string]$SubnetName, + + [Parameter(Mandatory = $true)] + [string[]]$SubnetAddressPrefixes + ) + + # Attempt to get the existing Virtual Network + $vnet = Get-AzVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName + if (!$vnet) { + # VNet does not exist, so create it + Write-Log -Message "Creating VNet: $VNetName in Resource Group: $ResourceGroupName" -Level 'WARN' + $subnetConfig = New-AzVirtualNetworkSubnetConfig ` + -Name $SubnetName ` + -AddressPrefix $SubnetAddressPrefixes ` + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnetConfig + + $vnet = New-AzVirtualNetwork ` + -Name $VNetName ` + -ResourceGroupName $ResourceGroupName ` + -Location $Location ` + -AddressPrefix $AddressPrefixes ` + -Subnet $subnetConfig + + # Commit creation + $vnet = $vnet | Set-AzVirtualNetwork + Write-Log -Message "Created VNet: $VNetName" -Level 'INFO' + return $vnet + } + + # If the VNet already exists, check for the subnet + $subnet = $vnet.Subnets | Where-Object { $_.Name -eq $SubnetName } + if (-not $subnet) { + # Subnet does not exist; add one with the delegation + Write-Log -Message "Adding subnet '$SubnetName' with delegation 'Microsoft.PowerPlatform/vnetaccesslinks' to VNet '$VNetName'." -Level 'WARN' + $subnetConfig = New-AzVirtualNetworkSubnetConfig ` + -Name $SubnetName ` + -AddressPrefix $SubnetAddressPrefixes ` + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnetConfig + + $vnet = $vnet | Set-AzVirtualNetwork + } + else { + # Subnet exists; ensure it has the correct delegation + $existingDelegation = $subnet.Delegations | Where-Object { $_.ServiceName -eq 'Microsoft.PowerPlatform/vnetaccesslinks' } + if (-not $existingDelegation) { + Write-Log -Message "Subnet '$SubnetName' found but missing delegation to 'Microsoft.PowerPlatform/vnetaccesslinks'. Adding Microsoft.PowerPlatform/vnetaccesslinks delegation..." -Level 'WARN' + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnet + + $vnet = $vnet | Set-AzVirtualNetwork + Write-Log -Message "Added missing delegation to subnet '$SubnetName'." -Level 'INFO' + } + } + Write-Log -Message "Az Virtual Network - Name: $($vnet.Name)" + return $vnet +} + # Define an array of modules to install -$modules = @('Az.Accounts', 'Az.Resources', 'Az.Fabric', 'pwsh-dotenv', 'ADOPS') +$modules = @('Az.Accounts', 'Az.Network', 'Az.Resources', 'Az.Fabric', 'pwsh-dotenv', 'ADOPS') # Loop through each module and install if not installed foreach ($module in $modules) { @@ -488,8 +632,8 @@ if (Test-Path -Path './wellknown.env') { Import-Dotenv -Path ./wellknown.env -AllowClobber } -if (!$Env:FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX) { - Write-Log -Message 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID, FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID, FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME, FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME and FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX are required environment variables.' -Level 'ERROR' +if (!$Env:FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION) { + Write-Log -Message 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID, FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID, FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME, FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME and FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX and FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME and FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION are required environment variables.' -Level 'ERROR' } # Check if already logged in to Azure, if not then login @@ -521,7 +665,7 @@ $capacity = $capacities.Response.value | Where-Object { $_.displayName -eq $Env: if (!$capacity) { Write-Log -Message "Fabric Capacity: $($Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME)" } -Write-Log -Message "Fabric Capacity - Name: $($Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME) / ID: $($capacity.id)" +Write-Log -Message "Fabric Capacity - Name: $($Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME) / ID: $($capacity.ID)" $wellKnown['Capacity'] = @{ id = $capacity.id displayName = $capacity.displayName @@ -535,6 +679,7 @@ $itemNaming = @{ 'Environment' = 'env' 'Eventhouse' = 'eh' 'Eventstream' = 'es' + 'GatewayVirtualNetwork' = 'gvn' 'KQLDashboard' = 'kqldash' 'KQLDatabase' = 'kqldb' 'KQLQueryset' = 'kqlqs' @@ -559,6 +704,9 @@ $itemNaming = @{ 'EntraServicePrincipal' = 'sp' 'EntraGroup' = 'grp' 'AzDOProject' = 'proj' + 'VirtualNetworkSubnet' = 'subnet' + 'VirtualNetworkInitial' = 'vneti' + 'VirtualNetworkUpdate' = 'vnetu' } $baseName = Get-BaseName @@ -568,6 +716,8 @@ $Env:FABRIC_TESTACC_WELLKNOWN_NAME_BASE = $baseName $envVarNames = @( 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID', 'FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID', + 'FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME' + 'FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION', 'FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME', 'FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME', 'FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX', @@ -660,7 +810,7 @@ $definition = @{ parts = @( @{ path = "mirroring.json" - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/mirrored_database/mirroring.json' + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/mirrored_database/mirroring.json' payloadType = 'InlineBase64' } ) @@ -678,12 +828,12 @@ $definition = @{ parts = @( @{ path = 'definition.pbism' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/semantic_model_tmsl/definition.pbism' + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/semantic_model_tmsl/definition.pbism' payloadType = 'InlineBase64' } @{ path = 'model.bim' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/semantic_model_tmsl/model.bim.tmpl' -Values @(@{ key = '{{ .ColumnName }}'; value = 'ColumnTest1' }) + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/semantic_model_tmsl/model.bim.tmpl' -Values @(@{ key = '{{ .ColumnName }}'; value = 'ColumnTest1' }) payloadType = 'InlineBase64' } ) @@ -701,22 +851,22 @@ $definition = @{ parts = @( @{ path = 'definition.pbir' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/report_pbir_legacy/definition.pbir.tmpl' -Values @(@{ key = '{{ .SemanticModelID }}'; value = $semanticModel.id }) + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/report_pbir_legacy/definition.pbir.tmpl' -Values @(@{ key = '{{ .SemanticModelID }}'; value = $semanticModel.id }) payloadType = 'InlineBase64' }, @{ path = 'report.json' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/report_pbir_legacy/report.json' + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/report_pbir_legacy/report.json' payloadType = 'InlineBase64' }, @{ path = 'StaticResources/SharedResources/BaseThemes/CY24SU10.json' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/report_pbir_legacy/StaticResources/SharedResources/BaseThemes/CY24SU10.json' + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/report_pbir_legacy/StaticResources/SharedResources/BaseThemes/CY24SU10.json' payloadType = 'InlineBase64' } @{ path = 'StaticResources/RegisteredResources/fabric_48_color10148978481469717.png' - payload = Get-DefinitionPartBase64 -Path 'internal/testhelp/fixtures/report_pbir_legacy/StaticResources/RegisteredResources/fabric_48_color10148978481469717.png' + payload = Get-DefinitionPartBase64 -Path './internal/testhelp/fixtures/report_pbir_legacy/StaticResources/RegisteredResources/fabric_48_color10148978481469717.png' payloadType = 'InlineBase64' } ) @@ -746,6 +896,76 @@ $wellKnown['DomainChild'] = @{ description = $childDomain.description } + +# Register the Microsoft.PowerPlatform resource provider +Write-Log -Message "Registering Microsoft.PowerPlatform resource provider" -Level 'WARN' +Register-AzResourceProvider -ProviderNamespace "Microsoft.PowerPlatform" + +# Create Azure initial Virtual Network if not exists +$vnetName = "${displayName}_$($itemNaming['VirtualNetworkInitial'])" +$addrRange = "10.10.0.0/16" +$subName = "${displayName}_$($itemNaming['VirtualNetworkSubnet'])" +$subRange = "10.10.1.0/24" + +$vnet = Set-AzureVirtualNetwork ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VNetName $vnetName ` + -Location $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION ` + -AddressPrefixes $addrRange ` + -SubnetName $subName ` + -SubnetAddressPrefixes $subRange + +$wellKnown['VirtualNetworkInitial'] = @{ + name = $vnet.Name + resourceGroupName = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME + subnetName = $subName + subscriptionId = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID +} + + +# Create Azure update Virtual Network if not exists +$vnetName = "${displayName}_$($itemNaming['VirtualNetworkUpdate'])" +$addrRange = "10.10.0.0/16" +$subName = "${displayName}_$($itemNaming['VirtualNetworkSubnet'])" +$subRange = "10.10.1.0/24" + +$vnet = Set-AzureVirtualNetwork ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VNetName $vnetName ` + -Location $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION ` + -AddressPrefixes $addrRange ` + -SubnetName $subName ` + -SubnetAddressPrefixes $subRange + +$wellKnown['VirtualNetworkUpdate'] = @{ + name = $vnet.Name + resourceGroupName = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME + subnetName = $subName + subscriptionId = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID +} + +# Create Fabric Gateway Virtual Network if not exists +$displayNameTemp = "${displayName}_$($itemNaming['GatewayVirtualNetwork'])" +$inactivityMinutesBeforeSleep = 30 +$numberOfMemberGateways = 1 + +$gateway = Set-FabricGatewayVirtualNetwork ` + -DisplayName $displayNameTemp ` + -CapacityId $capacity.id ` + -InactivityMinutesBeforeSleep $inactivityMinutesBeforeSleep ` + -NumberOfMemberGateways $numberOfMemberGateways ` + -SubscriptionId $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VirtualNetworkName $wellKnown['VirtualNetworkInitial'].name ` + -SubnetName $wellKnown['VirtualNetworkInitial'].subnetName + +$wellKnown['GatewayVirtualNetwork'] = @{ + id = $gateway.id + displayName = $gateway.displayName + type = $gateway.type + description = $gateway.description +} + $results = Invoke-FabricRest -Method 'GET' -Endpoint "workspaces/$($workspace.id)/lakehouses/$($wellKnown['Lakehouse']['id'])/tables" $result = $results.Response.data | Where-Object { $_.name -eq 'publicholidays' } if (!$result) {