Skip to content

Commit 9524b60

Browse files
authored
feat(zero_trust_access_application): Add support for MCP & MCP_PORTAL (#6326)
1 parent 5892196 commit 9524b60

File tree

10 files changed

+142
-25
lines changed

10 files changed

+142
-25
lines changed

docs/resources/zero_trust_access_application.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ The header value will be interpreted as a json object similar to:
154154
- `tags` (Set of String) The tags you want assigned to an application. Tags are used to filter applications in the App Launcher dashboard.
155155
- `target_criteria` (Attributes List) (see [below for nested schema](#nestedatt--target_criteria))
156156
- `type` (String) The application type.
157-
Available values: "self_hosted", "saas", "ssh", "vnc", "app_launcher", "warp", "biso", "bookmark", "dash_sso", "infrastructure", "rdp".
157+
Available values: "self_hosted", "saas", "ssh", "vnc", "app_launcher", "warp", "biso", "bookmark", "dash_sso", "infrastructure", "rdp", "mcp", "mcp_portal".
158158
- `zone_id` (String) The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
159159

160160
### Read-Only
@@ -186,6 +186,7 @@ Optional:
186186
- `hostname` (String) The hostname of the destination. Matches a valid SNI served by an HTTPS origin.
187187
- `l4_protocol` (String) The L4 protocol of the destination. When omitted, both UDP and TCP traffic will match.
188188
Available values: "tcp", "udp".
189+
- `mcp_server_id` (String) A MCP server id configured in ai-controls. Access will secure the MCP server if accessed through a MCP portal.
189190
- `port_range` (String) The port range of the destination. Can be a single port or a range of ports. When omitted, all ports will match.
190191
- `type` (String) Available values: "public", "private".
191192
- `uri` (String) The URI of the destination. Public destinations' URIs can include a domain and path with [wildcards](https://developers.cloudflare.com/cloudflare-one/policies/access/app-paths/).

internal/customvalidator/required_when_other_attribute_is_one_of_validator.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package customvalidator
33
import (
44
"context"
55
"fmt"
6+
"strings"
7+
68
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
79
"github.com/hashicorp/terraform-plugin-framework/attr"
810
"github.com/hashicorp/terraform-plugin-framework/diag"
911
"github.com/hashicorp/terraform-plugin-framework/path"
1012
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
1214
"github.com/hashicorp/terraform-plugin-framework/types"
13-
"strings"
1415
)
1516

1617
func RequiredWhenOtherStringIsOneOf(pathExpr path.Expression, wantStrValues ...string) requiredWhenOtherAttributeIsOneOfValidator {
@@ -85,3 +86,7 @@ func (i requiredWhenOtherAttributeIsOneOfValidator) validateAny(ctx context.Cont
8586
func (i requiredWhenOtherAttributeIsOneOfValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, res *validator.ObjectResponse) {
8687
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
8788
}
89+
90+
func (i requiredWhenOtherAttributeIsOneOfValidator) ValidateString(ctx context.Context, req validator.StringRequest, res *validator.StringResponse) {
91+
i.validateAny(ctx, &req.Config, req.PathExpression, req.Path, req.ConfigValue, &res.Diagnostics)
92+
}

internal/services/zero_trust_access_application/data_source_schema.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func DataSourceSchema(ctx context.Context) schema.Schema {
136136
Computed: true,
137137
},
138138
"type": schema.StringAttribute{
139-
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\".",
139+
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\", \"mcp\", \"mcp_portal\".",
140140
Computed: true,
141141
Validators: []validator.String{
142142
stringvalidator.OneOfCaseInsensitive(
@@ -151,6 +151,8 @@ func DataSourceSchema(ctx context.Context) schema.Schema {
151151
"dash_sso",
152152
"infrastructure",
153153
"rdp",
154+
"mcp",
155+
"mcp_portal",
154156
),
155157
},
156158
},
@@ -251,7 +253,7 @@ func DataSourceSchema(ctx context.Context) schema.Schema {
251253
Description: `Available values: "public", "private".`,
252254
Computed: true,
253255
Validators: []validator.String{
254-
stringvalidator.OneOfCaseInsensitive("public", "private"),
256+
stringvalidator.OneOfCaseInsensitive("public", "private", "via_mcp_server_portal"),
255257
},
256258
},
257259
"uri": schema.StringAttribute{
@@ -281,6 +283,10 @@ func DataSourceSchema(ctx context.Context) schema.Schema {
281283
Description: "The VNET ID to match the destination. When omitted, all VNETs will match.",
282284
Computed: true,
283285
},
286+
"mcp_server_id": schema.StringAttribute{
287+
Description: "A MCP server id configured in ai-controls. Access will secure the MCP server if accessed through a MCP portal.",
288+
Optional: true,
289+
},
284290
},
285291
},
286292
},

internal/services/zero_trust_access_application/list_data_source_schema.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func ListDataSourceSchema(ctx context.Context) schema.Schema {
7070
Computed: true,
7171
},
7272
"type": schema.StringAttribute{
73-
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\".",
73+
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\", \"mcp\", \"mcp_portal\".",
7474
Computed: true,
7575
Validators: []validator.String{
7676
stringvalidator.OneOfCaseInsensitive(
@@ -85,6 +85,8 @@ func ListDataSourceSchema(ctx context.Context) schema.Schema {
8585
"dash_sso",
8686
"infrastructure",
8787
"rdp",
88+
"mcp",
89+
"mcp_portal",
8890
),
8991
},
9092
},
@@ -208,7 +210,7 @@ func ListDataSourceSchema(ctx context.Context) schema.Schema {
208210
Description: `Available values: "public", "private".`,
209211
Computed: true,
210212
Validators: []validator.String{
211-
stringvalidator.OneOfCaseInsensitive("public", "private"),
213+
stringvalidator.OneOfCaseInsensitive("public", "private", "via_mcp_server_portal"),
212214
},
213215
},
214216
"uri": schema.StringAttribute{
@@ -238,6 +240,10 @@ func ListDataSourceSchema(ctx context.Context) schema.Schema {
238240
Description: "The VNET ID to match the destination. When omitted, all VNETs will match.",
239241
Computed: true,
240242
},
243+
"mcp_server_id": schema.StringAttribute{
244+
Description: "A MCP server id configured in ai-controls. Access will secure the MCP server if accessed through a MCP portal.",
245+
Optional: true,
246+
},
241247
},
242248
},
243249
},

internal/services/zero_trust_access_application/migrations_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
)
1818

1919
// TestMigrateZeroTrustAccessApplication_Basic tests basic state schema migration within v5 provider
20-
// Note: This tests the state migration (v0->v1) within the same resource type,
20+
// Note: This tests the state migration (v0->v1) within the same resource type,
2121
// not the resource type migration (cloudflare_access_application -> cloudflare_zero_trust_access_application)
2222
// which requires the cmd/migrate tool and terraform state mv commands.
2323
func TestMigrateZeroTrustAccessApplication_Basic(t *testing.T) {
@@ -439,7 +439,7 @@ func TestMigrateZeroTrustAccessApplication_V4toV5_WithPolicies(t *testing.T) {
439439
t.Setenv("CLOUDFLARE_API_TOKEN", "")
440440
}
441441

442-
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
442+
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
443443
domain := os.Getenv("CLOUDFLARE_DOMAIN")
444444
rnd := utils.GenerateRandomResourceName()
445445
appResourceName := "cloudflare_zero_trust_access_application." + rnd
@@ -681,7 +681,7 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
681681
})
682682
}
683683

684-
// TestMigrateZeroTrustAccessApplication_CORSHeaders_Manual tests recovery from cors_headers
684+
// TestMigrateZeroTrustAccessApplication_CORSHeaders_Manual tests recovery from cors_headers
685685
// array-to-object state corruption by manual state editing
686686
func TestMigrateZeroTrustAccessApplication_CORSHeaders_Manual(t *testing.T) {
687687
t.Skip("Manual test: This test demonstrates the cors_headers state issue. " +
@@ -740,4 +740,4 @@ resource "cloudflare_zero_trust_access_application" "%[1]s" {
740740
},
741741
},
742742
})
743-
}
743+
}

internal/services/zero_trust_access_application/model.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,14 @@ type ZeroTrustAccessApplicationTargetCriteriaModel struct {
188188
}
189189

190190
type ZeroTrustAccessApplicationDestinationsModel struct {
191-
Type types.String `tfsdk:"type" json:"type,computed_optional"`
192-
URI types.String `tfsdk:"uri" json:"uri,optional"`
193-
CIDR types.String `tfsdk:"cidr" json:"cidr,optional"`
194-
Hostname types.String `tfsdk:"hostname" json:"hostname,optional"`
195-
L4Protocol types.String `tfsdk:"l4_protocol" json:"l4_protocol,optional"`
196-
PortRange types.String `tfsdk:"port_range" json:"port_range,optional"`
197-
VnetID types.String `tfsdk:"vnet_id" json:"vnet_id,optional"`
191+
Type types.String `tfsdk:"type" json:"type,computed_optional"`
192+
URI types.String `tfsdk:"uri" json:"uri,optional"`
193+
CIDR types.String `tfsdk:"cidr" json:"cidr,optional"`
194+
Hostname types.String `tfsdk:"hostname" json:"hostname,optional"`
195+
L4Protocol types.String `tfsdk:"l4_protocol" json:"l4_protocol,optional"`
196+
PortRange types.String `tfsdk:"port_range" json:"port_range,optional"`
197+
VnetID types.String `tfsdk:"vnet_id" json:"vnet_id,optional"`
198+
McpServerID types.String `tfsdk:"mcp_server_id" json:"mcp_server_id,optional"`
198199
}
199200

200201
type ZeroTrustAccessApplicationLandingPageDesignModel struct {

internal/services/zero_trust_access_application/plan_modifiers.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@ import (
55
"regexp"
66
"slices"
77

8+
"github.com/cloudflare/terraform-provider-cloudflare/internal/customfield"
89
"github.com/hashicorp/terraform-plugin-framework/attr"
910
"github.com/hashicorp/terraform-plugin-framework/diag"
1011
"github.com/hashicorp/terraform-plugin-framework/resource"
1112
"github.com/hashicorp/terraform-plugin-framework/types"
12-
"github.com/cloudflare/terraform-provider-cloudflare/internal/customfield"
1313
)
1414

1515
var (
16-
selfHostedAppTypes = []string{"self_hosted", "ssh", "vnc", "rdp"}
16+
selfHostedAppTypes = []string{"self_hosted", "ssh", "vnc", "rdp", "mcp_portal"}
17+
destinationCompatibleAppTypes = []string{"self_hosted", "ssh", "vnc", "rdp", "mcp_portal", "mcp"}
1718
saasAppTypes = []string{"saas", "dash_sso"}
1819
appLauncherVisibleAppTypes = []string{"self_hosted", "ssh", "vnc", "rdp", "saas", "bookmark"}
1920
targetCompatibleAppTypes = []string{"rdp", "infrastructure"}
20-
sessionDurationCompatibleAppTypes = []string{"saas", "dash_sso", "self_hosted", "ssh", "vnc", "rdp", "app_launcher", "warp"}
21+
sessionDurationCompatibleAppTypes = []string{"saas", "dash_sso", "self_hosted", "ssh", "vnc", "rdp", "app_launcher", "warp", "mcp_portal", "mcp"}
2122
authenticateViaWarpCompatibleAppTypes = []string{"self_hosted", "ssh", "vnc", "rdp", "saas", "dash_sso"}
2223
durationRegex = regexp.MustCompile(`^(?:0|[-+]?(\d+(?:\.\d*)?|\.\d+)(?:ns|us|µs|ms|s|m|h)(?:(\d+(?:\.\d*)?|\.\d+)(?:ns|us|µs|ms|s|m|h))*)$`)
2324
)
@@ -46,7 +47,6 @@ func modifyPlanForDomains(ctx context.Context, planApp, stateApp *ZeroTrustAcces
4647

4748
setDefaultAccordingToAppTypes(selfHostedAppTypes, appType, &planApp.SelfHostedDomains, customfield.UnknownList[types.String](ctx), customfield.NullList[types.String](ctx))
4849
setDefaultAccordingToAppTypes(selfHostedAppTypes, appType, &planApp.Destinations, customfield.UnknownObjectList[ZeroTrustAccessApplicationDestinationsModel](ctx), customfield.NullObjectList[ZeroTrustAccessApplicationDestinationsModel](ctx))
49-
setDefaultAccordingToAppTypes(selfHostedAppTypes, appType, &planApp.HTTPOnlyCookieAttribute, types.BoolUnknown(), types.BoolNull())
5050

5151
// A self_hosted_app's 'domain', 'self_hosted_domains', and 'destinations' are all tied together in the API.
5252
// changing one, causes the others to change. So we need to tell TF to set the other two to unknown if any of them
@@ -128,7 +128,7 @@ func modifyPlan(ctx context.Context, req resource.ModifyPlanRequest, res *resour
128128
appType := planApp.Type.ValueString()
129129

130130
// Add default values for some app type specific attributes
131-
setDefaultAccordingToAppTypes(selfHostedAppTypes, appType, &planApp.HTTPOnlyCookieAttribute, types.BoolValue(true), types.BoolNull())
131+
setDefaultAccordingToAppTypes(append(selfHostedAppTypes, "mcp"), appType, &planApp.HTTPOnlyCookieAttribute, types.BoolValue(true), types.BoolNull())
132132
setDefaultAccordingToAppTypes(appLauncherVisibleAppTypes, appType, &planApp.AppLauncherVisible, types.BoolValue(true), types.BoolNull())
133133
setDefaultAccordingToAppType("app_launcher", appType, &planApp.SkipAppLauncherLoginPage, types.BoolValue(false), types.BoolNull())
134134
setDefaultAccordingToAppTypes(sessionDurationCompatibleAppTypes, appType, &planApp.SessionDuration, types.StringValue("24h"), types.StringNull())

internal/services/zero_trust_access_application/resource_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,3 +2035,70 @@ func TestAccCloudflareAccessApplication_TagsOrderIgnored(t *testing.T) {
20352035
func testAccCloudflareAccessApplicationConfigWithTagsOrdering(rnd, domain, accountID string) string {
20362036
return acctest.LoadTestCase("accessapplicationconfigwithtagsordering.tf", rnd, domain, accountID)
20372037
}
2038+
2039+
func TestAccCloudflareAccessApplication_MCPSetup(t *testing.T) {
2040+
rnd := utils.GenerateRandomResourceName()
2041+
mcpAppName := fmt.Sprintf("cloudflare_zero_trust_access_application.%s_mcp_server", rnd)
2042+
mcpPortalAppName := fmt.Sprintf("cloudflare_zero_trust_access_application.%s_mcp_portal", rnd)
2043+
mcpPortalDomain := fmt.Sprintf("%[1]s.%[2]s", rnd, domain)
2044+
2045+
resource.Test(t, resource.TestCase{
2046+
PreCheck: func() {
2047+
acctest.TestAccPreCheck(t)
2048+
acctest.TestAccPreCheck_AccountID(t)
2049+
},
2050+
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
2051+
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
2052+
Steps: []resource.TestStep{
2053+
{
2054+
Config: testAccCloudflareAccessApplicationMCPConfig(rnd, domain, accountID),
2055+
ConfigStateChecks: []statecheck.StateCheck{
2056+
// check mcp server app
2057+
statecheck.ExpectKnownValue(mcpAppName, tfjsonpath.New(consts.AccountIDSchemaKey), knownvalue.StringExact(accountID)),
2058+
statecheck.ExpectKnownValue(mcpAppName, tfjsonpath.New("name"), knownvalue.StringExact(rnd+"_mcp_server")),
2059+
statecheck.ExpectKnownValue(mcpAppName, tfjsonpath.New("domain"), knownvalue.Null()),
2060+
statecheck.ExpectKnownValue(mcpAppName, tfjsonpath.New("type"), knownvalue.StringExact("mcp")),
2061+
statecheck.ExpectKnownValue(mcpAppName, tfjsonpath.New("destinations"), knownvalue.ListExact([]knownvalue.Check{
2062+
knownvalue.ObjectPartial(map[string]knownvalue.Check{
2063+
"type": knownvalue.StringExact("via_mcp_server_portal"),
2064+
"mcp_server_id": knownvalue.StringExact(rnd),
2065+
}),
2066+
})),
2067+
// check mcp server portal app
2068+
statecheck.ExpectKnownValue(mcpPortalAppName, tfjsonpath.New(consts.AccountIDSchemaKey), knownvalue.StringExact(accountID)),
2069+
statecheck.ExpectKnownValue(mcpPortalAppName, tfjsonpath.New("name"), knownvalue.StringExact(rnd+"_mcp_portal")),
2070+
statecheck.ExpectKnownValue(mcpPortalAppName, tfjsonpath.New("domain"), knownvalue.StringExact(mcpPortalDomain)),
2071+
statecheck.ExpectKnownValue(mcpPortalAppName, tfjsonpath.New("type"), knownvalue.StringExact("mcp_portal")),
2072+
statecheck.ExpectKnownValue(mcpPortalAppName, tfjsonpath.New("destinations"), knownvalue.ListExact([]knownvalue.Check{
2073+
knownvalue.ObjectPartial(map[string]knownvalue.Check{
2074+
"type": knownvalue.StringExact("public"),
2075+
"uri": knownvalue.StringExact(mcpPortalDomain),
2076+
}),
2077+
})),
2078+
},
2079+
},
2080+
{
2081+
ResourceName: mcpPortalAppName,
2082+
ImportState: true,
2083+
ImportStateVerify: true,
2084+
ImportStateIdPrefix: fmt.Sprintf("accounts/%s/", accountID),
2085+
ImportStateVerifyIgnore: []string{"enable_binding_cookie", "options_preflight_bypass", "auto_redirect_to_identity"},
2086+
},
2087+
{
2088+
ResourceName: mcpAppName,
2089+
ImportState: true,
2090+
ImportStateVerify: true,
2091+
ImportStateIdPrefix: fmt.Sprintf("accounts/%s/", accountID),
2092+
ImportStateVerifyIgnore: []string{"service_auth_401_redirect", "destinations", "enable_binding_cookie", "options_preflight_bypass", "self_hosted_domains", "tags", "auto_redirect_to_identity"},
2093+
},
2094+
{
2095+
Config: testAccCloudflareAccessApplicationMCPConfig(rnd, domain, accountID),
2096+
PlanOnly: true,
2097+
},
2098+
},
2099+
})
2100+
}
2101+
2102+
func testAccCloudflareAccessApplicationMCPConfig(rnd, domain, accountID string) string {
2103+
return acctest.LoadTestCase("accessapplicationmcpconfig.tf", rnd, domain, accountID)
2104+
}

internal/services/zero_trust_access_application/schema.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func ResourceSchema(ctx context.Context) schema.Schema {
140140
},
141141
},
142142
"type": schema.StringAttribute{
143-
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\".",
143+
Description: "The application type.\nAvailable values: \"self_hosted\", \"saas\", \"ssh\", \"vnc\", \"app_launcher\", \"warp\", \"biso\", \"bookmark\", \"dash_sso\", \"infrastructure\", \"rdp\", \"mcp\", \"mcp_portal\".",
144144
Optional: true,
145145
Validators: []validator.String{
146146
stringvalidator.OneOfCaseInsensitive(
@@ -155,6 +155,8 @@ func ResourceSchema(ctx context.Context) schema.Schema {
155155
"dash_sso",
156156
"infrastructure",
157157
"rdp",
158+
"mcp",
159+
"mcp_portal",
158160
),
159161
},
160162
},
@@ -490,7 +492,7 @@ func ResourceSchema(ctx context.Context) schema.Schema {
490492
Computed: true,
491493
CustomType: customfield.NewNestedObjectListType[ZeroTrustAccessApplicationDestinationsModel](ctx),
492494
Validators: []validator.List{
493-
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRoot("type"), selfHostedAppTypes...),
495+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRoot("type"), destinationCompatibleAppTypes...),
494496
listvalidator.ConflictsWith(path.Expressions{
495497
path.MatchRoot("self_hosted_domains"),
496498
}...),
@@ -503,7 +505,7 @@ func ResourceSchema(ctx context.Context) schema.Schema {
503505
Computed: true,
504506
Default: stringdefault.StaticString("public"),
505507
Validators: []validator.String{
506-
stringvalidator.OneOfCaseInsensitive("public", "private"),
508+
stringvalidator.OneOfCaseInsensitive("public", "private", "via_mcp_server_portal"),
507509
},
508510
},
509511
"uri": schema.StringAttribute{
@@ -549,6 +551,15 @@ func ResourceSchema(ctx context.Context) schema.Schema {
549551
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "private"),
550552
},
551553
},
554+
"mcp_server_id": schema.StringAttribute{
555+
Description: "A MCP server id configured in ai-controls. Access will secure the MCP server if accessed through a MCP portal.",
556+
Optional: true,
557+
Validators: []validator.String{
558+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRelative().AtParent().AtName("type"), "via_mcp_server_portal"),
559+
customvalidator.RequiredWhenOtherStringIsOneOf(path.MatchRelative().AtParent().AtName("type"), "via_mcp_server_portal"),
560+
customvalidator.RequiresOtherStringAttributeToBeOneOf(path.MatchRoot("type"), "mcp"),
561+
},
562+
},
552563
},
553564
},
554565
},
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
resource "cloudflare_zero_trust_access_application" "%[1]s_mcp_server" {
2+
account_id = "%[3]s"
3+
name = "%[1]s_mcp_server"
4+
type = "mcp"
5+
destinations = [
6+
{
7+
"type": "via_mcp_server_portal",
8+
"mcp_server_id": "%[1]s"
9+
}
10+
]
11+
}
12+
13+
resource "cloudflare_zero_trust_access_application" "%[1]s_mcp_portal" {
14+
account_id = "%[3]s"
15+
name = "%[1]s_mcp_portal"
16+
type = "mcp_portal"
17+
session_duration = "24h"
18+
domain = "%[1]s.%[2]s"
19+
self_hosted_domains = ["%[1]s.%[2]s"]
20+
}

0 commit comments

Comments
 (0)