diff --git a/docs/data-sources/application_apis.md b/docs/data-sources/application_apis.md
index 0d5c3c69..4a1bbfba 100644
--- a/docs/data-sources/application_apis.md
+++ b/docs/data-sources/application_apis.md
@@ -2,12 +2,12 @@
page_title: "zitadel_application_apis Data Source - terraform-provider-zitadel"
subcategory: ""
description: |-
- Datasource representing an API application belonging to a project, with all configuration possibilities.
+ Datasource representing multiple API applications belonging to a project.
---
# zitadel_application_apis (Data Source)
-Datasource representing an API application belonging to a project, with all configuration possibilities.
+Datasource representing multiple API applications belonging to a project.
## Example Usage
diff --git a/docs/data-sources/application_oidcs.md b/docs/data-sources/application_oidcs.md
index 3501ac9e..52c0aff3 100644
--- a/docs/data-sources/application_oidcs.md
+++ b/docs/data-sources/application_oidcs.md
@@ -2,12 +2,12 @@
page_title: "zitadel_application_oidcs Data Source - terraform-provider-zitadel"
subcategory: ""
description: |-
- Datasource representing an OIDC application belonging to a project, with all configuration possibilities.
+ Datasource representing multiple OIDC applications belonging to a project.
---
# zitadel_application_oidcs (Data Source)
-Datasource representing an OIDC application belonging to a project, with all configuration possibilities.
+Datasource representing multiple OIDC applications belonging to a project.
## Example Usage
diff --git a/docs/data-sources/application_saml.md b/docs/data-sources/application_saml.md
new file mode 100644
index 00000000..c9385133
--- /dev/null
+++ b/docs/data-sources/application_saml.md
@@ -0,0 +1,38 @@
+---
+page_title: "zitadel_application_saml Data Source - terraform-provider-zitadel"
+subcategory: ""
+description: |-
+ Datasource representing a SAML application belonging to a project, with all configuration possibilities.
+---
+
+# zitadel_application_saml (Data Source)
+
+Datasource representing a SAML application belonging to a project, with all configuration possibilities.
+
+## Example Usage
+
+```terraform
+data "zitadel_application_saml" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ app_id = "123456789012345678"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `app_id` (String) The ID of this resource.
+- `project_id` (String) ID of the project
+
+### Optional
+
+- `org_id` (String) ID of the organization
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `metadata_xml` (String) Metadata as XML file
+- `name` (String) Name of the application
\ No newline at end of file
diff --git a/docs/data-sources/application_samls.md b/docs/data-sources/application_samls.md
new file mode 100644
index 00000000..6ff8735e
--- /dev/null
+++ b/docs/data-sources/application_samls.md
@@ -0,0 +1,50 @@
+---
+page_title: "zitadel_application_samls Data Source - terraform-provider-zitadel"
+subcategory: ""
+description: |-
+ Datasource representing multiple SAML applications belonging to a project.
+---
+
+# zitadel_application_samls (Data Source)
+
+Datasource representing multiple SAML applications belonging to a project.
+
+## Example Usage
+
+```terraform
+data "zitadel_application_samls" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ name = "example-name"
+ name_method = "TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE"
+}
+
+data "zitadel_application_saml" "default" {
+ for_each = toset(data.zitadel_application_samls.default.app_ids)
+ id = each.value
+}
+
+output "app_saml_names" {
+ value = toset([
+ for app in data.zitadel_application_saml.default : app.name
+ ])
+}
+```
+
+
+## Schema
+
+### Required
+
+- `name` (String) Name of the application
+- `project_id` (String) ID of the project
+
+### Optional
+
+- `name_method` (String) Method for querying applications by name, supported values: TEXT_QUERY_METHOD_EQUALS, TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE, TEXT_QUERY_METHOD_STARTS_WITH, TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE, TEXT_QUERY_METHOD_CONTAINS, TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, TEXT_QUERY_METHOD_ENDS_WITH, TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE
+- `org_id` (String) ID of the organization
+
+### Read-Only
+
+- `app_ids` (List of String) A set of all IDs.
+- `id` (String) The ID of this resource.
\ No newline at end of file
diff --git a/docs/resources/application_saml.md b/docs/resources/application_saml.md
new file mode 100644
index 00000000..7688a54f
--- /dev/null
+++ b/docs/resources/application_saml.md
@@ -0,0 +1,45 @@
+---
+page_title: "zitadel_application_saml Resource - terraform-provider-zitadel"
+subcategory: ""
+description: |-
+ Resource representing a SAML application belonging to a project, with all configuration possibilities.
+---
+
+# zitadel_application_saml (Resource)
+
+Resource representing a SAML application belonging to a project, with all configuration possibilities.
+
+## Example Usage
+
+```terraform
+resource "zitadel_application_saml" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ name = "applicationapi"
+ metadata_xml = "\n\n \n urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n \n \n \n"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `metadata_xml` (String, Sensitive) Metadata as XML file
+- `name` (String) Name of the application
+- `project_id` (String) ID of the project
+
+### Optional
+
+- `org_id` (String) ID of the organization
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+## Import
+
+```terraform
+# The resource can be imported using the ID format ``, e.g.
+terraform import application_saml.imported '123456789012345678:123456789012345678:123456789012345678'
+```
diff --git a/examples/provider/data-sources/application_saml.tf b/examples/provider/data-sources/application_saml.tf
new file mode 100644
index 00000000..2dd638ab
--- /dev/null
+++ b/examples/provider/data-sources/application_saml.tf
@@ -0,0 +1,5 @@
+data "zitadel_application_saml" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ app_id = "123456789012345678"
+}
diff --git a/examples/provider/data-sources/application_samls.tf b/examples/provider/data-sources/application_samls.tf
new file mode 100644
index 00000000..a9fb6d65
--- /dev/null
+++ b/examples/provider/data-sources/application_samls.tf
@@ -0,0 +1,17 @@
+data "zitadel_application_samls" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ name = "example-name"
+ name_method = "TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE"
+}
+
+data "zitadel_application_saml" "default" {
+ for_each = toset(data.zitadel_application_samls.default.app_ids)
+ id = each.value
+}
+
+output "app_saml_names" {
+ value = toset([
+ for app in data.zitadel_application_saml.default : app.name
+ ])
+}
diff --git a/examples/provider/resources/application_saml-import.sh b/examples/provider/resources/application_saml-import.sh
new file mode 100644
index 00000000..6a418a4d
--- /dev/null
+++ b/examples/provider/resources/application_saml-import.sh
@@ -0,0 +1,2 @@
+# The resource can be imported using the ID format ``, e.g.
+terraform import application_saml.imported '123456789012345678:123456789012345678:123456789012345678'
diff --git a/examples/provider/resources/application_saml.tf b/examples/provider/resources/application_saml.tf
new file mode 100644
index 00000000..b4da07db
--- /dev/null
+++ b/examples/provider/resources/application_saml.tf
@@ -0,0 +1,6 @@
+resource "zitadel_application_saml" "default" {
+ org_id = data.zitadel_org.default.id
+ project_id = data.zitadel_project.default.id
+ name = "applicationapi"
+ metadata_xml = "\n\n \n urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n \n \n \n"
+}
diff --git a/templates/data-sources/application_saml.md.tmpl b/templates/data-sources/application_saml.md.tmpl
new file mode 100644
index 00000000..dc9679ac
--- /dev/null
+++ b/templates/data-sources/application_saml.md.tmpl
@@ -0,0 +1,16 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+## Example Usage
+
+{{ tffile "examples/provider/data-sources/application_saml.tf" }}
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/application_samls.md.tmpl b/templates/data-sources/application_samls.md.tmpl
new file mode 100644
index 00000000..d98fe1d3
--- /dev/null
+++ b/templates/data-sources/application_samls.md.tmpl
@@ -0,0 +1,16 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+## Example Usage
+
+{{ tffile "examples/provider/data-sources/application_samls.tf" }}
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/resources/application_saml.md.tmpl b/templates/resources/application_saml.md.tmpl
new file mode 100644
index 00000000..af4521b3
--- /dev/null
+++ b/templates/resources/application_saml.md.tmpl
@@ -0,0 +1,20 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+## Example Usage
+
+{{ tffile "examples/provider/resources/application_saml.tf" }}
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+{{ tffile "examples/provider/resources/application_saml-import.sh" }}
diff --git a/zitadel/application_api/datasource.go b/zitadel/application_api/datasource.go
index 39b26a4b..c6207f0a 100644
--- a/zitadel/application_api/datasource.go
+++ b/zitadel/application_api/datasource.go
@@ -41,7 +41,7 @@ func GetDatasource() *schema.Resource {
func ListDatasources() *schema.Resource {
return &schema.Resource{
- Description: "Datasource representing an API application belonging to a project, with all configuration possibilities.",
+ Description: "Datasource representing multiple API applications belonging to a project.",
Schema: map[string]*schema.Schema{
helper.OrgIDVar: helper.OrgIDDatasourceField,
appIDsVar: {
diff --git a/zitadel/application_api/funcs.go b/zitadel/application_api/funcs.go
index 78cfbffd..4ac4e63c 100644
--- a/zitadel/application_api/funcs.go
+++ b/zitadel/application_api/funcs.go
@@ -179,6 +179,9 @@ func list(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagn
}
ids := make([]string, len(resp.Result))
for i, res := range resp.Result {
+ if res.GetApiConfig() == nil {
+ continue
+ }
ids[i] = res.Id
}
// If the ID is blank, the datasource is deleted and not usable.
diff --git a/zitadel/application_oidc/datasource.go b/zitadel/application_oidc/datasource.go
index c1aea588..3d715d34 100644
--- a/zitadel/application_oidc/datasource.go
+++ b/zitadel/application_oidc/datasource.go
@@ -121,7 +121,7 @@ func GetDatasource() *schema.Resource {
func ListDatasources() *schema.Resource {
return &schema.Resource{
- Description: "Datasource representing an OIDC application belonging to a project, with all configuration possibilities.",
+ Description: "Datasource representing multiple OIDC applications belonging to a project.",
Schema: map[string]*schema.Schema{
helper.OrgIDVar: helper.OrgIDDatasourceField,
appIDsVar: {
diff --git a/zitadel/application_oidc/funcs.go b/zitadel/application_oidc/funcs.go
index 691069aa..d961eb5a 100644
--- a/zitadel/application_oidc/funcs.go
+++ b/zitadel/application_oidc/funcs.go
@@ -284,6 +284,9 @@ func list(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagn
}
ids := make([]string, len(resp.Result))
for i, res := range resp.Result {
+ if res.GetOidcConfig() == nil {
+ continue
+ }
ids[i] = res.Id
}
// If the ID is blank, the datasource is deleted and not usable.
diff --git a/zitadel/application_saml/application_saml_test_dep/dependency.go b/zitadel/application_saml/application_saml_test_dep/dependency.go
new file mode 100644
index 00000000..46a60dc3
--- /dev/null
+++ b/zitadel/application_saml/application_saml_test_dep/dependency.go
@@ -0,0 +1,25 @@
+package application_saml_test_dep
+
+import (
+ "testing"
+
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/application_saml"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils"
+)
+
+func Create(t *testing.T, frame *test_utils.OrgTestFrame, projectID, name string) (string, string) {
+ return test_utils.CreateDefaultDependency(t, "zitadel_application_saml", application_saml.AppIDVar, func() (string, error) {
+ app, err := frame.AddSAMLApp(frame, &management.AddSAMLAppRequest{
+ ProjectId: projectID,
+ Name: name,
+ Metadata: &management.AddSAMLAppRequest_MetadataXml{MetadataXml: metadata(name)},
+ })
+ return app.GetAppId(), err
+ })
+}
+
+func metadata(name string) []byte {
+ return []byte("\n\n \n urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n \n \n \n")
+}
diff --git a/zitadel/application_saml/const.go b/zitadel/application_saml/const.go
new file mode 100644
index 00000000..a305df7a
--- /dev/null
+++ b/zitadel/application_saml/const.go
@@ -0,0 +1,10 @@
+package application_saml
+
+const (
+ AppIDVar = "app_id"
+ appIDsVar = "app_ids"
+ ProjectIDVar = "project_id"
+ NameVar = "name"
+ nameMethodVar = "name_method"
+ MetadataXMLVar = "metadata_xml"
+)
diff --git a/zitadel/application_saml/datasource.go b/zitadel/application_saml/datasource.go
new file mode 100644
index 00000000..6d28a94c
--- /dev/null
+++ b/zitadel/application_saml/datasource.go
@@ -0,0 +1,75 @@
+package application_saml
+
+import (
+ "github.com/hashicorp/go-cty/cty"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper"
+)
+
+func GetDatasource() *schema.Resource {
+ return &schema.Resource{
+ Description: "Datasource representing a SAML application belonging to a project, with all configuration possibilities.",
+ Schema: map[string]*schema.Schema{
+ AppIDVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The ID of this resource.",
+ },
+ helper.OrgIDVar: helper.OrgIDDatasourceField,
+ ProjectIDVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "ID of the project",
+ },
+ NameVar: {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Name of the application",
+ },
+ MetadataXMLVar: {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Metadata as XML file",
+ },
+ },
+ ReadContext: read,
+ }
+}
+
+func ListDatasources() *schema.Resource {
+ return &schema.Resource{
+ Description: "Datasource representing multiple SAML applications belonging to a project.",
+ Schema: map[string]*schema.Schema{
+ helper.OrgIDVar: helper.OrgIDDatasourceField,
+ appIDsVar: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "A set of all IDs.",
+ Elem: &schema.Schema{Type: schema.TypeString},
+ },
+ ProjectIDVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "ID of the project",
+ },
+ NameVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Name of the application",
+ },
+ nameMethodVar: {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Method for querying applications by name" + helper.DescriptionEnumValuesList(object.TextQueryMethod_name),
+ ValidateDiagFunc: func(value interface{}, path cty.Path) diag.Diagnostics {
+ return helper.EnumValueValidation(nameMethodVar, value, object.TextQueryMethod_value)
+ },
+ Default: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE.String(),
+ },
+ },
+ ReadContext: list,
+ }
+}
diff --git a/zitadel/application_saml/datasource_test.go b/zitadel/application_saml/datasource_test.go
new file mode 100644
index 00000000..c2eede61
--- /dev/null
+++ b/zitadel/application_saml/datasource_test.go
@@ -0,0 +1,105 @@
+package application_saml_test
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/application_saml"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/application_saml/application_saml_test_dep"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep"
+)
+
+func TestAccApplicationSAMLDatasource_ID(t *testing.T) {
+ datasourceName := "zitadel_application_saml"
+ frame := test_utils.NewOrgTestFrame(t, datasourceName)
+ config, attributes := test_utils.ReadExample(t, test_utils.Datasources, datasourceName)
+ exampleID := test_utils.AttributeValue(t, application_saml.AppIDVar, attributes).AsString()
+ projectDep, projectID := project_test_dep.Create(t, frame, frame.UniqueResourcesID)
+ appName := "application_saml_datasource_" + frame.UniqueResourcesID
+ _, appID := application_saml_test_dep.Create(t, frame, projectID, appName)
+ config = strings.Replace(config, exampleID, appID, 1)
+ test_utils.RunDatasourceTest(
+ t,
+ frame.BaseTestFrame,
+ config,
+ []string{frame.AsOrgDefaultDependency, projectDep},
+ nil,
+ map[string]string{
+ "org_id": frame.OrgID,
+ "project_id": projectID,
+ "app_id": appID,
+ "name": appName,
+ },
+ )
+}
+
+func TestAccApplicationSAMLsDatasources_ID_Name_Match(t *testing.T) {
+ datasourceName := "zitadel_application_samls"
+ frame := test_utils.NewOrgTestFrame(t, datasourceName)
+ config, attributes := test_utils.ReadExample(t, test_utils.Datasources, datasourceName)
+ exampleName := test_utils.AttributeValue(t, application_saml.NameVar, attributes).AsString()
+ appName := fmt.Sprintf("%s-%s", exampleName, frame.UniqueResourcesID)
+ // for-each is not supported in acceptance tests, so we cut the example down to the first block
+ // https://github.com/hashicorp/terraform-plugin-sdk/issues/536
+ config = strings.Join(strings.Split(config, "\n")[0:6], "\n")
+ config = strings.Replace(config, exampleName, appName, 1)
+ projectDep, projectID := project_test_dep.Create(t, frame, frame.UniqueResourcesID)
+ _, appID := application_saml_test_dep.Create(t, frame, projectID, appName)
+ test_utils.RunDatasourceTest(
+ t,
+ frame.BaseTestFrame,
+ config,
+ []string{frame.AsOrgDefaultDependency, projectDep},
+ checkRemoteDatasourceProperty(frame, projectID, appID)(appName),
+ map[string]string{
+ "app_ids.0": appID,
+ "app_ids.#": "1",
+ },
+ )
+}
+
+func TestAccApplicationSAMLsDatasources_ID_Name_Mismatch(t *testing.T) {
+ datasourceName := "zitadel_application_samls"
+ frame := test_utils.NewOrgTestFrame(t, datasourceName)
+ config, attributes := test_utils.ReadExample(t, test_utils.Datasources, datasourceName)
+ exampleName := test_utils.AttributeValue(t, application_saml.NameVar, attributes).AsString()
+ appName := fmt.Sprintf("%s-%s", exampleName, frame.UniqueResourcesID)
+ // for-each is not supported in acceptance tests, so we cut the example down to the first block
+ // https://github.com/hashicorp/terraform-plugin-sdk/issues/536
+ config = strings.Join(strings.Split(config, "\n")[0:6], "\n")
+ config = strings.Replace(config, exampleName, "mismatch", 1)
+ projectDep, projectID := project_test_dep.Create(t, frame, frame.UniqueResourcesID)
+ _, appID := application_saml_test_dep.Create(t, frame, projectID, appName)
+ test_utils.RunDatasourceTest(
+ t,
+ frame.BaseTestFrame,
+ config,
+ []string{frame.AsOrgDefaultDependency, projectDep},
+ checkRemoteDatasourceProperty(frame, projectID, appID)(appName),
+ map[string]string{
+ "app_ids.#": "0",
+ },
+ )
+}
+
+func checkRemoteDatasourceProperty(frame *test_utils.OrgTestFrame, projectId, id string) func(string) resource.TestCheckFunc {
+ return func(expect string) resource.TestCheckFunc {
+ return func(state *terraform.State) error {
+ remoteResource, err := frame.GetAppByID(frame, &management.GetAppByIDRequest{AppId: id, ProjectId: projectId})
+ if err != nil {
+ return err
+ }
+ actual := remoteResource.GetApp().GetName()
+ if actual != expect {
+ return fmt.Errorf("expected %s, but got %s", expect, actual)
+ }
+ return nil
+ }
+ }
+}
diff --git a/zitadel/application_saml/funcs.go b/zitadel/application_saml/funcs.go
new file mode 100644
index 00000000..493c524a
--- /dev/null
+++ b/zitadel/application_saml/funcs.go
@@ -0,0 +1,181 @@
+package application_saml
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/app"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/object"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper"
+)
+
+func delete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ tflog.Info(ctx, "started delete")
+
+ clientinfo, ok := m.(*helper.ClientInfo)
+ if !ok {
+ return diag.Errorf("failed to get client")
+ }
+
+ client, err := helper.GetManagementClient(clientinfo)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ _, err = client.RemoveApp(helper.CtxWithOrgID(ctx, d), &management.RemoveAppRequest{
+ ProjectId: d.Get(ProjectIDVar).(string),
+ AppId: d.Id(),
+ })
+ if err != nil {
+ return diag.Errorf("failed to delete applicationSAML: %v", err)
+ }
+ return nil
+}
+
+func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ tflog.Info(ctx, "started update")
+
+ clientinfo, ok := m.(*helper.ClientInfo)
+ if !ok {
+ return diag.Errorf("failed to get client")
+ }
+
+ client, err := helper.GetManagementClient(clientinfo)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ projectID := d.Get(ProjectIDVar).(string)
+ if d.HasChange(NameVar) {
+ _, err = client.UpdateApp(helper.CtxWithOrgID(ctx, d), &management.UpdateAppRequest{
+ ProjectId: projectID,
+ AppId: d.Id(),
+ Name: d.Get(NameVar).(string),
+ })
+ if err != nil {
+ return diag.Errorf("failed to update application: %v", err)
+ }
+ }
+
+ if d.HasChanges(MetadataXMLVar) {
+ _, err = client.UpdateSAMLAppConfig(helper.CtxWithOrgID(ctx, d), &management.UpdateSAMLAppConfigRequest{
+ ProjectId: projectID,
+ AppId: d.Id(),
+ Metadata: &management.UpdateSAMLAppConfigRequest_MetadataXml{
+ MetadataXml: []byte(d.Get(MetadataXMLVar).(string)),
+ },
+ })
+ if err != nil {
+ return diag.Errorf("failed to update applicationSAML: %v", err)
+ }
+ }
+ return nil
+}
+
+func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ tflog.Info(ctx, "started create")
+
+ clientinfo, ok := m.(*helper.ClientInfo)
+ if !ok {
+ return diag.Errorf("failed to get client")
+ }
+
+ client, err := helper.GetManagementClient(clientinfo)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ resp, err := client.AddSAMLApp(helper.CtxWithOrgID(ctx, d), &management.AddSAMLAppRequest{
+ ProjectId: d.Get(ProjectIDVar).(string),
+ Name: d.Get(NameVar).(string),
+ Metadata: &management.AddSAMLAppRequest_MetadataXml{MetadataXml: []byte(d.Get(MetadataXMLVar).(string))},
+ })
+ if err != nil {
+ return diag.Errorf("failed to create applicationSAML: %v", err)
+ }
+ d.SetId(resp.GetAppId())
+ return nil
+}
+
+func read(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ tflog.Info(ctx, "started read")
+
+ clientinfo, ok := m.(*helper.ClientInfo)
+ if !ok {
+ return diag.Errorf("failed to get client")
+ }
+
+ client, err := helper.GetManagementClient(clientinfo)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ resp, err := client.GetAppByID(helper.CtxWithOrgID(ctx, d), &management.GetAppByIDRequest{ProjectId: d.Get(ProjectIDVar).(string), AppId: helper.GetID(d, AppIDVar)})
+ if err != nil && helper.IgnoreIfNotFoundError(err) == nil {
+ d.SetId("")
+ return nil
+ }
+ if err != nil {
+ return diag.Errorf("failed to get application saml")
+ }
+
+ app := resp.GetApp()
+ set := map[string]interface{}{
+ helper.OrgIDVar: app.GetDetails().GetResourceOwner(),
+ NameVar: app.GetName(),
+ MetadataXMLVar: string(app.GetSamlConfig().GetMetadataXml()),
+ }
+ for k, v := range set {
+ if err := d.Set(k, v); err != nil {
+ return diag.Errorf("failed to set %s of applicationSAML: %v", k, err)
+ }
+ }
+ d.SetId(app.GetId())
+ return nil
+}
+
+func list(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ tflog.Info(ctx, "started list")
+ name := d.Get(NameVar).(string)
+ nameMethod := d.Get(nameMethodVar).(string)
+ clientinfo, ok := m.(*helper.ClientInfo)
+ if !ok {
+ return diag.Errorf("failed to get client")
+ }
+ client, err := helper.GetManagementClient(clientinfo)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ req := &management.ListAppsRequest{
+ ProjectId: d.Get(ProjectIDVar).(string),
+ }
+ if name != "" {
+ req.Queries = append(req.Queries,
+ &app.AppQuery{
+ Query: &app.AppQuery_NameQuery{
+ NameQuery: &app.AppNameQuery{
+ Name: name,
+ Method: object.TextQueryMethod(object.TextQueryMethod_value[nameMethod]),
+ },
+ },
+ })
+ }
+ resp, err := client.ListApps(helper.CtxWithOrgID(ctx, d), req)
+ if err != nil {
+ return diag.Errorf("error while getting app by name %s: %v", name, err)
+ }
+ ids := make([]string, len(resp.Result))
+ for i, res := range resp.Result {
+ if res.GetSamlConfig() == nil {
+ continue
+ }
+ ids[i] = res.Id
+ }
+ // If the ID is blank, the datasource is deleted and not usable.
+ d.SetId("-")
+ return diag.FromErr(d.Set(appIDsVar, ids))
+}
diff --git a/zitadel/application_saml/resource.go b/zitadel/application_saml/resource.go
new file mode 100644
index 00000000..2bf425b7
--- /dev/null
+++ b/zitadel/application_saml/resource.go
@@ -0,0 +1,41 @@
+package application_saml
+
+import (
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper"
+)
+
+func GetResource() *schema.Resource {
+ return &schema.Resource{
+ Description: "Resource representing a SAML application belonging to a project, with all configuration possibilities.",
+ Schema: map[string]*schema.Schema{
+ helper.OrgIDVar: helper.OrgIDResourceField,
+ ProjectIDVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "ID of the project",
+ ForceNew: true,
+ },
+ NameVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Name of the application",
+ },
+ MetadataXMLVar: {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Metadata as XML file",
+ Sensitive: true,
+ },
+ },
+ DeleteContext: delete,
+ CreateContext: create,
+ UpdateContext: update,
+ ReadContext: read,
+ Importer: helper.ImportWithIDAndOptionalOrg(
+ AppIDVar,
+ helper.NewImportAttribute(ProjectIDVar, helper.ConvertID, false),
+ ),
+ }
+}
diff --git a/zitadel/application_saml/resource_test.go b/zitadel/application_saml/resource_test.go
new file mode 100644
index 00000000..a751194f
--- /dev/null
+++ b/zitadel/application_saml/resource_test.go
@@ -0,0 +1,55 @@
+package application_saml_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+ "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
+
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/application_saml"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/helper/test_utils"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/project/project_test_dep"
+)
+
+func TestAccAppSAML(t *testing.T) {
+ frame := test_utils.NewOrgTestFrame(t, "zitadel_application_saml")
+ resourceExample, exampleAttributes := test_utils.ReadExample(t, test_utils.Resources, frame.ResourceType)
+ exampleProperty := test_utils.AttributeValue(t, application_saml.NameVar, exampleAttributes).AsString()
+ projectDep, projectID := project_test_dep.Create(t, frame, frame.UniqueResourcesID)
+ test_utils.RunLifecyleTest(
+ t,
+ frame.BaseTestFrame,
+ []string{frame.AsOrgDefaultDependency, projectDep},
+ test_utils.ReplaceAll(resourceExample, exampleProperty, ""),
+ exampleProperty, "updatedproperty",
+ "", "", "",
+ false,
+ checkRemoteProperty(frame, projectID),
+ helper.ZitadelGeneratedIdOnlyRegex,
+ test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame, projectID), ""),
+ test_utils.ChainImportStateIdFuncs(
+ test_utils.ImportResourceId(frame.BaseTestFrame),
+ test_utils.ImportStateAttribute(frame.BaseTestFrame, application_saml.ProjectIDVar),
+ test_utils.ImportOrgId(frame),
+ ),
+ )
+}
+
+func checkRemoteProperty(frame *test_utils.OrgTestFrame, projectId string) func(string) resource.TestCheckFunc {
+ return func(expect string) resource.TestCheckFunc {
+ return func(state *terraform.State) error {
+ remoteResource, err := frame.GetAppByID(frame, &management.GetAppByIDRequest{AppId: frame.State(state).ID, ProjectId: projectId})
+ if err != nil {
+ return err
+ }
+ actual := remoteResource.GetApp().GetName()
+ if actual != expect {
+ return fmt.Errorf("expected %s, but got %s", expect, actual)
+ }
+ return nil
+ }
+ }
+}
diff --git a/zitadel/provider.go b/zitadel/provider.go
index 899e901e..86bb46d7 100644
--- a/zitadel/provider.go
+++ b/zitadel/provider.go
@@ -17,6 +17,7 @@ import (
"github.com/zitadel/terraform-provider-zitadel/zitadel/application_api"
"github.com/zitadel/terraform-provider-zitadel/zitadel/application_key"
"github.com/zitadel/terraform-provider-zitadel/zitadel/application_oidc"
+ "github.com/zitadel/terraform-provider-zitadel/zitadel/application_saml"
"github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_claimed_message_text"
"github.com/zitadel/terraform-provider-zitadel/zitadel/default_domain_policy"
"github.com/zitadel/terraform-provider-zitadel/zitadel/default_init_message_text"
@@ -217,6 +218,8 @@ func Provider() *schema.Provider {
"zitadel_application_oidcs": application_oidc.ListDatasources(),
"zitadel_application_api": application_api.GetDatasource(),
"zitadel_application_apis": application_api.ListDatasources(),
+ "zitadel_application_saml": application_saml.GetDatasource(),
+ "zitadel_application_samls": application_saml.ListDatasources(),
"zitadel_trigger_actions": trigger_actions.GetDatasource(),
"zitadel_idp_github": idp_github.GetDatasource(),
"zitadel_idp_github_es": idp_github_es.GetDatasource(),
@@ -278,6 +281,7 @@ func Provider() *schema.Provider {
"zitadel_action": action.GetResource(),
"zitadel_application_oidc": application_oidc.GetResource(),
"zitadel_application_api": application_api.GetResource(),
+ "zitadel_application_saml": application_saml.GetResource(),
"zitadel_application_key": application_key.GetResource(),
"zitadel_project_grant": project_grant.GetResource(),
"zitadel_user_grant": user_grant.GetResource(),