diff --git a/ovh/provider.go b/ovh/provider.go
index 6579553a5..a3f9bb1c9 100644
--- a/ovh/provider.go
+++ b/ovh/provider.go
@@ -208,6 +208,7 @@ func Provider() *schema.Provider {
"ovh_iploadbalancing_tcp_route_rule": resourceIPLoadbalancingTcpRouteRule(),
"ovh_iploadbalancing_vrack_network": resourceIPLoadbalancingVrackNetwork(),
"ovh_me_identity_group": resourceMeIdentityGroup(),
+ "ovh_me_identity_provider": resourceMeIdentityProvider(),
"ovh_me_identity_user": resourceMeIdentityUser(),
"ovh_me_installation_template": resourceMeInstallationTemplate(),
"ovh_me_installation_template_partition_scheme": resourceMeInstallationTemplatePartitionScheme(),
diff --git a/ovh/resource_me_identity_provider.go b/ovh/resource_me_identity_provider.go
new file mode 100644
index 000000000..d2611c901
--- /dev/null
+++ b/ovh/resource_me_identity_provider.go
@@ -0,0 +1,181 @@
+package ovh
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func resourceMeIdentityProvider() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: resourceMeIdentityProviderCreate,
+ ReadContext: resourceMeIdentityProviderRead,
+ UpdateContext: resourceMeIdentityProviderUpdate,
+ DeleteContext: resourceMeIdentityProviderDelete,
+
+ Importer: &schema.ResourceImporter{
+ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
+ return []*schema.ResourceData{d}, nil
+ },
+ },
+
+ Schema: map[string]*schema.Schema{
+ "metadata": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "group_attribute_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "requested_attributes": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "is_required": {
+ Type: schema.TypeBool,
+ Required: true,
+ },
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "name_format": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "values": {
+ Type: schema.TypeList,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ Required: true,
+ },
+ },
+ },
+ },
+ "disable_users": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ },
+ "creation": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_update": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+// Common function with the datasource
+func resourceMeIdentityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*Config)
+
+ providerConfDetails := &MeIdentityProviderResponse{}
+ if err := config.OVHClient.GetWithContext(ctx, "/me/identity/provider", providerConfDetails); err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.Set("group_attribute_name", providerConfDetails.GroupAttributeName)
+ d.Set("disable_users", providerConfDetails.DisableUsers)
+ d.Set("requested_attributes", requestedAttributesToMapList(providerConfDetails.Extensions.RequestedAttributes))
+ d.Set("creation", providerConfDetails.Creation)
+ d.Set("last_update", providerConfDetails.LastUpdate)
+
+ return nil
+}
+
+func requestedAttributesToMapList(attributes []MeIdentityProviderAttribute) []map[string]interface{} {
+ requestedAttributes := []map[string]interface{}{}
+ for _, v := range attributes {
+ requestedAttributes = append(requestedAttributes, map[string]interface{}{
+ "is_required": v.IsRequired,
+ "name": v.Name,
+ "name_format": v.NameFormat,
+ "values": v.Values,
+ })
+ }
+ return requestedAttributes
+}
+
+func resourceMeIdentityProviderCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*Config)
+
+ metadata := d.Get("metadata").(string)
+
+ groupAttributeName := d.Get("group_attribute_name").(string)
+ disableUsers := d.Get("disable_users").(bool)
+ requestedAttributes, err := loadMeIdentityProviderAttributeListFromResource(d.Get("requested_attributes"))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ params := &MeIdentityProviderCreateOpts{
+ Metadata: metadata,
+ GroupAttributeName: groupAttributeName,
+ DisableUsers: disableUsers,
+ Extensions: MeIdentityProviderExtensions{
+ RequestedAttributes: requestedAttributes,
+ },
+ }
+
+ err = config.OVHClient.PostWithContext(ctx, "/me/identity/provider", params, nil)
+ if err != nil {
+ return diag.Errorf("Error creating identity provider:\n\t %v", err)
+ }
+
+ d.SetId("ovh_sso")
+
+ return resourceMeIdentityProviderRead(ctx, d, meta)
+}
+
+func resourceMeIdentityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*Config)
+
+ groupAttributeName := d.Get("group_attribute_name").(string)
+ disableUsers := d.Get("disable_users").(bool)
+ requestedAttributes, err := loadMeIdentityProviderAttributeListFromResource(d.Get("requested_attributes"))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ params := &MeIdentityProviderUpdateOpts{
+ GroupAttributeName: groupAttributeName,
+ DisableUsers: disableUsers,
+ Extensions: MeIdentityProviderExtensions{
+ RequestedAttributes: requestedAttributes,
+ },
+ }
+ err = config.OVHClient.PutWithContext(ctx,
+ "/me/identity/provider",
+ params,
+ nil,
+ )
+ if err != nil {
+ return diag.Errorf("Unable to update identity provider:\n\t %q", err)
+ }
+
+ return resourceMeIdentityProviderRead(ctx, d, meta)
+}
+
+func resourceMeIdentityProviderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*Config)
+
+ err := config.OVHClient.DeleteWithContext(ctx,
+ "/me/identity/provider",
+ nil,
+ )
+ if err != nil {
+ return diag.Errorf("Unable to delete identity provider:\n\t %q", err)
+ }
+
+ d.SetId("")
+ return nil
+}
diff --git a/ovh/resource_me_identity_provider_test.go b/ovh/resource_me_identity_provider_test.go
new file mode 100644
index 000000000..571651002
--- /dev/null
+++ b/ovh/resource_me_identity_provider_test.go
@@ -0,0 +1,136 @@
+package ovh
+
+import (
+ "fmt"
+ "log"
+ "testing"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+)
+
+const samlIDPMetadata string = `
+
+
+
+
+
+ MIIFlTCCA32gAwIBAgIUP8WQwHQwrvTa00RU9JROZAJj9ccwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxDDAKBgNVBAcMA1JCWDERMA8GA1UECgwIT1ZIY2xvdWQxFTATBgNVBAMMDG92aGNsb3VkLmNvbTAeFw0yMzExMDkxMDA2MjRaFw0zMzExMDYxMDA2MjRaMFoxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMQwwCgYDVQQHDANSQlgxETAPBgNVBAoMCE9WSGNsb3VkMRUwEwYDVQQDDAxvdmhjbG91ZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4V3HulFBksxpgkgR6KgDaSSIKkKRgyDGCF06oQN/WPxGDSHTQQHTMN7jnsbr2uJieNKh+iasGvE9JFmd6nutloL1UHoO/ecrE8P2PYgpgezl7WfyoscBDZAjWM8E9FdENnonhvlga2DgV2DGIB4+D7aN6TIPfWukOB2MjfQloA9Iw71+peO9R55S7x7zixgpLO9NovbmaAyClbz06Tsm/7ezM+Vte7BfFqGUnwuNzqgOYfQm88EqXTpCT3QfR8i2IydGgAFLMFs9YvMnCaNLw9PCN7U6VPkY6M6cFQhO/moRb3H/euJnLNRMsXp99K8ruUnQ6902NXpOOnQu5Ewzfahmx0WWvlpFGdJK34oXjaWeTodGuvHtDxCY4tiHr8jCHf9h4cmC20xAyd/V7XBtu1Pc5UAg4I0w5ehWvHtVdxCsuPEh7c4qtuLyN9Qh15r+eRbiqnWTH/xJTwfo6q6iafXXcFOlTn7WoWmmeq0R8whg6XjcxMIzBXjtynTDbQa4LVq3T8iJiGfuDgwv5OwDPRN1CsawxefETsCUQ+jf/Iw/4nZpD/YqCI5xvYtDgPSt3v2TsoOnwOSjOqKmEOoHxGTN3mhbcD+I1QKJW79zqu6GVXVwMkgWdP4pkIWGccB0FqhIVzY19xQ40DbfnCkMTv2XN4t53c/q7CYhtvyN3XwIDAQABo1MwUTAdBgNVHQ4EFgQUC8yuX4Ub/Od5jSaz7NdwHUSlq5wwHwYDVR0jBBgwFoAUC8yuX4Ub/Od5jSaz7NdwHUSlq5wwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAR4MzroH8kEwqcZeB94hetY/NQGZI+kZ26iKnvLaZa8r56UeiIrEGdEeys5JdQh/XJDWsEU6piJ0dwrIkkpgZELmUmToylcxndzjjcHbiKLlqkL+kBu9QeO/r6JTHaNyWs0An2VvCUfo+Frt8hvrJCINlCDylOaWIxHH3P0TG7ThFGWSy8nW+VMMXDS8vQIGRM66HqgYlu6HBryecf0SsCkVYbUb1zYJ+lEhYK0pj4RORainJX+PU+mIMUwQtfBByuI7RP0a2Vny0gffrtPuNfhRJb8Pwt2UYw2niWUDOfXuk9RYgqX/1wLVqk72KJJlD3c7+abZ6BcNEJax5e/icilUrxcs4MymDPjk63kQURRVzcC4hCXYqJVQmRfVT4fdLLKPmeg3ysl+U4eJZ8odmaqoVGqZryncdAC+nT5lnLRm6m2lv3v+YhConctLxzCwV/xA8jU2w9VVRw2gkY8bdkvOb7c2OpXU6J3TYtaltG7foQiuXbRd37GWzzzEspxiAI9y8uIEJTsASaufsEdpR+a1sPy3rYJom/Li3dH9p9Ch+tp51pMYhSRGEiNu9g5918zMbrKvwkl6h/PQlTOlb65qUUoNKC5Baxhz3VkGxSKMUwS4Lj/WHvCGU5OteGFHglDgDm125FDakOYU1dnMm/P55yNhnSUH2sXngybxnw/w=
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+
+
+
+`
+
+func init() {
+ resource.AddTestSweepers("ovh_me_identity_provider", &resource.Sweeper{
+ Name: "ovh_me_identity_provider",
+ F: testSweepMeIdentityProvider,
+ })
+}
+
+func testSweepMeIdentityProvider(region string) error {
+ client, err := sharedClientForRegion(region)
+ if err != nil {
+ return fmt.Errorf("error getting client: %s", err)
+ }
+
+ err = resource.Retry(5*time.Minute, func() *resource.RetryError {
+ log.Printf("[INFO] Deleting identity provider")
+ if err := client.Delete("/me/identity/provider", nil); err != nil {
+ return resource.RetryableError(err)
+ }
+
+ // Successful delete
+ return nil
+ })
+
+ return err
+}
+
+func TestAccMeIdentityProvider_basic(t *testing.T) {
+ groupeAttribute := acctest.RandomWithPrefix(test_prefix)
+ disableUsers := "false"
+ config := fmt.Sprintf(testAccMeIdentityProviderConfig_basic, groupeAttribute, disableUsers, samlIDPMetadata)
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckCredentials(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ checkIdentityProviderResourceAttr("ovh_me_identity_provider.my_provider", groupeAttribute, disableUsers, samlIDPMetadata, nil)...,
+ ),
+ },
+ },
+ })
+}
+
+func TestAccMeIdentityProvider_requestedAttributes(t *testing.T) {
+ groupeAttribute := acctest.RandomWithPrefix(test_prefix)
+ disableUsers := "false"
+ requestedAttribute := map[string]string{
+ "is_required": "false",
+ "name": "test1",
+ "name_format": "test2",
+ "values": "test3",
+ }
+ config := fmt.Sprintf(testAccMeIdentityProviderConfig_requestedAttribute, groupeAttribute, disableUsers, samlIDPMetadata, requestedAttribute["is_required"], requestedAttribute["name"], requestedAttribute["name_format"], requestedAttribute["values"])
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckCredentials(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ checkIdentityProviderResourceAttr("ovh_me_identity_provider.my_provider", groupeAttribute, disableUsers, samlIDPMetadata, requestedAttribute)...,
+ ),
+ },
+ },
+ })
+}
+
+func checkIdentityProviderResourceAttr(name, group_attribute, disable_users, metadata string, requestedAttributes map[string]string) []resource.TestCheckFunc {
+ checks := []resource.TestCheckFunc{}
+ checks = append(checks, resource.TestCheckResourceAttr(name, "group_attribute_name", group_attribute))
+ checks = append(checks, resource.TestCheckResourceAttr(name, "disable_users", disable_users))
+ checks = append(checks, resource.TestCheckResourceAttr(name, "metadata", metadata+"\n"))
+ if requestedAttributes != nil {
+ checks = append(checks, resource.TestCheckResourceAttr(name, "requested_attributes.0.is_required", requestedAttributes["is_required"]))
+ checks = append(checks, resource.TestCheckResourceAttr(name, "requested_attributes.0.name", requestedAttributes["name"]))
+ checks = append(checks, resource.TestCheckResourceAttr(name, "requested_attributes.0.name_format", requestedAttributes["name_format"]))
+ checks = append(checks, resource.TestCheckResourceAttr(name, "requested_attributes.0.values.0", requestedAttributes["values"]))
+ }
+ return checks
+}
+
+const testAccMeIdentityProviderConfig_basic = `
+resource "ovh_me_identity_provider" "my_provider" {
+ group_attribute_name = "%s"
+ disable_users = %s
+ metadata = <
+
+
+
+
+
+ MIIFlTCCA32gAwIBAgIUP8WQwHQwrvTa00RU9JROZAJj9ccwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxDDAKBgNVBAcMA1JCWDERMA8GA1UECgwIT1ZIY2xvdWQxFTATBgNVBAMMDG92aGNsb3VkLmNvbTAeFw0yMzExMDkxMDA2[...]xA8jU2w9VVRw2gkY8bdkvOb7c2OpXU6J3TYtaltG7foQiuXbRd37GWzzzEspxiAI9y8uIEJTsASaufsEdpR+a1sPy3rYJom/Li3dH9p9Ch+tp51pMYhSRGEiNu9g5918zMbrKvwkl6h/PQlTOlb65qUUoNKC5Baxhz3VkGxSKMUwS4Lj/WHvCGU5OteGFHglDgDm125FDakOYU1dnMm/P55yNhnSUH2sXngybxnw/w=
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+
+
+
+
+EOT
+
+ disable_users = false
+
+ requested_attributes {
+ is_required = false
+ name = "group"
+ name_format = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
+ values = ["test"]
+ }
+ requested_attributes {
+ is_required = false
+ name = "email"
+ name_format = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
+ values = ["test@example.org"]
+ }
+}
+```
+
+## Argument Reference
+
+* `group_attribute_name` - The name of the attribute containing the information of which group the connecting users belong to.
+* `metadata` - The SAML xml metadata of the Identity Provider to federate to.
+* `disable_users` - Whether account users should still be usable as a login method or not (optional, defaults to true).
+* `requested_attributes` A SAML 2.0 requested attribute that should be added to SAML requests when using this provider (optional).
+ * `is_required` Expresses that this RequestedAttribute is mandatory.
+ * `name` Name of the SAML RequestedAttribute.
+ * `name_format` NameFormat of the SAML RequestedAttribute.
+ * `values` List of AttributeValues allowed for this RequestedAttribute
+
+## Attributes Reference
+
+* `creation` - Creation date of the SAML Federation.
+* `last_update` - Date of the last update of the SAML Federation.