diff --git a/client/microfrontend_group.go b/client/microfrontend_group.go
new file mode 100644
index 00000000..737c3a64
--- /dev/null
+++ b/client/microfrontend_group.go
@@ -0,0 +1,187 @@
+package client
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+)
+
+type MicrofrontendGroup struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Slug string `json:"slug"`
+ TeamID string `json:"team_id"`
+ Projects map[string]MicrofrontendGroupMembership `json:"projects"`
+ DefaultApp MicrofrontendGroupMembership `json:"defaultApp"`
+}
+
+type MicrofrontendGroupsAPIResponse struct {
+ Groups []struct {
+ Group struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Slug string `json:"slug"`
+ TeamID string `json:"team_id"`
+ Projects map[string]struct {
+ IsDefaultApp bool `json:"isDefaultApp"`
+ DefaultRoute string `json:"defaultRoute"`
+ RouteObservabilityToThisProject bool `json:"routeObservabilityToThisProject"`
+ ProjectID string `json:"projectId"`
+ Enabled bool `json:"enabled"`
+ } `json:"projects"`
+ } `json:"group"`
+ Projects []MicrofrontendGroupMembershipsResponseAPI `json:"projects"`
+ } `json:"groups"`
+}
+
+func (c *Client) CreateMicrofrontendGroup(ctx context.Context, TeamID string, Name string) (r MicrofrontendGroup, err error) {
+ if c.teamID(TeamID) == "" {
+ return r, fmt.Errorf("team_id is required")
+ }
+ tflog.Info(ctx, "creating microfrontend group", map[string]interface{}{
+ "microfrontend_group_name": Name,
+ "team_id": c.teamID(TeamID),
+ })
+ url := fmt.Sprintf("%s/teams/%s/microfrontends", c.baseURL, c.teamID(TeamID))
+ payload := string(mustMarshal(struct {
+ NewMicrofrontendsGroupName string `json:"newMicrofrontendsGroupName"`
+ }{
+ NewMicrofrontendsGroupName: Name,
+ }))
+ apiResponse := struct {
+ NewMicrofrontendGroup MicrofrontendGroup `json:"newMicrofrontendsGroup"`
+ }{}
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "PATCH",
+ url: url,
+ body: payload,
+ }, &apiResponse)
+ if err != nil {
+ return r, err
+ }
+ return MicrofrontendGroup{
+ ID: apiResponse.NewMicrofrontendGroup.ID,
+ Name: apiResponse.NewMicrofrontendGroup.Name,
+ Slug: apiResponse.NewMicrofrontendGroup.Slug,
+ TeamID: c.teamID(TeamID),
+ }, nil
+}
+
+func (c *Client) UpdateMicrofrontendGroup(ctx context.Context, request MicrofrontendGroup) (r MicrofrontendGroup, err error) {
+ if c.teamID(request.TeamID) == "" {
+ return r, fmt.Errorf("team_id is required")
+ }
+ url := fmt.Sprintf("%s/teams/%s/microfrontends/%s", c.baseURL, c.teamID(request.TeamID), request.ID)
+ payload := string(mustMarshal(struct {
+ Name string `json:"name"`
+ }{
+ Name: request.Name,
+ }))
+ tflog.Info(ctx, "updating microfrontend group", map[string]interface{}{
+ "url": url,
+ "payload": payload,
+ })
+ apiResponse := struct {
+ UpdatedMicrofrontendsGroup MicrofrontendGroup `json:"updatedMicrofrontendsGroup"`
+ }{}
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "PATCH",
+ url: url,
+ body: payload,
+ }, &apiResponse)
+ if err != nil {
+ return r, err
+ }
+ return MicrofrontendGroup{
+ ID: apiResponse.UpdatedMicrofrontendsGroup.ID,
+ Name: apiResponse.UpdatedMicrofrontendsGroup.Name,
+ Slug: apiResponse.UpdatedMicrofrontendsGroup.Slug,
+ TeamID: c.teamID(request.TeamID),
+ }, nil
+}
+
+func (c *Client) DeleteMicrofrontendGroup(ctx context.Context, request MicrofrontendGroup) (r struct{}, err error) {
+ if c.teamID(request.TeamID) == "" {
+ return r, fmt.Errorf("team_id is required")
+ }
+ url := fmt.Sprintf("%s/teams/%s/microfrontends/%s", c.baseURL, c.teamID(request.TeamID), request.ID)
+
+ tflog.Info(ctx, "deleting microfrontend group", map[string]interface{}{
+ "url": url,
+ })
+
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "DELETE",
+ url: url,
+ body: "",
+ }, &r)
+ return r, err
+}
+
+func (c *Client) GetMicrofrontendGroup(ctx context.Context, microfrontendGroupID string, teamID string) (r MicrofrontendGroup, err error) {
+ if c.teamID(teamID) == "" {
+ return r, fmt.Errorf("team_id is required")
+ }
+ url := fmt.Sprintf("%s/v1/microfrontends/groups", c.baseURL)
+ if c.teamID(teamID) != "" {
+ url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID))
+ }
+
+ tflog.Info(ctx, "getting microfrontend group", map[string]interface{}{
+ "url": url,
+ })
+ out := MicrofrontendGroupsAPIResponse{}
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "GET",
+ url: url,
+ body: "",
+ }, &out)
+
+ if err != nil {
+ return r, err
+ }
+
+ tflog.Info(ctx, "getting microfrontend group", map[string]interface{}{
+ "out": out,
+ })
+
+ for i := range out.Groups {
+ if out.Groups[i].Group.ID == microfrontendGroupID {
+ projects := map[string]MicrofrontendGroupMembership{}
+ defaultApp := MicrofrontendGroupMembership{}
+ for _, p := range out.Groups[i].Projects {
+ projects[p.ID] = MicrofrontendGroupMembership{
+ MicrofrontendGroupID: microfrontendGroupID,
+ ProjectID: p.ID,
+ TeamID: c.teamID(teamID),
+ Enabled: p.Microfrontends.Enabled,
+ IsDefaultApp: p.Microfrontends.IsDefaultApp,
+ DefaultRoute: p.Microfrontends.DefaultRoute,
+ RouteObservabilityToThisProject: p.Microfrontends.RouteObservabilityToThisProject,
+ }
+ if p.Microfrontends.IsDefaultApp {
+ defaultApp = projects[p.ID]
+ }
+ }
+ r := MicrofrontendGroup{
+ ID: out.Groups[i].Group.ID,
+ Name: out.Groups[i].Group.Name,
+ Slug: out.Groups[i].Group.Slug,
+ TeamID: c.teamID(teamID),
+ DefaultApp: defaultApp,
+ Projects: projects,
+ }
+ tflog.Info(ctx, "returning microfrontend group", map[string]interface{}{
+ "r": r,
+ })
+ return r, nil
+ }
+ }
+
+ return r, fmt.Errorf("microfrontend group not found")
+}
diff --git a/client/microfrontend_group_membership.go b/client/microfrontend_group_membership.go
new file mode 100644
index 00000000..91315a55
--- /dev/null
+++ b/client/microfrontend_group_membership.go
@@ -0,0 +1,137 @@
+package client
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+)
+
+type MicrofrontendGroupMembership struct {
+ MicrofrontendGroupID string `json:"microfrontendsGroupId"`
+ IsDefaultApp bool `json:"isDefaultApp"`
+ DefaultRoute string `json:"defaultRoute"`
+ RouteObservabilityToThisProject bool `json:"routeObservabilityToThisProject"`
+ ProjectID string `json:"projectId"`
+ Enabled bool `json:"enabled"`
+ TeamID string `json:"team_id"`
+}
+
+type MicrofrontendGroupMembershipResponseAPI struct {
+ GroupIds []string `json:"groupIds"`
+ Enabled bool `json:"enabled"`
+ IsDefaultApp bool `json:"isDefaultApp"`
+ DefaultRoute string `json:"defaultRoute"`
+ RouteObservabilityToThisProject bool `json:"routeObservabilityToThisProject"`
+ TeamID string `json:"team_id"`
+ UpdatedAt int `json:"updatedAt"`
+}
+
+type MicrofrontendGroupMembershipsResponseAPI struct {
+ ID string `json:"id"`
+ Microfrontends MicrofrontendGroupMembershipResponseAPI `json:"microfrontends"`
+}
+
+func (c *Client) GetMicrofrontendGroupMembership(ctx context.Context, TeamID string, GroupID string, ProjectID string) (r MicrofrontendGroupMembership, err error) {
+ tflog.Info(ctx, "getting microfrontend group", map[string]interface{}{
+ "project_id": ProjectID,
+ "group_id": GroupID,
+ "team_id": c.teamID(TeamID),
+ })
+ group, err := c.GetMicrofrontendGroup(ctx, GroupID, c.teamID(TeamID))
+ if err != nil {
+ return r, err
+ }
+ tflog.Info(ctx, "getting microfrontend group membership", map[string]interface{}{
+ "project_id": ProjectID,
+ "group": group,
+ })
+ return group.Projects[ProjectID], nil
+}
+
+func (c *Client) AddOrUpdateMicrofrontendGroupMembership(ctx context.Context, request MicrofrontendGroupMembership) (r MicrofrontendGroupMembership, err error) {
+ tflog.Info(ctx, "adding / updating microfrontend project to group", map[string]interface{}{
+ "is_default_app": request.IsDefaultApp,
+ "project_id": request.ProjectID,
+ "group_id": request.MicrofrontendGroupID,
+ })
+ p, err := c.PatchMicrofrontendGroupMembership(ctx, MicrofrontendGroupMembership{
+ ProjectID: request.ProjectID,
+ TeamID: c.teamID(request.TeamID),
+ Enabled: true,
+ IsDefaultApp: request.IsDefaultApp,
+ DefaultRoute: request.DefaultRoute,
+ RouteObservabilityToThisProject: request.RouteObservabilityToThisProject,
+ MicrofrontendGroupID: request.MicrofrontendGroupID,
+ })
+ if err != nil {
+ return r, err
+ }
+ return p, nil
+}
+
+func (c *Client) RemoveMicrofrontendGroupMembership(ctx context.Context, request MicrofrontendGroupMembership) (r MicrofrontendGroupMembership, err error) {
+ tflog.Info(ctx, "removing microfrontend project from group", map[string]interface{}{
+ "project_id": request.ProjectID,
+ "group_id": request.MicrofrontendGroupID,
+ "team_id": c.teamID(request.TeamID),
+ })
+ p, err := c.PatchMicrofrontendGroupMembership(ctx, MicrofrontendGroupMembership{
+ ProjectID: request.ProjectID,
+ TeamID: c.teamID(request.TeamID),
+ Enabled: false,
+ MicrofrontendGroupID: request.MicrofrontendGroupID,
+ })
+ if err != nil {
+ return r, err
+ }
+ return p, nil
+}
+
+func (c *Client) PatchMicrofrontendGroupMembership(ctx context.Context, request MicrofrontendGroupMembership) (r MicrofrontendGroupMembership, err error) {
+ url := fmt.Sprintf("%s/projects/%s/microfrontends", c.baseURL, request.ProjectID)
+ payload := string(mustMarshal(MicrofrontendGroupMembership{
+ IsDefaultApp: request.IsDefaultApp,
+ DefaultRoute: request.DefaultRoute,
+ RouteObservabilityToThisProject: request.RouteObservabilityToThisProject,
+ ProjectID: request.ProjectID,
+ Enabled: request.Enabled,
+ MicrofrontendGroupID: request.MicrofrontendGroupID,
+ }))
+ if !request.Enabled {
+ payload = string(mustMarshal(struct {
+ ProjectID string `json:"projectId"`
+ Enabled bool `json:"enabled"`
+ }{
+ ProjectID: request.ProjectID,
+ Enabled: request.Enabled,
+ }))
+ }
+ if c.teamID(request.TeamID) != "" {
+ url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID))
+ }
+
+ tflog.Info(ctx, "updating microfrontend group membership", map[string]interface{}{
+ "url": url,
+ "payload": payload,
+ })
+ apiResponse := MicrofrontendGroupMembershipsResponseAPI{}
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "PATCH",
+ url: url,
+ body: payload,
+ }, &apiResponse)
+ if err != nil {
+ return r, err
+ }
+ return MicrofrontendGroupMembership{
+ MicrofrontendGroupID: request.MicrofrontendGroupID,
+ ProjectID: request.ProjectID,
+ TeamID: c.teamID(request.TeamID),
+ Enabled: apiResponse.Microfrontends.Enabled,
+ IsDefaultApp: apiResponse.Microfrontends.IsDefaultApp,
+ DefaultRoute: apiResponse.Microfrontends.DefaultRoute,
+ RouteObservabilityToThisProject: apiResponse.Microfrontends.RouteObservabilityToThisProject,
+ }, nil
+}
diff --git a/docs/data-sources/microfrontend_group.md b/docs/data-sources/microfrontend_group.md
new file mode 100644
index 00000000..8089e51c
--- /dev/null
+++ b/docs/data-sources/microfrontend_group.md
@@ -0,0 +1,47 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_microfrontend_group Data Source - terraform-provider-vercel"
+subcategory: ""
+description: |-
+ Provides information about an existing Microfrontend Group.
+ A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+---
+
+# vercel_microfrontend_group (Data Source)
+
+Provides information about an existing Microfrontend Group.
+
+A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+
+## Example Usage
+
+```terraform
+data "vercel_microfrontend_group" "example" {
+ id = "mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) A unique identifier for the group of microfrontends. Example: mfe_12HKQaOmR5t5Uy6vdcQsNIiZgHGB
+
+### Optional
+
+- `team_id` (String) The team ID to add the project to. Required when configuring a team resource if a default team has not been set in the provider.
+
+### Read-Only
+
+- `default_app` (Attributes) The default app for the project. Used as the entry point for the microfrontend. (see [below for nested schema](#nestedatt--default_app))
+- `name` (String) A human readable name for the microfrontends group.
+- `slug` (String) A slugified version of the name.
+
+
+### Nested Schema for `default_app`
+
+Read-Only:
+
+- `default_route` (String) The default route for the project. Used for the screenshot of deployments.
+- `project_id` (String) The ID of the project.
diff --git a/docs/data-sources/microfrontend_group_membership.md b/docs/data-sources/microfrontend_group_membership.md
new file mode 100644
index 00000000..e2882086
--- /dev/null
+++ b/docs/data-sources/microfrontend_group_membership.md
@@ -0,0 +1,40 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_microfrontend_group_membership Data Source - terraform-provider-vercel"
+subcategory: ""
+description: |-
+ Provides information about an existing Microfrontend Group Membership.
+ A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+---
+
+# vercel_microfrontend_group_membership (Data Source)
+
+Provides information about an existing Microfrontend Group Membership.
+
+A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+
+## Example Usage
+
+```terraform
+data "vercel_microfrontend_group_membership" "example" {
+ project_id = "prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ microfrontend_group_id = "mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `microfrontend_group_id` (String) The ID of the microfrontend group.
+- `project_id` (String) The ID of the project.
+
+### Optional
+
+- `team_id` (String) The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.
+
+### Read-Only
+
+- `default_route` (String) The default route for the project. Used for the screenshot of deployments.
+- `route_observability_to_this_project` (Boolean) Whether the project is route observability for this project. If dalse, the project will be route observability for all projects to the default project.
diff --git a/docs/resources/microfrontend_group.md b/docs/resources/microfrontend_group.md
new file mode 100644
index 00000000..d63ecad8
--- /dev/null
+++ b/docs/resources/microfrontend_group.md
@@ -0,0 +1,84 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_microfrontend_group Resource - terraform-provider-vercel"
+subcategory: ""
+description: |-
+ Provides a Microfrontend Group resource.
+ A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+---
+
+# vercel_microfrontend_group (Resource)
+
+Provides a Microfrontend Group resource.
+
+A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+
+## Example Usage
+
+```terraform
+data "vercel_project" "parent_mfe_project" {
+ name = "my parent project"
+}
+
+data "vercel_project" "child_mfe_project" {
+ name = "my child project"
+}
+
+resource "vercel_microfrontend_group" "example_mfe_group" {
+ name = "my mfe"
+ default_app = vercel_project.parent_mfe_project.id
+}
+
+resource "vercel_microfrontend_group_membership" "parent_mfe_project_mfe_membership" {
+ project_id = vercel_project.parent_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+
+resource "vercel_microfrontend_group_membership" "child_mfe_project_mfe_membership" {
+ project_id = vercel_project.child_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+```
+
+
+## Schema
+
+### Required
+
+- `default_app` (Attributes) The default app for the project. Used as the entry point for the microfrontend. (see [below for nested schema](#nestedatt--default_app))
+- `name` (String) A human readable name for the microfrontends group.
+
+### Optional
+
+- `team_id` (String) The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.
+
+### Read-Only
+
+- `id` (String) A unique identifier for the group of microfrontends. Example: mfe_12HKQaOmR5t5Uy6vdcQsNIiZgHGB
+- `slug` (String) A slugified version of the name.
+
+
+### Nested Schema for `default_app`
+
+Required:
+
+- `project_id` (String) The ID of the project.
+
+Optional:
+
+- `default_route` (String) The default route for the project. Used for the screenshot of deployments.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# If importing into a personal account, or with a team configured on the provider, simply use the record id.
+# - the microfrontend ID can be taken from the microfrontend settings page
+terraform import vercel_microfrontend_group.example mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and microfrontend_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - the microfrontend ID can be taken from the microfrontend settings page
+terraform import vercel_microfrontend_group.example team_xxxxxxxxxxxxxxxxxxxxxxxx/mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+```
diff --git a/docs/resources/microfrontend_group_membership.md b/docs/resources/microfrontend_group_membership.md
new file mode 100644
index 00000000..db79a184
--- /dev/null
+++ b/docs/resources/microfrontend_group_membership.md
@@ -0,0 +1,72 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_microfrontend_group_membership Resource - terraform-provider-vercel"
+subcategory: ""
+description: |-
+ Provides a Microfrontend Group Membership resource.
+ A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+---
+
+# vercel_microfrontend_group_membership (Resource)
+
+Provides a Microfrontend Group Membership resource.
+
+A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+
+## Example Usage
+
+```terraform
+data "vercel_project" "parent_mfe_project" {
+ name = "my parent project"
+}
+
+data "vercel_project" "child_mfe_project" {
+ name = "my child project"
+}
+
+resource "vercel_microfrontend_group" "example_mfe_group" {
+ name = "my mfe"
+ default_app = vercel_project.parent_mfe_project.id
+}
+
+resource "vercel_microfrontend_group_membership" "parent_mfe_project_mfe_membership" {
+ project_id = vercel_project.parent_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+
+resource "vercel_microfrontend_group_membership" "child_mfe_project_mfe_membership" {
+ project_id = vercel_project.child_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+```
+
+
+## Schema
+
+### Required
+
+- `microfrontend_group_id` (String) The ID of the microfrontend group.
+- `project_id` (String) The ID of the project.
+
+### Optional
+
+- `default_route` (String) The default route for the project. Used for the screenshot of deployments.
+- `route_observability_to_this_project` (Boolean) Whether the project is route observability for this project. If dalse, the project will be route observability for all projects to the default project.
+- `team_id` (String) The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# If importing into a personal account, or with a team configured on the provider, simply use the record id.
+# - the microfrontend ID can be taken from the microfrontend settings page
+# - the project ID can be taken from the project settings page
+terraform import vercel_microfrontend_group_membership.example mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/pid_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and microfrontend_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - the microfrontend ID can be taken from the microfrontend settings page
+# - the project ID can be taken from the project settings page
+terraform import vercel_microfrontend_group_membership.example team_xxxxxxxxxxxxxxxxxxxxxxxx/mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/pid_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+```
diff --git a/examples/data-sources/vercel_microfrontend_group/data-source.tf b/examples/data-sources/vercel_microfrontend_group/data-source.tf
new file mode 100644
index 00000000..c0e1275a
--- /dev/null
+++ b/examples/data-sources/vercel_microfrontend_group/data-source.tf
@@ -0,0 +1,4 @@
+
+data "vercel_microfrontend_group" "example" {
+ id = "mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
diff --git a/examples/data-sources/vercel_microfrontend_group_membership/data-source.tf b/examples/data-sources/vercel_microfrontend_group_membership/data-source.tf
new file mode 100644
index 00000000..f5371ef8
--- /dev/null
+++ b/examples/data-sources/vercel_microfrontend_group_membership/data-source.tf
@@ -0,0 +1,5 @@
+
+data "vercel_microfrontend_group_membership" "example" {
+ project_id = "prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ microfrontend_group_id = "mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
diff --git a/examples/resources/vercel_microfrontend_group/import.sh b/examples/resources/vercel_microfrontend_group/import.sh
new file mode 100644
index 00000000..1fc0574e
--- /dev/null
+++ b/examples/resources/vercel_microfrontend_group/import.sh
@@ -0,0 +1,9 @@
+# If importing into a personal account, or with a team configured on the provider, simply use the record id.
+# - the microfrontend ID can be taken from the microfrontend settings page
+terraform import vercel_microfrontend_group.example mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and microfrontend_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - the microfrontend ID can be taken from the microfrontend settings page
+terraform import vercel_microfrontend_group.example team_xxxxxxxxxxxxxxxxxxxxxxxx/mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
diff --git a/examples/resources/vercel_microfrontend_group/resource.tf b/examples/resources/vercel_microfrontend_group/resource.tf
new file mode 100644
index 00000000..805709ff
--- /dev/null
+++ b/examples/resources/vercel_microfrontend_group/resource.tf
@@ -0,0 +1,22 @@
+data "vercel_project" "parent_mfe_project" {
+ name = "my parent project"
+}
+
+data "vercel_project" "child_mfe_project" {
+ name = "my child project"
+}
+
+resource "vercel_microfrontend_group" "example_mfe_group" {
+ name = "my mfe"
+ default_app = vercel_project.parent_mfe_project.id
+}
+
+resource "vercel_microfrontend_group_membership" "parent_mfe_project_mfe_membership" {
+ project_id = vercel_project.parent_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+
+resource "vercel_microfrontend_group_membership" "child_mfe_project_mfe_membership" {
+ project_id = vercel_project.child_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
diff --git a/examples/resources/vercel_microfrontend_group_membership/import.sh b/examples/resources/vercel_microfrontend_group_membership/import.sh
new file mode 100644
index 00000000..04ddbf93
--- /dev/null
+++ b/examples/resources/vercel_microfrontend_group_membership/import.sh
@@ -0,0 +1,11 @@
+# If importing into a personal account, or with a team configured on the provider, simply use the record id.
+# - the microfrontend ID can be taken from the microfrontend settings page
+# - the project ID can be taken from the project settings page
+terraform import vercel_microfrontend_group_membership.example mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/pid_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and microfrontend_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - the microfrontend ID can be taken from the microfrontend settings page
+# - the project ID can be taken from the project settings page
+terraform import vercel_microfrontend_group_membership.example team_xxxxxxxxxxxxxxxxxxxxxxxx/mfe_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/pid_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
diff --git a/examples/resources/vercel_microfrontend_group_membership/resource.tf b/examples/resources/vercel_microfrontend_group_membership/resource.tf
new file mode 100644
index 00000000..805709ff
--- /dev/null
+++ b/examples/resources/vercel_microfrontend_group_membership/resource.tf
@@ -0,0 +1,22 @@
+data "vercel_project" "parent_mfe_project" {
+ name = "my parent project"
+}
+
+data "vercel_project" "child_mfe_project" {
+ name = "my child project"
+}
+
+resource "vercel_microfrontend_group" "example_mfe_group" {
+ name = "my mfe"
+ default_app = vercel_project.parent_mfe_project.id
+}
+
+resource "vercel_microfrontend_group_membership" "parent_mfe_project_mfe_membership" {
+ project_id = vercel_project.parent_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
+
+resource "vercel_microfrontend_group_membership" "child_mfe_project_mfe_membership" {
+ project_id = vercel_project.child_mfe_project.id
+ microfrontend_group_id = vercel_microfrontend_group.example_mfe_group.id
+}
diff --git a/vercel/data_source_microfrontend_group.go b/vercel/data_source_microfrontend_group.go
new file mode 100644
index 00000000..788fe96a
--- /dev/null
+++ b/vercel/data_source_microfrontend_group.go
@@ -0,0 +1,129 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/vercel/terraform-provider-vercel/v2/client"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = µfrontendGroupDataSource{}
+ _ datasource.DataSourceWithConfigure = µfrontendGroupDataSource{}
+)
+
+func newMicrofrontendGroupDataSource() datasource.DataSource {
+ return µfrontendGroupDataSource{}
+}
+
+type microfrontendGroupDataSource struct {
+ client *client.Client
+}
+
+func (d *microfrontendGroupDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_microfrontend_group"
+}
+
+func (d *microfrontendGroupDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*client.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ d.client = client
+}
+
+// Schema returns the schema information for an microfrontendGroup data source
+func (r *microfrontendGroupDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: `
+Provides information about an existing Microfrontend Group.
+
+A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+`,
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Description: "A unique identifier for the group of microfrontends. Example: mfe_12HKQaOmR5t5Uy6vdcQsNIiZgHGB",
+ Required: true,
+ },
+ "name": schema.StringAttribute{
+ Description: "A human readable name for the microfrontends group.",
+ Computed: true,
+ },
+ "slug": schema.StringAttribute{
+ Description: "A slugified version of the name.",
+ Computed: true,
+ },
+ "team_id": schema.StringAttribute{
+ Description: "The team ID to add the project to. Required when configuring a team resource if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ },
+ "default_app": schema.SingleNestedAttribute{
+ Description: "The default app for the project. Used as the entry point for the microfrontend.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "project_id": schema.StringAttribute{
+ Description: "The ID of the project.",
+ Computed: true,
+ },
+ "default_route": schema.StringAttribute{
+ Description: "The default route for the project. Used for the screenshot of deployments.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ }
+}
+
+func (d *microfrontendGroupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ tflog.Info(ctx, "Reading microfrontend group")
+ var config MicrofrontendGroup
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := d.client.GetMicrofrontendGroup(ctx, config.ID.ValueString(), config.TeamID.ValueString())
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading microfrontend group",
+ fmt.Sprintf("Could not get microfrontend group %s %s, unexpected error: %s",
+ config.TeamID.ValueString(),
+ config.ID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroup(out)
+ tflog.Info(ctx, "read microfrontend group", map[string]interface{}{
+ "result": result,
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/data_source_microfrontend_group_membership.go b/vercel/data_source_microfrontend_group_membership.go
new file mode 100644
index 00000000..2eca3831
--- /dev/null
+++ b/vercel/data_source_microfrontend_group_membership.go
@@ -0,0 +1,123 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/vercel/terraform-provider-vercel/v2/client"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = µfrontendGroupMembershipDataSource{}
+ _ datasource.DataSourceWithConfigure = µfrontendGroupMembershipDataSource{}
+)
+
+func newMicrofrontendGroupMembershipDataSource() datasource.DataSource {
+ return µfrontendGroupMembershipDataSource{}
+}
+
+type microfrontendGroupMembershipDataSource struct {
+ client *client.Client
+}
+
+func (d *microfrontendGroupMembershipDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_microfrontend_group_membership"
+}
+
+func (d *microfrontendGroupMembershipDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*client.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ d.client = client
+}
+
+// Schema returns the schema information for an microfrontendGroupMembership data source
+func (r *microfrontendGroupMembershipDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: `
+Provides information about an existing Microfrontend Group Membership.
+
+A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+`,
+ Attributes: map[string]schema.Attribute{
+ "project_id": schema.StringAttribute{
+ Description: "The ID of the project.",
+ Required: true,
+ },
+ "microfrontend_group_id": schema.StringAttribute{
+ Description: "The ID of the microfrontend group.",
+ Required: true,
+ },
+ "team_id": schema.StringAttribute{
+ Description: "The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ },
+ "default_route": schema.StringAttribute{
+ Description: "The default route for the project. Used for the screenshot of deployments.",
+ Computed: true,
+ },
+ "route_observability_to_this_project": schema.BoolAttribute{
+ Description: "Whether the project is route observability for this project. If dalse, the project will be route observability for all projects to the default project.",
+ Computed: true,
+ },
+ },
+ }
+}
+
+func (d *microfrontendGroupMembershipDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var config MicrofrontendGroupMembership
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := d.client.GetMicrofrontendGroupMembership(ctx, config.TeamID.ValueString(),
+ config.MicrofrontendGroupID.ValueString(),
+ config.ProjectID.ValueString(),
+ )
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading microfrontend group membership",
+ fmt.Sprintf("Could not get microfrontend group %s %s, unexpected error: %s",
+ config.TeamID.ValueString(),
+ config.MicrofrontendGroupID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroupMembership(out)
+ tflog.Info(ctx, "read microfrontend group membership", map[string]interface{}{
+ "team_id": result.TeamID.ValueString(),
+ "group_id": result.MicrofrontendGroupID.ValueString(),
+ "project_id": result.ProjectID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/data_source_microfrontend_group_test.go b/vercel/data_source_microfrontend_group_test.go
new file mode 100644
index 00000000..3c0f75d5
--- /dev/null
+++ b/vercel/data_source_microfrontend_group_test.go
@@ -0,0 +1,57 @@
+package vercel_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAcc_MicrofrontendGroupDataSource(t *testing.T) {
+ name := acctest.RandString(16)
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ resource "vercel_project" "test_project_1" {
+ name = "test-acc-project-%[1]s"
+ %[2]s
+ }
+ resource "vercel_project" "test_project_2" {
+ name = "test-acc-project-2-%[1]s"
+ %[2]s
+ }
+ resource "vercel_microfrontend_group" "test_group" {
+ name = "test-acc-microfrontend-group-%[1]s"
+ default_app = {
+ project_id = vercel_project.test_project_1.id
+ }
+ %[2]s
+ }
+ resource "vercel_microfrontend_group_membership" "test_child" {
+ project_id = vercel_project.test_project_2.id
+ microfrontend_group_id = vercel_microfrontend_group.test_group.id
+ %[2]s
+ }
+ data "vercel_microfrontend_group" "test_group" {
+ id = vercel_microfrontend_group.test_group.id
+ %[2]s
+ }
+ data "vercel_microfrontend_group_membership" "test_child" {
+ microfrontend_group_id = vercel_microfrontend_group.test_group.id
+ project_id = vercel_project.test_project_2.id
+ %[2]s
+ }
+ `, name, teamIDConfig()),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("data.vercel_microfrontend_group.test_group", "name", "test-acc-microfrontend-group-"+name),
+ resource.TestCheckResourceAttrSet("data.vercel_microfrontend_group.test_group", "default_app.project_id"),
+ resource.TestCheckResourceAttr("data.vercel_microfrontend_group_membership.test_child", "%", "5"),
+ ),
+ },
+ },
+ })
+}
diff --git a/vercel/provider.go b/vercel/provider.go
index eb0ef20f..41d7c045 100644
--- a/vercel/provider.go
+++ b/vercel/provider.go
@@ -75,6 +75,8 @@ func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource
newTeamConfigResource,
newTeamMemberResource,
newWebhookResource,
+ newMicrofrontendGroupResource,
+ newMicrofrontendGroupMembershipResource,
}
}
@@ -101,6 +103,8 @@ func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.Data
newSharedEnvironmentVariableDataSource,
newTeamConfigDataSource,
newTeamMemberDataSource,
+ newMicrofrontendGroupDataSource,
+ newMicrofrontendGroupMembershipDataSource,
}
}
diff --git a/vercel/provider_test.go b/vercel/provider_test.go
index e4c97127..40fe1f89 100644
--- a/vercel/provider_test.go
+++ b/vercel/provider_test.go
@@ -23,6 +23,7 @@ func mustHaveEnv(t *testing.T, name string) {
func testAccPreCheck(t *testing.T) {
mustHaveEnv(t, "VERCEL_API_TOKEN")
+ mustHaveEnv(t, "VERCEL_TERRAFORM_TESTING_TEAM")
mustHaveEnv(t, "VERCEL_TERRAFORM_TESTING_GITHUB_REPO")
mustHaveEnv(t, "VERCEL_TERRAFORM_TESTING_GITLAB_REPO")
mustHaveEnv(t, "VERCEL_TERRAFORM_TESTING_BITBUCKET_REPO")
diff --git a/vercel/resource_microfrontend_group.go b/vercel/resource_microfrontend_group.go
new file mode 100644
index 00000000..1917beee
--- /dev/null
+++ b/vercel/resource_microfrontend_group.go
@@ -0,0 +1,403 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/vercel/terraform-provider-vercel/v2/client"
+)
+
+var (
+ _ resource.Resource = µfrontendGroupResource{}
+ _ resource.ResourceWithConfigure = µfrontendGroupResource{}
+ _ resource.ResourceWithImportState = µfrontendGroupResource{}
+)
+
+func newMicrofrontendGroupResource() resource.Resource {
+ return µfrontendGroupResource{}
+}
+
+type microfrontendGroupResource struct {
+ client *client.Client
+}
+
+func (r *microfrontendGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_microfrontend_group"
+}
+
+func (r *microfrontendGroupResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*client.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Resource Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ r.client = client
+}
+
+// Schema returns the schema information for a microfrontendGroup resource.
+func (r *microfrontendGroupResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: `
+Provides a Microfrontend Group resource.
+
+A Microfrontend Group is a definition of a microfrontend belonging to a Vercel Team.
+`,
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ Description: "A human readable name for the microfrontends group.",
+ Required: true,
+ },
+ "id": schema.StringAttribute{
+ Description: "A unique identifier for the group of microfrontends. Example: mfe_12HKQaOmR5t5Uy6vdcQsNIiZgHGB",
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
+ },
+ "slug": schema.StringAttribute{
+ Description: "A slugified version of the name.",
+ Computed: true,
+ },
+ "team_id": schema.StringAttribute{
+ Description: "The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()},
+ },
+ "default_app": schema.SingleNestedAttribute{
+ Description: "The default app for the project. Used as the entry point for the microfrontend.",
+ Required: true,
+ Attributes: getMicrofrontendGroupMembershipSchema(true),
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplaceIf(func(ctx context.Context, req planmodifier.ObjectRequest, resp *objectplanmodifier.RequiresReplaceIfFuncResponse) {
+ oldDefaultApp, okOld := req.ConfigValue.ToObjectValue(ctx)
+ newDefaultApp, okNew := req.PlanValue.ToObjectValue(ctx)
+ if okOld.HasError() || okNew.HasError() {
+ return
+ }
+ oldValue := oldDefaultApp.Attributes()["project_id"]
+ newValue := newDefaultApp.Attributes()["project_id"]
+
+ if oldValue != newValue {
+ resp.RequiresReplace = true
+ }
+ }, "The default app for the group has changed.", "The default app for the group has changed."),
+ },
+ },
+ },
+ }
+}
+
+type MicrofrontendGroupDefaultApp struct {
+ ProjectID types.String `tfsdk:"project_id"`
+ DefaultRoute types.String `tfsdk:"default_route"`
+}
+
+type MicrofrontendGroup struct {
+ TeamID types.String `tfsdk:"team_id"`
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Slug types.String `tfsdk:"slug"`
+ DefaultApp *MicrofrontendGroupDefaultApp `tfsdk:"default_app"`
+}
+
+func convertResponseToMicrofrontendGroup(group client.MicrofrontendGroup) MicrofrontendGroup {
+ return MicrofrontendGroup{
+ ID: types.StringValue(group.ID),
+ Name: types.StringValue(group.Name),
+ Slug: types.StringValue(group.Slug),
+ TeamID: types.StringValue(group.TeamID),
+ DefaultApp: &MicrofrontendGroupDefaultApp{
+ ProjectID: types.StringValue(group.DefaultApp.ProjectID),
+ DefaultRoute: types.StringValue(group.DefaultApp.DefaultRoute),
+ },
+ }
+}
+
+func (r *microfrontendGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan MicrofrontendGroup
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting microfrontend group plan",
+ "Error getting microfrontend group plan",
+ )
+ return
+ }
+
+ tflog.Info(ctx, "creating microfrontend group", map[string]interface{}{
+ "team_id": plan.TeamID.ValueString(),
+ "name": plan.Name.ValueString(),
+ })
+
+ out, err := r.client.CreateMicrofrontendGroup(ctx, plan.TeamID.ValueString(), plan.Name.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating microfrontend group",
+ "Could not create microfrontend group, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "creating default group membership", map[string]interface{}{
+ "team_id": plan.TeamID.ValueString(),
+ "name": plan.Name.ValueString(),
+ "default_app": plan.DefaultApp.ProjectID.ValueString(),
+ })
+
+ default_app, err := r.client.AddOrUpdateMicrofrontendGroupMembership(ctx, client.MicrofrontendGroupMembership{
+ ProjectID: plan.DefaultApp.ProjectID.ValueString(),
+ MicrofrontendGroupID: out.ID,
+ TeamID: plan.TeamID.ValueString(),
+ DefaultRoute: plan.DefaultApp.DefaultRoute.ValueString(),
+ IsDefaultApp: true,
+ })
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating microfrontend default app group membership",
+ "Could not create microfrontend default app group membership, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ group := client.MicrofrontendGroup{
+ ID: out.ID,
+ Name: out.Name,
+ Slug: out.Slug,
+ TeamID: out.TeamID,
+ DefaultApp: client.MicrofrontendGroupMembership{
+ ProjectID: default_app.ProjectID,
+ TeamID: default_app.TeamID,
+ DefaultRoute: default_app.DefaultRoute,
+ RouteObservabilityToThisProject: default_app.RouteObservabilityToThisProject,
+ MicrofrontendGroupID: out.ID,
+ IsDefaultApp: default_app.IsDefaultApp,
+ },
+ Projects: out.Projects,
+ }
+
+ result := convertResponseToMicrofrontendGroup(group)
+ tflog.Info(ctx, "created microfrontend group", map[string]interface{}{
+ "team_id": result.TeamID.ValueString(),
+ "group_id": result.ID.ValueString(),
+ "slug": result.Slug.ValueString(),
+ "name": result.Name.ValueString(),
+ "default_app": result.DefaultApp.ProjectID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state MicrofrontendGroup
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := r.client.GetMicrofrontendGroup(ctx, state.ID.ValueString(), state.TeamID.ValueString())
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading microfrontend group",
+ fmt.Sprintf("Could not get microfrontend group %s %s, unexpected error: %s",
+ state.TeamID.ValueString(),
+ state.ID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroup(out)
+ tflog.Info(ctx, "read microfrontend group", map[string]interface{}{
+ "defaultApp": result.DefaultApp.ProjectID.ValueString(),
+ "team_id": result.TeamID.ValueString(),
+ "group_id": result.ID.ValueString(),
+ "slug": result.Slug.ValueString(),
+ "name": result.Name.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan MicrofrontendGroup
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting microfrontend group plan",
+ "Error getting microfrontend group plan",
+ )
+ return
+ }
+
+ var state MicrofrontendGroup
+ diags = req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := r.client.UpdateMicrofrontendGroup(ctx, client.MicrofrontendGroup{
+ ID: state.ID.ValueString(),
+ Name: plan.Name.ValueString(),
+ TeamID: state.TeamID.ValueString(),
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating microfrontend group",
+ fmt.Sprintf(
+ "Could not update microfrontend group %s %s, unexpected error: %s",
+ state.TeamID.ValueString(),
+ state.ID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "updated microfrontend group", map[string]interface{}{
+ "team_id": out.TeamID,
+ "group_id": out.ID,
+ "name": out.Name,
+ "slug": out.Slug,
+ })
+
+ result := convertResponseToMicrofrontendGroup(out)
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state MicrofrontendGroup
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ tflog.Info(ctx, "deleting microfrontend default app group membership", map[string]interface{}{
+ "group_id": state.ID.ValueString(),
+ "project_id": state.DefaultApp.ProjectID.ValueString(),
+ "team_id": state.TeamID.ValueString(),
+ })
+
+ _, err := r.client.RemoveMicrofrontendGroupMembership(ctx, client.MicrofrontendGroupMembership{
+ MicrofrontendGroupID: state.ID.ValueString(),
+ TeamID: state.TeamID.ValueString(),
+ ProjectID: state.DefaultApp.ProjectID.ValueString(),
+ })
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting microfrontend default app group membership",
+ fmt.Sprintf(
+ "Could not delete microfrontend default app group membership %s %s, unexpected error: %s",
+ state.ID.ValueString(),
+ state.DefaultApp.ProjectID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "deleting microfrontend group", map[string]interface{}{
+ "group_id": state.ID.ValueString(),
+ })
+
+ _, err = r.client.DeleteMicrofrontendGroup(ctx, client.MicrofrontendGroup{
+ ID: state.ID.ValueString(),
+ TeamID: state.TeamID.ValueString(),
+ Slug: state.Slug.ValueString(),
+ Name: state.Name.ValueString(),
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting microfrontend group",
+ fmt.Sprintf(
+ "Could not delete microfrontend group %s, unexpected error: %s",
+ state.ID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+ tflog.Info(ctx, "deleted microfrontendGroup", map[string]any{
+ "group_id": state.ID.ValueString(),
+ })
+}
+
+func (r *microfrontendGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ teamID, microfrontendID, ok := splitInto1Or2(req.ID)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Error importing Microfrontend Group",
+ fmt.Sprintf("Invalid id '%s' specified. should be in format \"team_id/microfrontend_id\" or \"microfrontend_id\"", req.ID),
+ )
+ }
+ out, err := r.client.GetMicrofrontendGroup(ctx, microfrontendID, teamID)
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error importing microfrontend group",
+ fmt.Sprintf("Could not import microfrontend group %s %s, unexpected error: %s",
+ teamID,
+ microfrontendID,
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroup(out)
+ tflog.Info(ctx, "import microfrontend group", map[string]interface{}{
+ "defaultApp": result.DefaultApp.ProjectID.ValueString(),
+ "team_id": result.TeamID.ValueString(),
+ "group_id": result.ID.ValueString(),
+ "slug": result.Slug.ValueString(),
+ "name": result.Name.ValueString(),
+ })
+
+ diags := resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/resource_microfrontend_group_membership.go b/vercel/resource_microfrontend_group_membership.go
new file mode 100644
index 00000000..0f403374
--- /dev/null
+++ b/vercel/resource_microfrontend_group_membership.go
@@ -0,0 +1,350 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/vercel/terraform-provider-vercel/v2/client"
+)
+
+var (
+ _ resource.Resource = µfrontendGroupMembershipResource{}
+ _ resource.ResourceWithConfigure = µfrontendGroupMembershipResource{}
+ _ resource.ResourceWithImportState = µfrontendGroupMembershipResource{}
+)
+
+func newMicrofrontendGroupMembershipResource() resource.Resource {
+ return µfrontendGroupMembershipResource{}
+}
+
+type microfrontendGroupMembershipResource struct {
+ client *client.Client
+}
+
+func (r *microfrontendGroupMembershipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_microfrontend_group_membership"
+}
+
+func (r *microfrontendGroupMembershipResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*client.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Resource Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ r.client = client
+}
+
+func getMicrofrontendGroupMembershipSchema(isDefaultApp bool) map[string]schema.Attribute {
+ res := map[string]schema.Attribute{}
+
+ res["project_id"] = schema.StringAttribute{
+ Description: "The ID of the project.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
+ }
+ res["default_route"] = schema.StringAttribute{
+ Description: "The default route for the project. Used for the screenshot of deployments.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
+ }
+
+ if !isDefaultApp {
+ res["microfrontend_group_id"] = schema.StringAttribute{
+ Description: "The ID of the microfrontend group.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
+ }
+ res["team_id"] = schema.StringAttribute{
+ Description: "The team ID to add the microfrontend group to. Required when configuring a team resource if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()},
+ }
+ res["route_observability_to_this_project"] = schema.BoolAttribute{
+ Description: "Whether the project is route observability for this project. If dalse, the project will be route observability for all projects to the default project.",
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(true),
+ PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()},
+ }
+ }
+
+ return res
+}
+
+// Schema returns the schema information for a microfrontendGroupMembership resource.
+func (r *microfrontendGroupMembershipResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: `
+Provides a Microfrontend Group Membership resource.
+
+A Microfrontend Group Membership is a definition of a Vercel Project being a part of a Microfrontend Group.
+`,
+ Attributes: getMicrofrontendGroupMembershipSchema(false),
+ }
+}
+
+type MicrofrontendGroupMembership struct {
+ ProjectID types.String `tfsdk:"project_id"`
+ MicrofrontendGroupID types.String `tfsdk:"microfrontend_group_id"`
+ TeamID types.String `tfsdk:"team_id"`
+ DefaultRoute types.String `tfsdk:"default_route"`
+ RouteObservabilityToThisProject types.Bool `tfsdk:"route_observability_to_this_project"`
+}
+
+func convertResponseToMicrofrontendGroupMembership(membership client.MicrofrontendGroupMembership) MicrofrontendGroupMembership {
+ return MicrofrontendGroupMembership{
+ ProjectID: types.StringValue(membership.ProjectID),
+ MicrofrontendGroupID: types.StringValue(membership.MicrofrontendGroupID),
+ TeamID: types.StringValue(membership.TeamID),
+ DefaultRoute: types.StringValue(membership.DefaultRoute),
+ RouteObservabilityToThisProject: types.BoolValue(membership.RouteObservabilityToThisProject),
+ }
+}
+
+func (r *microfrontendGroupMembershipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan MicrofrontendGroupMembership
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting microfrontend group membership plan",
+ "Error getting microfrontend group membership plan",
+ )
+ return
+ }
+
+ tflog.Info(ctx, "creating microfrontend group membership", map[string]interface{}{
+ "project_id": plan.ProjectID.ValueString(),
+ "group_id": plan.MicrofrontendGroupID.ValueString(),
+ "plan": plan,
+ })
+
+ out, err := r.client.AddOrUpdateMicrofrontendGroupMembership(ctx, client.MicrofrontendGroupMembership{
+ ProjectID: plan.ProjectID.ValueString(),
+ MicrofrontendGroupID: plan.MicrofrontendGroupID.ValueString(),
+ DefaultRoute: plan.DefaultRoute.ValueString(),
+ RouteObservabilityToThisProject: plan.RouteObservabilityToThisProject.ValueBool(),
+ TeamID: plan.TeamID.ValueString(),
+ IsDefaultApp: false,
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating microfrontend group membership",
+ "Could not create microfrontend group, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroupMembership(out)
+ tflog.Info(ctx, "created microfrontend group membership", map[string]interface{}{
+ "project_id": result.ProjectID.ValueString(),
+ "group_id": result.MicrofrontendGroupID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupMembershipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state MicrofrontendGroupMembership
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ if state.ProjectID.ValueString() == "" || state.MicrofrontendGroupID.ValueString() == "" {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ out, err := r.client.GetMicrofrontendGroupMembership(ctx,
+ state.TeamID.ValueString(),
+ state.MicrofrontendGroupID.ValueString(),
+ state.ProjectID.ValueString(),
+ )
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading microfrontend group membership",
+ fmt.Sprintf("Could not get microfrontend group membership %s %s, unexpected error: %s",
+ state.ProjectID.ValueString(),
+ state.MicrofrontendGroupID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroupMembership(out)
+ tflog.Info(ctx, "read microfrontend group membership", map[string]interface{}{
+ "team_id": result.TeamID.ValueString(),
+ "group_id": result.MicrofrontendGroupID.ValueString(),
+ "project_id": result.ProjectID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupMembershipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan MicrofrontendGroupMembership
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting microfrontend group plan",
+ "Error getting microfrontend group plan",
+ )
+ return
+ }
+
+ var state MicrofrontendGroupMembership
+ diags = req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := r.client.AddOrUpdateMicrofrontendGroupMembership(ctx, client.MicrofrontendGroupMembership{
+ ProjectID: plan.ProjectID.ValueString(),
+ MicrofrontendGroupID: plan.MicrofrontendGroupID.ValueString(),
+ DefaultRoute: plan.DefaultRoute.ValueString(),
+ RouteObservabilityToThisProject: plan.RouteObservabilityToThisProject.ValueBool(),
+ TeamID: plan.TeamID.ValueString(),
+ IsDefaultApp: false,
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating microfrontend group membership",
+ fmt.Sprintf(
+ "Could not update microfrontend group membership %s %s %s, unexpected error: %s",
+ state.TeamID.ValueString(),
+ state.MicrofrontendGroupID.ValueString(),
+ state.ProjectID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "updated microfrontend group membership", map[string]interface{}{
+ "team_id": out.TeamID,
+ "microfrontend_group_id": out.MicrofrontendGroupID,
+ "project_id": out.ProjectID,
+ })
+
+ result := convertResponseToMicrofrontendGroupMembership(out)
+
+ diags = resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *microfrontendGroupMembershipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state MicrofrontendGroupMembership
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ tflog.Info(ctx, "deleting microfrontend group membership", map[string]interface{}{
+ "project_id": state.ProjectID.ValueString(),
+ "group_id": state.MicrofrontendGroupID.ValueString(),
+ "team_id": state.TeamID.ValueString(),
+ })
+
+ _, err := r.client.RemoveMicrofrontendGroupMembership(ctx, client.MicrofrontendGroupMembership{
+ MicrofrontendGroupID: state.MicrofrontendGroupID.ValueString(),
+ ProjectID: state.ProjectID.ValueString(),
+ TeamID: state.TeamID.ValueString(),
+ IsDefaultApp: false,
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting microfrontend group membership",
+ fmt.Sprintf(
+ "Could not delete microfrontend group membership %s %s, unexpected error: %s",
+ state.MicrofrontendGroupID.ValueString(),
+ state.ProjectID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+ tflog.Info(ctx, "deleted microfrontend group membership", map[string]any{
+ "group_id": state.MicrofrontendGroupID.ValueString(),
+ "project_id": state.ProjectID.ValueString(),
+ })
+}
+
+func (r *microfrontendGroupMembershipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ teamID, microfrontendID, projectID, ok := splitInto2Or3(req.ID)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Error importing Microfrontend Group Membership",
+ fmt.Sprintf("Invalid id '%s' specified. should be in format \"team_id/microfrontend_id/project_id\" or \"microfrontend_id/project_id\"", req.ID),
+ )
+ }
+ out, err := r.client.GetMicrofrontendGroupMembership(ctx, teamID, microfrontendID, projectID)
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error importing microfrontend group membership",
+ fmt.Sprintf("Could not import microfrontend group membership %s %s %s, unexpected error: %s",
+ teamID,
+ microfrontendID,
+ projectID,
+ err,
+ ),
+ )
+ return
+ }
+
+ result := convertResponseToMicrofrontendGroupMembership(out)
+ tflog.Info(ctx, "import microfrontend group", map[string]interface{}{
+ "project_id": result.ProjectID.ValueString(),
+ "group_id": result.MicrofrontendGroupID.ValueString(),
+ "team_id": result.TeamID.ValueString(),
+ })
+
+ diags := resp.State.Set(ctx, result)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/resource_microfrontend_group_test.go b/vercel/resource_microfrontend_group_test.go
new file mode 100644
index 00000000..55ebecd2
--- /dev/null
+++ b/vercel/resource_microfrontend_group_test.go
@@ -0,0 +1,92 @@
+package vercel_test
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func testCheckMicrofrontendGroupExists(teamID, n string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[n]
+ if !ok {
+ return fmt.Errorf("not found: %s", n)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("no ID is set")
+ }
+
+ _, err := testClient().GetMicrofrontendGroup(context.TODO(), rs.Primary.ID, teamID)
+ return err
+ }
+}
+
+func testCheckMicrofrontendGroupDeleted(n, teamID string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[n]
+ if !ok {
+ return fmt.Errorf("not found: %s", n)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("no ID is set")
+ }
+
+ _, err := testClient().GetMicrofrontendGroup(context.TODO(), rs.Primary.ID, teamID)
+ if err == nil {
+ return fmt.Errorf("expected not_found error, but got no error")
+ }
+ if !(err.Error() == "microfrontend group not found") {
+ return fmt.Errorf("Unexpected error checking for deleted microfrontend group: %s", err)
+ }
+
+ return nil
+ }
+}
+
+func TestAcc_MicrofrontendGroupResource(t *testing.T) {
+ name := acctest.RandString(16)
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ CheckDestroy: testCheckMicrofrontendGroupDeleted("vercel_microfrontend_group.test", testTeam()),
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ %[2]s
+ }
+ resource "vercel_project" "test-2" {
+ name = "test-acc-project-2-%[1]s"
+ %[2]s
+ }
+ resource "vercel_microfrontend_group" "test" {
+ name = "test-acc-microfrontend-group-%[1]s"
+ default_app = {
+ project_id = vercel_project.test.id
+ }
+ %[2]s
+ }
+ resource "vercel_microfrontend_group_membership" "test-2" {
+ project_id = vercel_project.test-2.id
+ microfrontend_group_id = vercel_microfrontend_group.test.id
+ %[2]s
+ }
+ `, name, teamIDConfig()),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ testCheckMicrofrontendGroupExists(testTeam(), "vercel_microfrontend_group.test"),
+ resource.TestCheckResourceAttr("vercel_microfrontend_group.test", "name", "test-acc-microfrontend-group-"+name),
+ resource.TestCheckResourceAttrSet("vercel_microfrontend_group.test", "id"),
+ resource.TestCheckResourceAttrSet("vercel_microfrontend_group.test", "default_app.project_id"),
+ resource.TestCheckResourceAttrSet("vercel_microfrontend_group_membership.test-2", "microfrontend_group_id"),
+ ),
+ },
+ },
+ })
+}