Repository: ovh/terraform-provider-ovh

Issue:
[FEATURE] Add support for password rotation on ovh_cloud_project_user via rotate_when_changed
### Description

Currently, the ovh_cloud_project_user resource does not support a mechanism for automatic password rotation triggered by external conditions. This is important for adhering to security best practices and automating credential lifecycle management.

Adding support for an argument similar to rotate_when_changed, as implemented in the AzureAD provider, would enable password rotation through external triggers such as a timestamp, improving automation and compliance with security standards. ([rotate_when_changed – azuread_service_principal_password](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal_password#rotate_when_changed-1))

### Affected Resource(s) and/or Data Source(s)

* ovh_cloud_project_user


### Potential Terraform Configuration

```hcl
resource "ovh_cloud_project_user" "example" {
  service_name = var.project_Id
  description  = "Service User created by Terraform"

  rotate_when_changed = {
    last_rotation = var.last_rotation
  }
} 
```

A map of arbitrary key/value pairs that will force recreation of the password when they change, enabling password rotation based on external conditions such as a rotating timestamp.

### Additional context

This follows the same pattern used in other Terraform providers. For example, the AzureAD provider supports the rotate_when_changed argument for azuread_service_principal_password.

Implementing this in the ovh_cloud_project_user resource would allow users to safely automate password management through Terraform, without requiring manual deletion or external scripting. It enhances security hygiene and simplifies integration into CI/CD pipelines.


Discussion:

Solution patch:
diff --git a/.cds/terraform-provider-ovh-check.pip.yml b/.cds/terraform-provider-ovh-check.pip.yml
index 2b5fc8bb..3b9750ba 100644
--- a/.cds/terraform-provider-ovh-check.pip.yml
+++ b/.cds/terraform-provider-ovh-check.pip.yml
@@ -15,4 +15,4 @@ jobs:
       make test
       make vet
   requirements:
-  - model: shared.infra/Go-official-1.22-bookworm
+  - model: shared.infra/Go-official-1.23-bookworm
diff --git a/.cds/terraform-provider-ovh-run-sweepers.pip.yml b/.cds/terraform-provider-ovh-run-sweepers.pip.yml
index c47f9c7b..9e28a301 100644
--- a/.cds/terraform-provider-ovh-run-sweepers.pip.yml
+++ b/.cds/terraform-provider-ovh-run-sweepers.pip.yml
@@ -24,4 +24,4 @@ jobs:
       TF_ACC=1 go test ./ovh/ -v -sweep=1
 
   requirements:
-  - model: shared.infra/Go-official-1.22-bookworm
+  - model: shared.infra/Go-official-1.23-bookworm
diff --git a/.cds/terraform-provider-ovh-testacc.pip.yml b/.cds/terraform-provider-ovh-testacc.pip.yml
index 66c55bf3..e84a76d2 100644
--- a/.cds/terraform-provider-ovh-testacc.pip.yml
+++ b/.cds/terraform-provider-ovh-testacc.pip.yml
@@ -37,4 +37,4 @@ jobs:
       make testacc TESTARGS="-parallel=1 ${CDS_PIP_TESTARGS}"
 
   requirements:
-  - model: shared.infra/Go-official-1.22-bookworm
+  - model: shared.infra/Go-official-1.23-bookworm
diff --git a/docs/data-sources/cloud_project_flavor.md b/docs/data-sources/cloud_project_flavor.md
new file mode 100644
index 00000000..2e8efba0
--- /dev/null
+++ b/docs/data-sources/cloud_project_flavor.md
@@ -0,0 +1,60 @@
+---
+subcategory : "Cloud Project"
+---
+
+# ovh_cloud_project_flavor
+
+Retrieves the given flavor in a public cloud project.
+
+## Example Usage
+
+```terraform
+data "ovh_cloud_project_flavor" "flavor" {
+  service_name = "YYYY"
+  id           = "XXXX"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+- `id` (String) Flavor ID
+
+### Read-Only
+
+- `available` (Boolean) Available in stock
+- `capabilities` (Attributes List) Capabilities of the flavor (see [below for nested schema](#nestedatt--capabilities))
+- `disk` (Number) Number of disks
+- `id` (String) Flavor id
+- `inbound_bandwidth` (Number) Max capacity of inbound traffic in Mbit/s
+- `name` (String) Flavor name
+- `os_type` (String) OS to install on
+- `outbound_bandwidth` (Number) Max capacity of outbound traffic in Mbit/s
+- `plan_codes` (Attributes) Plan codes to order instances (see [below for nested schema](#nestedatt--plan_codes))
+- `quota` (Number) Number instance you can spawn with your actual quota
+- `ram` (Number) Ram quantity (Gio)
+- `region` (String) Flavor region
+- `type` (String) Flavor type
+- `vcpus` (Number) Number of VCPUs
+
+<a id="nestedatt--capabilities"></a>
+
+### Nested Schema for `capabilities`
+
+Read-Only:
+
+- `enabled` (Boolean) Is the capability enabled
+- `name` (String) Name of the capability
+
+<a id="nestedatt--plan_codes"></a>
+
+### Nested Schema for `plan_codes`
+
+Read-Only:
+
+- `hourly` (String) Plan code to order hourly instance
+- `monthly` (String) Plan code to order monthly instance
diff --git a/docs/data-sources/cloud_project_flavors.md b/docs/data-sources/cloud_project_flavors.md
index 5de23196..aeb5d39d 100644
--- a/docs/data-sources/cloud_project_flavors.md
+++ b/docs/data-sources/cloud_project_flavors.md
@@ -25,6 +25,7 @@ data "ovh_cloud_project_flavors" "flavors" {
 ### Optional
 
 - `region` (String) Flavor region
+- `name_filter` (String) Filter flavors using the given name (strict equality, e.g. b2-7)
 
 ### Read-Only
 
diff --git a/docs/data-sources/cloud_project_rancher_capabilities_plan.md b/docs/data-sources/cloud_project_rancher_capabilities_plan.md
new file mode 100644
index 00000000..2d3d5e6c
--- /dev/null
+++ b/docs/data-sources/cloud_project_rancher_capabilities_plan.md
@@ -0,0 +1,39 @@
+---
+subcategory: "Managed Rancher Service (MRS)"
+description: "List available and current plans for the given managed Rancher service"
+---
+
+# ovh_cloud_project_rancher_capabilities_plan (Data Source)
+
+List available and current plans for the given managed Rancher service
+
+## Example Usage
+
+```terraform
+data "ovh_cloud_project_rancher_capabilities_plan" "plans" {
+  project_id = "XXX"
+  rancher_id = "YYY"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `project_id` (String) Project ID
+- `rancher_id` (String) Rancher ID
+
+### Read-Only
+
+- `plans` (Attributes Set) (see [below for nested schema](#nestedatt--plans))
+
+<a id="nestedatt--plans"></a>
+### Nested Schema for `plans`
+
+Read-Only:
+
+- `cause` (String) Cause for an unavailability
+- `message` (String) Human-readable description of the unavailability cause
+- `name` (String) Name of the plan
+- `status` (String) Status of the plan
diff --git a/docs/data-sources/cloud_project_rancher_capabilities_version.md b/docs/data-sources/cloud_project_rancher_capabilities_version.md
new file mode 100644
index 00000000..971a07d7
--- /dev/null
+++ b/docs/data-sources/cloud_project_rancher_capabilities_version.md
@@ -0,0 +1,40 @@
+---
+subcategory: "Managed Rancher Service (MRS)"
+description: "List available and current versions for the given managed Rancher service"
+---
+
+# ovh_cloud_project_rancher_capabilities_version (Data Source)
+
+List available and current versions for the given managed Rancher service
+
+## Example Usage
+
+```terraform
+data "ovh_cloud_project_rancher_capabilities_version" "versions" {
+  project_id = "XXX"
+  rancher_id = "YYY"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `project_id` (String) Project ID
+- `rancher_id` (String) Rancher ID
+
+### Read-Only
+
+- `versions` (Attributes Set) (see [below for nested schema](#nestedatt--versions))
+
+<a id="nestedatt--versions"></a>
+### Nested Schema for `versions`
+
+Read-Only:
+
+- `cause` (String) Cause for an unavailability
+- `changelog_url` (String) Changelog URL of the version
+- `message` (String) Human-readable description of the unavailability cause
+- `name` (String) Name of the version
+- `status` (String) Status of the version
diff --git a/docs/data-sources/cloud_project_region.md b/docs/data-sources/cloud_project_region.md
index 2574950f..a5061ecb 100644
--- a/docs/data-sources/cloud_project_region.md
+++ b/docs/data-sources/cloud_project_region.md
@@ -18,15 +18,20 @@ data "ovh_cloud_project_region" "GRA1" {
 ## Argument Reference
 
 * `service_name` - (Required) The id of the public cloud project. If omitted, the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used.
-
-* `name` - (Required) The name of the region associated with the public cloud project.
+* `name`         - (Required) The name of the region associated with the public cloud project.
 
 ## Attributes Reference
 
 `id` is set to the ID of the project concatenated with the name of the region. In addition, the following attributes are exported:
 
-* `continent_code` - the code of the geographic continent the region is running. E.g.: EU for Europe, US for America...
+* `type`                - Region type (localzone | region | region-3-az)
+* `status`              - Openstack region status (DOWN | MAINTENANCE | UP)
+* `continent_code`      - The code of the geographic continent the region is running. E.g.: EU for Europe, US for America...
+* `country_code`        - Region country code
 * `datacenter_location` - The location code of the datacenter. E.g.: "GRA", meaning Gravelines, for region "GRA1"
-* `services` - The list of public cloud services running within the region
-  * `name` - the name of the public cloud service
-  * `status` - the status of the service
+* `availability_zones`  - Availability zones of the region
+* `ip_countries`        - Allowed countries for failover IP
+* `services`            - The list of public cloud services running within the region
+  * `name`              - the name of the public cloud service
+  * `status`            - the status of the service
+  * `endpoint`          - Endpoint URL
diff --git a/docs/data-sources/cloud_project_ssh_key.md b/docs/data-sources/cloud_project_ssh_key.md
new file mode 100644
index 00000000..d3d9fb2c
--- /dev/null
+++ b/docs/data-sources/cloud_project_ssh_key.md
@@ -0,0 +1,32 @@
+---
+subcategory: "Cloud Project"
+description: "Get SSH key"
+---
+
+# ovh_cloud_project_ssh_key (Data Source)
+
+Retrieve information about a SSH key.
+
+## Example Usage
+
+```terraform
+data "ovh_cloud_project_ssh_key" "key" {
+  service_name = "XXX"
+  id           = "YYY"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `id` (String) SSH key ID
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `finger_print` (String) SSH key fingerprint
+- `name` (String) SSH key name
+- `public_key` (String) SSH public key
+- `regions` (List of String) SSH key regions
diff --git a/docs/data-sources/cloud_project_ssh_keys.md b/docs/data-sources/cloud_project_ssh_keys.md
new file mode 100644
index 00000000..3b56a182
--- /dev/null
+++ b/docs/data-sources/cloud_project_ssh_keys.md
@@ -0,0 +1,41 @@
+---
+subcategory: "Cloud Project"
+description: "Get SSH keys"
+---
+
+# ovh_cloud_project_ssh_keys (Data Source)
+
+Get SSH keys in a Public Cloud project.
+
+## Example Usage
+
+```terraform
+data "ovh_cloud_project_ssh_keys" "keys" {
+  service_name = "XXX"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Optional
+
+- `region` (String) Region
+
+### Read-Only
+
+- `ssh_keys` (Attributes Set) (see [below for nested schema](#nestedatt--ssh_keys))
+
+<a id="nestedatt--ssh_keys"></a>
+### Nested Schema for `ssh_keys`
+
+Read-Only:
+
+- `id` (String) SSH key ID
+- `name` (String) SSH key name
+- `public_key` (String) SSH public key
+- `regions` (List of String) SSH key regions
diff --git a/docs/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md b/docs/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md
new file mode 100644
index 00000000..a88a46bc
--- /dev/null
+++ b/docs/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md
@@ -0,0 +1,41 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pop_datacenter_extras (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Datacenter Extra configuration
+
+## Example Usage
+
+```terraform
+data "ovh_ovhcloud_connect_config_pop_datacenter_extras" "extra_cfgs" {
+  service_name = "XXX"
+  config_pop_id = "YYY"
+  config_datacenter_id = "ZZZ"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `config_datacenter_id` (Number) ID of the Datacenter Configuration
+- `config_pop_id` (Number) ID of the Pop Configuration
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `extra_configs` (Attributes Set) (see [below for nested schema](#nestedatt--extra_configs))
+
+<a id="nestedatt--extra_configs"></a>
+### Nested Schema for `extra_configs`
+
+- `id` (Number) ID of the extra configuration
+- `bgp_neighbor_area` (Number) BGP AS number
+- `bgp_neighbor_ip` (String) Router IP for BGP
+- `next_hop` (String) Static route next hop
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Static route ip
+- `type` (String) Type of the configuration
diff --git a/docs/data-sources/ovhcloud_connect_config_pop_datacenters.md b/docs/data-sources/ovhcloud_connect_config_pop_datacenters.md
new file mode 100644
index 00000000..25babdc5
--- /dev/null
+++ b/docs/data-sources/ovhcloud_connect_config_pop_datacenters.md
@@ -0,0 +1,37 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pop_datacenters (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Datacenter configuration
+
+## Example Usage
+
+```terraform
+data "ovh_ovhcloud_connect_config_pop_datacenters" "datacenter_cfgs" {
+  service_name = "XXX"
+  config_pop_id = "YYY"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `config_pop_id` (Number) ID of the Pop Configuration
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `datacenter_configs` (Attributes Set) (see [below for nested schema](#nestedatt--datacenter_configs))
+
+<a id="nestedatt--datacenter_configs"></a>
+### Nested Schema for `datacenter_configs`
+
+- `id` (Number) ID of the Datacenter configuration
+- `datacenter_id` (Number) Datacenter ID
+- `ovh_bgp_area` (Number) OVH Private AS
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Subnet should be a /28 min
diff --git a/docs/data-sources/ovhcloud_connect_config_pops.md b/docs/data-sources/ovhcloud_connect_config_pops.md
new file mode 100644
index 00000000..1ca58884
--- /dev/null
+++ b/docs/data-sources/ovhcloud_connect_config_pops.md
@@ -0,0 +1,37 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pops (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Pop configuration
+
+## Example Usage
+
+```terraform
+data "ovh_ovhcloud_connect_config_pops" "pop_cfgs" {
+  service_name = "XXX"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `pop_configs` (Attributes Set) (see [below for nested schema](#nestedatt--pop_configs))
+
+<a id="nestedatt--pop_configs"></a>
+### Nested Schema for `pop_configs`
+
+- `id` (Number) ID of the Pop Configuration
+- `customer_bgp_area` (Number) Customer Private AS
+- `interface_id` (Number) ID of the interface
+- `ovh_bgp_area` (Number) OVH Private AS
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Subnet should be a /30, first IP for OVH, second IP for customer
+- `type` (String) Type of the pop configuration
diff --git a/docs/data-sources/ovhcloud_connect_datacenters.md b/docs/data-sources/ovhcloud_connect_datacenters.md
new file mode 100644
index 00000000..9def499d
--- /dev/null
+++ b/docs/data-sources/ovhcloud_connect_datacenters.md
@@ -0,0 +1,41 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_datacenters (Data Source)
+
+Use this data source to retrieve information about Datacenters for an Ovhcloud Connect product.
+
+## Example Usage
+
+```terraform
+data "ovh_ovhcloud_connect_datacenters" "dcs" {
+  service_name = "XXX"
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `datacenters` (Attributes Set) (see [below for nested schema](#nestedatt--datacenters))
+
+<a id="nestedatt--datacenters"></a>
+### Nested Schema for `datacenters`
+
+Required:
+
+- `id` (Number) Id
+
+Read-Only:
+
+- `available` (Boolean) Get availability to add new configuration on it
+- `name` (String) name of the datacenter
+- `region` (String) region of the datacenter
+- `region_type` (String) region type of the datacenter
diff --git a/docs/index.md b/docs/index.md
index 47f73819..8dd5e8fc 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -186,6 +186,14 @@ The following arguments are supported:
 
 * `consumer_key` - (Optional) The API Consumer key. If omitted, the `OVH_CONSUMER_KEY` environment variable is used.
 
+* `client_id` - (Optional) The OAuth2 client ID. If omitted, the `OVH_CLIENT_ID` environment variable is used.
+
+* `client_id` - (Optional) The OAuth2 client secret. If omitted, the `OVH_CLIENT_SECRET` environment variable is used.
+
+* `access_token` - (Optional) The OVH API Access Token. If omitted, the `OVH_ACCESS_TOKEN` environment variable is used.
+
+* `user_agent_extra` - (Optional) Extra information to append at the end of the user-agent used when making API calls.
+
 ## Terraform State storage in an OVHcloud Object Storage (S3 compatibility)
 
 In order to store your Terraform states on a High Performance (S3) OVHcloud Object Storage, please follow the [guide](https://help.ovhcloud.com/csm/en-public-cloud-compute-terraform-high-perf-object-storage-backend-state?id=kb_article_view&sysparm_article=KB0051345).
diff --git a/docs/resources/cloud_project_user.md b/docs/resources/cloud_project_user.md
index fa16fa3a..0e8a7554 100644
--- a/docs/resources/cloud_project_user.md
+++ b/docs/resources/cloud_project_user.md
@@ -14,6 +14,18 @@ resource "ovh_cloud_project_user" "user1" {
 }
 ```
 
+```terraform
+# Change password_reset with the datetime each time you want to reset the password to trigger an update
+resource "ovh_cloud_project_user" "user_with_rotation" {
+  service_name   = "XXX"
+  description    = "Service User created by Terraform with password rotation"
+  password_reset = "2025-04-30"
+}
+```
+
+-> **NOTE** To reset password of the user previously created, update the `password_reset` attribute. Use the `terraform refresh` command after executing `terraform apply` to update the output with the new password. This attribute can be an arbitrary string but we recommend 2 formats:
+- a datetime to keep a trace of the last reset
+- a md5 of other variables to automatically trigger it based on this variable update
 ## Argument Reference
 
 The following arguments are supported:
@@ -25,7 +37,8 @@ The following arguments are supported:
 * `role_name` - The name of a role. See `role_names`.
 
 * `role_names` - A list of role names. Values can be:
-  - administrator,
+  - admin
+  - administrator
   - ai_training_operator
   - ai_training_read
   - authentication
@@ -33,11 +46,16 @@ The following arguments are supported:
   - compute_operator
   - image_operator
   - infrastructure_supervisor
+  - key-manager_operator
+  - key-manager_read
+  - load-balancer_operator
   - network_operator
   - network_security_operator
   - objectstore_operator
   - volume_operator
 
+* `password_reset` - (Optional) Arbitrary string to change to trigger a password update. Use the `terraform refresh` command after executing `terraform apply` to update the output with the new password.
+
 ## Attributes Reference
 
 The following attributes are exported:
@@ -46,6 +64,7 @@ The following attributes are exported:
 * `description` - See Argument Reference above.
 * `openstack_rc` - a convenient map representing an openstack_rc file. Note: no password nor sensitive token is set in this map.
 * `password` - (Sensitive) the password generated for the user. The password can be used with the Openstack API. This attribute is sensitive and will only be retrieve once during creation.
+* `password_reset` - See Argument Reference above.
 * `roles` - A list of roles associated with the user.
   * `description` - description of the role
   * `id` - id of the role
diff --git a/examples/data-sources/cloud_project_flavor/example_1.tf b/examples/data-sources/cloud_project_flavor/example_1.tf
new file mode 100644
index 00000000..9ed2575c
--- /dev/null
+++ b/examples/data-sources/cloud_project_flavor/example_1.tf
@@ -0,0 +1,4 @@
+data "ovh_cloud_project_flavor" "flavor" {
+  service_name = "YYYY"
+  id           = "XXXX"
+}
diff --git a/examples/data-sources/cloud_project_rancher_capabilities_plan/data-source.tf b/examples/data-sources/cloud_project_rancher_capabilities_plan/data-source.tf
new file mode 100644
index 00000000..1a021050
--- /dev/null
+++ b/examples/data-sources/cloud_project_rancher_capabilities_plan/data-source.tf
@@ -0,0 +1,4 @@
+data "ovh_cloud_project_rancher_capabilities_plan" "plans" {
+  project_id = "XXX"
+  rancher_id = "YYY"
+}
\ No newline at end of file
diff --git a/examples/data-sources/cloud_project_rancher_capabilities_version/data-source.tf b/examples/data-sources/cloud_project_rancher_capabilities_version/data-source.tf
new file mode 100644
index 00000000..3aa7a248
--- /dev/null
+++ b/examples/data-sources/cloud_project_rancher_capabilities_version/data-source.tf
@@ -0,0 +1,4 @@
+data "ovh_cloud_project_rancher_capabilities_version" "versions" {
+  project_id = "XXX"
+  rancher_id = "YYY"
+}
\ No newline at end of file
diff --git a/examples/data-sources/cloud_project_ssh_key/data-source.tf b/examples/data-sources/cloud_project_ssh_key/data-source.tf
new file mode 100644
index 00000000..fe457d10
--- /dev/null
+++ b/examples/data-sources/cloud_project_ssh_key/data-source.tf
@@ -0,0 +1,4 @@
+data "ovh_cloud_project_ssh_key" "key" {
+  service_name = "XXX"
+  id           = "YYY"
+}
\ No newline at end of file
diff --git a/examples/data-sources/cloud_project_ssh_keys/data-source.tf b/examples/data-sources/cloud_project_ssh_keys/data-source.tf
new file mode 100644
index 00000000..e0db49d6
--- /dev/null
+++ b/examples/data-sources/cloud_project_ssh_keys/data-source.tf
@@ -0,0 +1,3 @@
+data "ovh_cloud_project_ssh_keys" "keys" {
+  service_name = "XXX"
+}
\ No newline at end of file
diff --git a/examples/data-sources/ovhcloud_connect_config_pop_datacenter_extras/data-source.tf b/examples/data-sources/ovhcloud_connect_config_pop_datacenter_extras/data-source.tf
new file mode 100644
index 00000000..c221a524
--- /dev/null
+++ b/examples/data-sources/ovhcloud_connect_config_pop_datacenter_extras/data-source.tf
@@ -0,0 +1,5 @@
+data "ovh_ovhcloud_connect_config_pop_datacenter_extras" "extra_cfgs" {
+  service_name = "XXX"
+  config_pop_id = "YYY"
+  config_datacenter_id = "ZZZ"
+}
diff --git a/examples/data-sources/ovhcloud_connect_config_pop_datacenters/data-source.tf b/examples/data-sources/ovhcloud_connect_config_pop_datacenters/data-source.tf
new file mode 100644
index 00000000..8582b3ad
--- /dev/null
+++ b/examples/data-sources/ovhcloud_connect_config_pop_datacenters/data-source.tf
@@ -0,0 +1,4 @@
+data "ovh_ovhcloud_connect_config_pop_datacenters" "datacenter_cfgs" {
+  service_name = "XXX"
+  config_pop_id = "YYY"
+}
diff --git a/examples/data-sources/ovhcloud_connect_config_pops/data-source.tf b/examples/data-sources/ovhcloud_connect_config_pops/data-source.tf
new file mode 100644
index 00000000..0b281035
--- /dev/null
+++ b/examples/data-sources/ovhcloud_connect_config_pops/data-source.tf
@@ -0,0 +1,3 @@
+data "ovh_ovhcloud_connect_config_pops" "pop_cfgs" {
+  service_name = "XXX"
+}
diff --git a/examples/data-sources/ovhcloud_connect_datacenters/data-source.tf b/examples/data-sources/ovhcloud_connect_datacenters/data-source.tf
new file mode 100644
index 00000000..20125e8a
--- /dev/null
+++ b/examples/data-sources/ovhcloud_connect_datacenters/data-source.tf
@@ -0,0 +1,3 @@
+data "ovh_ovhcloud_connect_datacenters" "dcs" {
+  service_name = "XXX"
+}
diff --git a/examples/resources/cloud_project_user/example_2.tf b/examples/resources/cloud_project_user/example_2.tf
new file mode 100644
index 00000000..7fea80f8
--- /dev/null
+++ b/examples/resources/cloud_project_user/example_2.tf
@@ -0,0 +1,7 @@
+# Change password_reset with the datetime each time you want to reset the password to trigger an update
+resource "ovh_cloud_project_user" "user_with_rotation" {
+  service_name   = "XXX"
+  description    = "Service User created by Terraform with password rotation"
+  password_reset = "2025-04-30"
+}
+
diff --git a/ovh/config.go b/ovh/config.go
index 496176f3..cc3cedc6 100644
--- a/ovh/config.go
+++ b/ovh/config.go
@@ -29,6 +29,9 @@ type Config struct {
 	ClientID     string
 	ClientSecret string
 
+	// Extra user-agent information
+	UserAgentExtra string
+
 	OVHClient     *ovh.Client
 	authenticated bool
 	authFailed    error
@@ -74,6 +77,9 @@ func clientDefault(c *Config) (*ovh.Client, error) {
 	}
 
 	client.UserAgent = "Terraform/" + providerVersion + "/" + providerCommit
+	if c.UserAgentExtra != "" {
+		client.UserAgent += " - " + c.UserAgentExtra
+	}
 
 	return client, nil
 }
diff --git a/ovh/data_cloud_project_flavor.go b/ovh/data_cloud_project_flavor.go
new file mode 100644
index 00000000..8bf7a490
--- /dev/null
+++ b/ovh/data_cloud_project_flavor.go
@@ -0,0 +1,71 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+var _ datasource.DataSourceWithConfigure = (*cloudProjectFlavorDataSource)(nil)
+
+func NewCloudProjectFlavorDataSource() datasource.DataSource {
+	return &cloudProjectFlavorDataSource{}
+}
+
+type cloudProjectFlavorDataSource struct {
+	config *Config
+}
+
+func (d *cloudProjectFlavorDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_cloud_project_flavor"
+}
+
+func (d *cloudProjectFlavorDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *cloudProjectFlavorDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = CloudProjectFlavorDataSourceSchema(ctx)
+}
+
+func (d *cloudProjectFlavorDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data CloudProjectFlavorModel
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	endpoint := "/cloud/project/" + url.PathEscape(data.ServiceName.ValueString()) + "/flavor/" + url.PathEscape(data.Id.ValueString())
+
+	var flavor CloudProjectFlavorModel
+	if err := d.config.OVHClient.Get(endpoint, &flavor); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	flavor.ServiceName = data.ServiceName
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &flavor)...)
+}
diff --git a/ovh/data_cloud_project_flavor_gen.go b/ovh/data_cloud_project_flavor_gen.go
new file mode 100644
index 00000000..25e559e3
--- /dev/null
+++ b/ovh/data_cloud_project_flavor_gen.go
@@ -0,0 +1,168 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+func CloudProjectFlavorDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"available": schema.BoolAttribute{
+			CustomType:          ovhtypes.TfBoolType{},
+			Computed:            true,
+			Description:         "Available in stock",
+			MarkdownDescription: "Available in stock",
+		},
+		"capabilities": schema.ListNestedAttribute{
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"enabled": schema.BoolAttribute{
+						CustomType:          ovhtypes.TfBoolType{},
+						Computed:            true,
+						Description:         "Is the capability enabled",
+						MarkdownDescription: "Is the capability enabled",
+					},
+					"name": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Name of the capability",
+						MarkdownDescription: "Name of the capability",
+					},
+				},
+				CustomType: CloudProjectFlavorCapabilitiesType{
+					ObjectType: types.ObjectType{
+						AttrTypes: CloudProjectFlavorCapabilitiesValue{}.AttributeTypes(ctx),
+					},
+				},
+			},
+			CustomType:          ovhtypes.NewTfListNestedType[CloudProjectFlavorCapabilitiesValue](ctx),
+			Computed:            true,
+			Description:         "Capabilities of the flavor",
+			MarkdownDescription: "Capabilities of the flavor",
+		},
+		"disk": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Number of disks",
+			MarkdownDescription: "Number of disks",
+		},
+
+		"inbound_bandwidth": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Max capacity of inbound traffic in Mbit/s",
+			MarkdownDescription: "Max capacity of inbound traffic in Mbit/s",
+		},
+		"name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "Flavor name",
+			MarkdownDescription: "Flavor name",
+		},
+		"os_type": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "OS to install on",
+			MarkdownDescription: "OS to install on",
+		},
+		"outbound_bandwidth": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Max capacity of outbound traffic in Mbit/s",
+			MarkdownDescription: "Max capacity of outbound traffic in Mbit/s",
+		},
+		"plan_codes": schema.SingleNestedAttribute{
+			Attributes: map[string]schema.Attribute{
+				"hourly": schema.StringAttribute{
+					CustomType:          ovhtypes.TfStringType{},
+					Computed:            true,
+					Description:         "Plan code to order hourly instance",
+					MarkdownDescription: "Plan code to order hourly instance",
+				},
+				"monthly": schema.StringAttribute{
+					CustomType:          ovhtypes.TfStringType{},
+					Computed:            true,
+					Description:         "Plan code to order monthly instance",
+					MarkdownDescription: "Plan code to order monthly instance",
+				},
+			},
+			CustomType: CloudProjectFlavorPlanCodesType{
+				ObjectType: types.ObjectType{
+					AttrTypes: CloudProjectFlavorPlanCodesValue{}.AttributeTypes(ctx),
+				},
+			},
+			Computed:            true,
+			Description:         "Plan codes to order instances",
+			MarkdownDescription: "Plan codes to order instances",
+		},
+		"quota": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Number instance you can spawn with your actual quota",
+			MarkdownDescription: "Number instance you can spawn with your actual quota",
+		},
+		"ram": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Ram quantity (Gio)",
+			MarkdownDescription: "Ram quantity (Gio)",
+		},
+		"region": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "Flavor region",
+			MarkdownDescription: "Flavor region",
+		},
+		"type": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "Flavor type",
+			MarkdownDescription: "Flavor type",
+		},
+		"vcpus": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Computed:            true,
+			Description:         "Number of VCPUs",
+			MarkdownDescription: "Number of VCPUs",
+		},
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Flavor id",
+			MarkdownDescription: "Flavor id",
+		},
+	}
+
+	return schema.Schema{
+		Attributes: attrs,
+	}
+}
+
+type CloudProjectFlavorModel struct {
+	Available              ovhtypes.TfBoolValue                                            `tfsdk:"available" json:"available"`
+	Capabilities           ovhtypes.TfListNestedValue[CloudProjectFlavorCapabilitiesValue] `tfsdk:"capabilities" json:"capabilities"`
+	Disk                   ovhtypes.TfInt64Value                                           `tfsdk:"disk" json:"disk"`
+	Id                     ovhtypes.TfStringValue                                          `tfsdk:"id" json:"id"`
+	InboundBandwidth       ovhtypes.TfInt64Value                                           `tfsdk:"inbound_bandwidth" json:"inboundBandwidth"`
+	Name                   ovhtypes.TfStringValue                                          `tfsdk:"name" json:"name"`
+	OsType                 ovhtypes.TfStringValue                                          `tfsdk:"os_type" json:"osType"`
+	OutboundBandwidth      ovhtypes.TfInt64Value                                           `tfsdk:"outbound_bandwidth" json:"outboundBandwidth"`
+	PlanCodes              CloudProjectFlavorPlanCodesValue                                `tfsdk:"plan_codes" json:"planCodes"`
+	Quota                  ovhtypes.TfInt64Value                                           `tfsdk:"quota" json:"quota"`
+	Ram                    ovhtypes.TfInt64Value                                           `tfsdk:"ram" json:"ram"`
+	Region                 ovhtypes.TfStringValue                                          `tfsdk:"region" json:"region"`
+	CloudProjectFlavorType ovhtypes.TfStringValue                                          `tfsdk:"type" json:"type"`
+	Vcpus                  ovhtypes.TfInt64Value                                           `tfsdk:"vcpus" json:"vcpus"`
+	ServiceName            ovhtypes.TfStringValue                                          `tfsdk:"service_name" json:"serviceName"`
+}
diff --git a/ovh/data_cloud_project_flavors.go b/ovh/data_cloud_project_flavors.go
index 378e292c..df6a77f3 100644
--- a/ovh/data_cloud_project_flavors.go
+++ b/ovh/data_cloud_project_flavors.go
@@ -74,7 +74,9 @@ func (d *cloudProjectFlavorsDataSource) Read(ctx context.Context, req datasource
 
 	var b []attr.Value
 	for _, a := range arr {
-		b = append(b, a)
+		if data.NameFilter.IsNull() || data.NameFilter.IsUnknown() || a.Name.Equal(data.NameFilter) {
+			b = append(b, a)
+		}
 	}
 
 	data.Flavors = ovhtypes.TfListNestedValue[CloudProjectFlavorValue]{
diff --git a/ovh/data_cloud_project_flavors_gen.go b/ovh/data_cloud_project_flavors_gen.go
index 603bedbd..9abd9f06 100644
--- a/ovh/data_cloud_project_flavors_gen.go
+++ b/ovh/data_cloud_project_flavors_gen.go
@@ -162,6 +162,11 @@ func CloudProjectFlavorsDataSourceSchema(ctx context.Context) schema.Schema {
 			Description:         "Flavor region",
 			MarkdownDescription: "Flavor region",
 		},
+		"name_filter": schema.StringAttribute{
+			CustomType:  ovhtypes.TfStringType{},
+			Optional:    true,
+			Description: "Filter results on the given flavor name",
+		},
 		"service_name": schema.StringAttribute{
 			CustomType:          ovhtypes.TfStringType{},
 			Required:            true,
@@ -178,6 +183,7 @@ func CloudProjectFlavorsDataSourceSchema(ctx context.Context) schema.Schema {
 type CloudProjectFlavorsModel struct {
 	Flavors     ovhtypes.TfListNestedValue[CloudProjectFlavorValue] `tfsdk:"flavors" json:"cloudProjectFlavor"`
 	Region      ovhtypes.TfStringValue                              `tfsdk:"region" json:"region"`
+	NameFilter  ovhtypes.TfStringValue                              `tfsdk:"name_filter" json:"_"`
 	ServiceName ovhtypes.TfStringValue                              `tfsdk:"service_name" json:"serviceName"`
 }
 
@@ -190,6 +196,10 @@ func (v *CloudProjectFlavorsModel) MergeWith(other *CloudProjectFlavorsModel) {
 		v.Region = other.Region
 	}
 
+	if (v.NameFilter.IsUnknown() || v.NameFilter.IsNull()) && !other.NameFilter.IsUnknown() {
+		v.NameFilter = other.NameFilter
+	}
+
 	if (v.ServiceName.IsUnknown() || v.ServiceName.IsNull()) && !other.ServiceName.IsUnknown() {
 		v.ServiceName = other.ServiceName
 	}
diff --git a/ovh/data_cloud_project_rancher_capabilities_plan.go b/ovh/data_cloud_project_rancher_capabilities_plan.go
new file mode 100644
index 00000000..9f12ce65
--- /dev/null
+++ b/ovh/data_cloud_project_rancher_capabilities_plan.go
@@ -0,0 +1,69 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+var _ datasource.DataSourceWithConfigure = (*cloudProjectRancherCapabilitiesPlanDataSource)(nil)
+
+func NewCloudProjectRancherCapabilitiesPlanDataSource() datasource.DataSource {
+	return &cloudProjectRancherCapabilitiesPlanDataSource{}
+}
+
+type cloudProjectRancherCapabilitiesPlanDataSource struct {
+	config *Config
+}
+
+func (d *cloudProjectRancherCapabilitiesPlanDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_cloud_project_rancher_capabilities_plan"
+}
+
+func (d *cloudProjectRancherCapabilitiesPlanDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *cloudProjectRancherCapabilitiesPlanDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = CloudProjectRancherCapabilitiesPlanDataSourceSchema(ctx)
+}
+
+func (d *cloudProjectRancherCapabilitiesPlanDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data CloudProjectRancherCapabilitiesPlanModel
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/v2/publicCloud/project/" + url.PathEscape(data.ProjectId.ValueString()) + "/rancher/" + url.PathEscape(data.RancherId.ValueString()) + "/capabilities/plan"
+
+	if err := d.config.OVHClient.Get(endpoint, &data.Plans); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_cloud_project_rancher_capabilities_plan_gen.go b/ovh/data_cloud_project_rancher_capabilities_plan_gen.go
new file mode 100644
index 00000000..c1fa80cb
--- /dev/null
+++ b/ovh/data_cloud_project_rancher_capabilities_plan_gen.go
@@ -0,0 +1,626 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/hashicorp/terraform-plugin-framework/attr"
+	"github.com/hashicorp/terraform-plugin-framework/diag"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+	"github.com/hashicorp/terraform-plugin-go/tftypes"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+	"strings"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func CloudProjectRancherCapabilitiesPlanDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"plans": schema.SetNestedAttribute{
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"cause": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Cause for an unavailability",
+						MarkdownDescription: "Cause for an unavailability",
+					},
+					"message": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Human-readable description of the unavailability cause",
+						MarkdownDescription: "Human-readable description of the unavailability cause",
+					},
+					"name": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Name of the plan",
+						MarkdownDescription: "Name of the plan",
+					},
+					"status": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Status of the plan",
+						MarkdownDescription: "Status of the plan",
+					},
+				},
+				CustomType: CloudProjectRancherCapabilitiesPlanType{
+					ObjectType: types.ObjectType{
+						AttrTypes: CloudProjectRancherCapabilitiesPlanValue{}.AttributeTypes(ctx),
+					},
+				},
+			},
+			CustomType: ovhtypes.NewTfListNestedType[CloudProjectRancherCapabilitiesPlanValue](ctx),
+			Computed:   true,
+		},
+		"project_id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Project ID",
+			MarkdownDescription: "Project ID",
+		},
+		"rancher_id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Rancher ID",
+			MarkdownDescription: "Rancher ID",
+		},
+	}
+
+	return schema.Schema{
+		Description: "List available and current plans for the given managed Rancher service",
+		Attributes:  attrs,
+	}
+}
+
+type CloudProjectRancherCapabilitiesPlanModel struct {
+	Plans     ovhtypes.TfListNestedValue[CloudProjectRancherCapabilitiesPlanValue] `tfsdk:"plans" json:"plans"`
+	ProjectId ovhtypes.TfStringValue                                               `tfsdk:"project_id" json:"projectId"`
+	RancherId ovhtypes.TfStringValue                                               `tfsdk:"rancher_id" json:"rancherId"`
+}
+
+func (v *CloudProjectRancherCapabilitiesPlanModel) MergeWith(other *CloudProjectRancherCapabilitiesPlanModel) {
+
+	if (v.Plans.IsUnknown() || v.Plans.IsNull()) && !other.Plans.IsUnknown() {
+		v.Plans = other.Plans
+	}
+
+	if (v.ProjectId.IsUnknown() || v.ProjectId.IsNull()) && !other.ProjectId.IsUnknown() {
+		v.ProjectId = other.ProjectId
+	}
+
+	if (v.RancherId.IsUnknown() || v.RancherId.IsNull()) && !other.RancherId.IsUnknown() {
+		v.RancherId = other.RancherId
+	}
+
+}
+
+var _ basetypes.ObjectTypable = CloudProjectRancherCapabilitiesPlanType{}
+
+type CloudProjectRancherCapabilitiesPlanType struct {
+	basetypes.ObjectType
+}
+
+func (t CloudProjectRancherCapabilitiesPlanType) Equal(o attr.Type) bool {
+	other, ok := o.(CloudProjectRancherCapabilitiesPlanType)
+
+	if !ok {
+		return false
+	}
+
+	return t.ObjectType.Equal(other.ObjectType)
+}
+
+func (t CloudProjectRancherCapabilitiesPlanType) String() string {
+	return "CloudProjectRancherCapabilitiesPlanType"
+}
+
+func (t CloudProjectRancherCapabilitiesPlanType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	attributes := in.Attributes()
+
+	causeAttribute, ok := attributes["cause"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`cause is missing from object`)
+
+		return nil, diags
+	}
+
+	causeVal, ok := causeAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`cause expected to be ovhtypes.TfStringValue, was: %T`, causeAttribute))
+	}
+
+	messageAttribute, ok := attributes["message"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`message is missing from object`)
+
+		return nil, diags
+	}
+
+	messageVal, ok := messageAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`message expected to be ovhtypes.TfStringValue, was: %T`, messageAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return nil, diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	statusAttribute, ok := attributes["status"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`status is missing from object`)
+
+		return nil, diags
+	}
+
+	statusVal, ok := statusAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`status expected to be ovhtypes.TfStringValue, was: %T`, statusAttribute))
+	}
+
+	if diags.HasError() {
+		return nil, diags
+	}
+
+	return CloudProjectRancherCapabilitiesPlanValue{
+		Cause:   causeVal,
+		Message: messageVal,
+		Name:    nameVal,
+		Status:  statusVal,
+		state:   attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectRancherCapabilitiesPlanValueNull() CloudProjectRancherCapabilitiesPlanValue {
+	return CloudProjectRancherCapabilitiesPlanValue{
+		state: attr.ValueStateNull,
+	}
+}
+
+func NewCloudProjectRancherCapabilitiesPlanValueUnknown() CloudProjectRancherCapabilitiesPlanValue {
+	return CloudProjectRancherCapabilitiesPlanValue{
+		state: attr.ValueStateUnknown,
+	}
+}
+
+func NewCloudProjectRancherCapabilitiesPlanValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CloudProjectRancherCapabilitiesPlanValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
+	ctx := context.Background()
+
+	for name, attributeType := range attributeTypes {
+		attribute, ok := attributes[name]
+
+		if !ok {
+			diags.AddError(
+				"Missing CloudProjectRancherCapabilitiesPlanValue Attribute Value",
+				"While creating a CloudProjectRancherCapabilitiesPlanValue value, a missing attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesPlanValue must contain values for all attributes, even if null or unknown. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesPlanValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
+			)
+
+			continue
+		}
+
+		if !attributeType.Equal(attribute.Type(ctx)) {
+			diags.AddError(
+				"Invalid CloudProjectRancherCapabilitiesPlanValue Attribute Type",
+				"While creating a CloudProjectRancherCapabilitiesPlanValue value, an invalid attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesPlanValue must use a matching attribute type for the value. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesPlanValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesPlanValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
+			)
+		}
+	}
+
+	for name := range attributes {
+		_, ok := attributeTypes[name]
+
+		if !ok {
+			diags.AddError(
+				"Extra CloudProjectRancherCapabilitiesPlanValue Attribute Value",
+				"While creating a CloudProjectRancherCapabilitiesPlanValue value, an extra attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesPlanValue must not contain values beyond the expected attribute types. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("Extra CloudProjectRancherCapabilitiesPlanValue Attribute Name: %s", name),
+			)
+		}
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	causeAttribute, ok := attributes["cause"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`cause is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	causeVal, ok := causeAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`cause expected to be ovhtypes.TfStringValue, was: %T`, causeAttribute))
+	}
+
+	messageAttribute, ok := attributes["message"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`message is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	messageVal, ok := messageAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`message expected to be ovhtypes.TfStringValue, was: %T`, messageAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	statusAttribute, ok := attributes["status"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`status is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	statusVal, ok := statusAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`status expected to be ovhtypes.TfStringValue, was: %T`, statusAttribute))
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), diags
+	}
+
+	return CloudProjectRancherCapabilitiesPlanValue{
+		Cause:   causeVal,
+		Message: messageVal,
+		Name:    nameVal,
+		Status:  statusVal,
+		state:   attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectRancherCapabilitiesPlanValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CloudProjectRancherCapabilitiesPlanValue {
+	object, diags := NewCloudProjectRancherCapabilitiesPlanValue(attributeTypes, attributes)
+
+	if diags.HasError() {
+		// This could potentially be added to the diag package.
+		diagsStrings := make([]string, 0, len(diags))
+
+		for _, diagnostic := range diags {
+			diagsStrings = append(diagsStrings, fmt.Sprintf(
+				"%s | %s | %s",
+				diagnostic.Severity(),
+				diagnostic.Summary(),
+				diagnostic.Detail()))
+		}
+
+		panic("NewCloudProjectRancherCapabilitiesPlanValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
+	}
+
+	return object
+}
+
+func (t CloudProjectRancherCapabilitiesPlanType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
+	if in.Type() == nil {
+		return NewCloudProjectRancherCapabilitiesPlanValueNull(), nil
+	}
+
+	if !in.Type().Equal(t.TerraformType(ctx)) {
+		return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
+	}
+
+	if !in.IsKnown() {
+		return NewCloudProjectRancherCapabilitiesPlanValueUnknown(), nil
+	}
+
+	if in.IsNull() {
+		return NewCloudProjectRancherCapabilitiesPlanValueNull(), nil
+	}
+
+	attributes := map[string]attr.Value{}
+
+	val := map[string]tftypes.Value{}
+
+	err := in.As(&val)
+
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range val {
+		a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
+
+		if err != nil {
+			return nil, err
+		}
+
+		attributes[k] = a
+	}
+
+	return NewCloudProjectRancherCapabilitiesPlanValueMust(CloudProjectRancherCapabilitiesPlanValue{}.AttributeTypes(ctx), attributes), nil
+}
+
+func (t CloudProjectRancherCapabilitiesPlanType) ValueType(ctx context.Context) attr.Value {
+	return CloudProjectRancherCapabilitiesPlanValue{}
+}
+
+var _ basetypes.ObjectValuable = CloudProjectRancherCapabilitiesPlanValue{}
+
+type CloudProjectRancherCapabilitiesPlanValue struct {
+	Cause   ovhtypes.TfStringValue `tfsdk:"cause" json:"cause"`
+	Message ovhtypes.TfStringValue `tfsdk:"message" json:"message"`
+	Name    ovhtypes.TfStringValue `tfsdk:"name" json:"name"`
+	Status  ovhtypes.TfStringValue `tfsdk:"status" json:"status"`
+	state   attr.ValueState
+}
+
+func (v *CloudProjectRancherCapabilitiesPlanValue) UnmarshalJSON(data []byte) error {
+	type JsonCloudProjectRancherCapabilitiesPlanValue CloudProjectRancherCapabilitiesPlanValue
+
+	var tmp JsonCloudProjectRancherCapabilitiesPlanValue
+	if err := json.Unmarshal(data, &tmp); err != nil {
+		return err
+	}
+	v.Cause = tmp.Cause
+	v.Message = tmp.Message
+	v.Name = tmp.Name
+	v.Status = tmp.Status
+
+	v.state = attr.ValueStateKnown
+
+	return nil
+}
+
+func (v *CloudProjectRancherCapabilitiesPlanValue) MergeWith(other *CloudProjectRancherCapabilitiesPlanValue) {
+
+	if (v.Cause.IsUnknown() || v.Cause.IsNull()) && !other.Cause.IsUnknown() {
+		v.Cause = other.Cause
+	}
+
+	if (v.Message.IsUnknown() || v.Message.IsNull()) && !other.Message.IsUnknown() {
+		v.Message = other.Message
+	}
+
+	if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() {
+		v.Name = other.Name
+	}
+
+	if (v.Status.IsUnknown() || v.Status.IsNull()) && !other.Status.IsUnknown() {
+		v.Status = other.Status
+	}
+
+	if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown {
+		v.state = other.state
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) Attributes() map[string]attr.Value {
+	return map[string]attr.Value{
+		"cause":   v.Cause,
+		"message": v.Message,
+		"name":    v.Name,
+		"status":  v.Status,
+	}
+}
+func (v CloudProjectRancherCapabilitiesPlanValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
+	attrTypes := make(map[string]tftypes.Type, 4)
+
+	var val tftypes.Value
+	var err error
+
+	attrTypes["cause"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["message"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["name"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["status"] = basetypes.StringType{}.TerraformType(ctx)
+
+	objectType := tftypes.Object{AttributeTypes: attrTypes}
+
+	switch v.state {
+	case attr.ValueStateKnown:
+		vals := make(map[string]tftypes.Value, 4)
+
+		val, err = v.Cause.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["cause"] = val
+
+		val, err = v.Message.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["message"] = val
+
+		val, err = v.Name.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["name"] = val
+
+		val, err = v.Status.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["status"] = val
+
+		if err := tftypes.ValidateValue(objectType, vals); err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		return tftypes.NewValue(objectType, vals), nil
+	case attr.ValueStateNull:
+		return tftypes.NewValue(objectType, nil), nil
+	case attr.ValueStateUnknown:
+		return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
+	default:
+		panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) IsNull() bool {
+	return v.state == attr.ValueStateNull
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) IsUnknown() bool {
+	return v.state == attr.ValueStateUnknown
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) String() string {
+	return "CloudProjectRancherCapabilitiesPlanValue"
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	objVal, diags := types.ObjectValue(
+		map[string]attr.Type{
+			"cause":   ovhtypes.TfStringType{},
+			"message": ovhtypes.TfStringType{},
+			"name":    ovhtypes.TfStringType{},
+			"status":  ovhtypes.TfStringType{},
+		},
+		map[string]attr.Value{
+			"cause":   v.Cause,
+			"message": v.Message,
+			"name":    v.Name,
+			"status":  v.Status,
+		})
+
+	return objVal, diags
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) Equal(o attr.Value) bool {
+	other, ok := o.(CloudProjectRancherCapabilitiesPlanValue)
+
+	if !ok {
+		return false
+	}
+
+	if v.state != other.state {
+		return false
+	}
+
+	if v.state != attr.ValueStateKnown {
+		return true
+	}
+
+	if !v.Cause.Equal(other.Cause) {
+		return false
+	}
+
+	if !v.Message.Equal(other.Message) {
+		return false
+	}
+
+	if !v.Name.Equal(other.Name) {
+		return false
+	}
+
+	if !v.Status.Equal(other.Status) {
+		return false
+	}
+
+	return true
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) Type(ctx context.Context) attr.Type {
+	return CloudProjectRancherCapabilitiesPlanType{
+		basetypes.ObjectType{
+			AttrTypes: v.AttributeTypes(ctx),
+		},
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesPlanValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
+	return map[string]attr.Type{
+		"cause":   ovhtypes.TfStringType{},
+		"message": ovhtypes.TfStringType{},
+		"name":    ovhtypes.TfStringType{},
+		"status":  ovhtypes.TfStringType{},
+	}
+}
diff --git a/ovh/data_cloud_project_rancher_capabilities_version.go b/ovh/data_cloud_project_rancher_capabilities_version.go
new file mode 100644
index 00000000..eea6b96f
--- /dev/null
+++ b/ovh/data_cloud_project_rancher_capabilities_version.go
@@ -0,0 +1,69 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+var _ datasource.DataSourceWithConfigure = (*cloudProjectRancherCapabilitiesVersionDataSource)(nil)
+
+func NewCloudProjectRancherCapabilitiesVersionDataSource() datasource.DataSource {
+	return &cloudProjectRancherCapabilitiesVersionDataSource{}
+}
+
+type cloudProjectRancherCapabilitiesVersionDataSource struct {
+	config *Config
+}
+
+func (d *cloudProjectRancherCapabilitiesVersionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_cloud_project_rancher_capabilities_version"
+}
+
+func (d *cloudProjectRancherCapabilitiesVersionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *cloudProjectRancherCapabilitiesVersionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = CloudProjectRancherCapabilitiesVersionDataSourceSchema(ctx)
+}
+
+func (d *cloudProjectRancherCapabilitiesVersionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data CloudProjectRancherCapabilitiesVersionModel
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/v2/publicCloud/project/" + url.PathEscape(data.ProjectId.ValueString()) + "/rancher/" + url.PathEscape(data.RancherId.ValueString()) + "/capabilities/version"
+
+	if err := d.config.OVHClient.Get(endpoint, &data.Versions); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_cloud_project_rancher_capabilities_version_gen.go b/ovh/data_cloud_project_rancher_capabilities_version_gen.go
new file mode 100644
index 00000000..f4014ab8
--- /dev/null
+++ b/ovh/data_cloud_project_rancher_capabilities_version_gen.go
@@ -0,0 +1,693 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/hashicorp/terraform-plugin-framework/attr"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/diag"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+	"github.com/hashicorp/terraform-plugin-go/tftypes"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+func CloudProjectRancherCapabilitiesVersionDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"versions": schema.SetNestedAttribute{
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"cause": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Cause for an unavailability",
+						MarkdownDescription: "Cause for an unavailability",
+					},
+					"changelog_url": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Changelog URL of the version",
+						MarkdownDescription: "Changelog URL of the version",
+					},
+					"message": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Human-readable description of the unavailability cause",
+						MarkdownDescription: "Human-readable description of the unavailability cause",
+					},
+					"name": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Name of the version",
+						MarkdownDescription: "Name of the version",
+					},
+					"status": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Status of the version",
+						MarkdownDescription: "Status of the version",
+					},
+				},
+				CustomType: CloudProjectRancherCapabilitiesVersionType{
+					ObjectType: types.ObjectType{
+						AttrTypes: CloudProjectRancherCapabilitiesVersionValue{}.AttributeTypes(ctx),
+					},
+				},
+			},
+			CustomType: ovhtypes.NewTfListNestedType[CloudProjectRancherCapabilitiesVersionValue](ctx),
+			Computed:   true,
+		},
+		"project_id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Project ID",
+			MarkdownDescription: "Project ID",
+		},
+		"rancher_id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Rancher ID",
+			MarkdownDescription: "Rancher ID",
+		},
+	}
+
+	return schema.Schema{
+		Description: "List available and current versions for the given managed Rancher service",
+		Attributes:  attrs,
+	}
+}
+
+type CloudProjectRancherCapabilitiesVersionModel struct {
+	Versions  ovhtypes.TfListNestedValue[CloudProjectRancherCapabilitiesVersionValue] `tfsdk:"versions" json:"versions"`
+	ProjectId ovhtypes.TfStringValue                                                  `tfsdk:"project_id" json:"projectId"`
+	RancherId ovhtypes.TfStringValue                                                  `tfsdk:"rancher_id" json:"rancherId"`
+}
+
+func (v *CloudProjectRancherCapabilitiesVersionModel) MergeWith(other *CloudProjectRancherCapabilitiesVersionModel) {
+
+	if (v.Versions.IsUnknown() || v.Versions.IsNull()) && !other.Versions.IsUnknown() {
+		v.Versions = other.Versions
+	}
+
+	if (v.ProjectId.IsUnknown() || v.ProjectId.IsNull()) && !other.ProjectId.IsUnknown() {
+		v.ProjectId = other.ProjectId
+	}
+
+	if (v.RancherId.IsUnknown() || v.RancherId.IsNull()) && !other.RancherId.IsUnknown() {
+		v.RancherId = other.RancherId
+	}
+
+}
+
+var _ basetypes.ObjectTypable = CloudProjectRancherCapabilitiesVersionType{}
+
+type CloudProjectRancherCapabilitiesVersionType struct {
+	basetypes.ObjectType
+}
+
+func (t CloudProjectRancherCapabilitiesVersionType) Equal(o attr.Type) bool {
+	other, ok := o.(CloudProjectRancherCapabilitiesVersionType)
+
+	if !ok {
+		return false
+	}
+
+	return t.ObjectType.Equal(other.ObjectType)
+}
+
+func (t CloudProjectRancherCapabilitiesVersionType) String() string {
+	return "CloudProjectRancherCapabilitiesVersionType"
+}
+
+func (t CloudProjectRancherCapabilitiesVersionType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	attributes := in.Attributes()
+
+	causeAttribute, ok := attributes["cause"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`cause is missing from object`)
+
+		return nil, diags
+	}
+
+	causeVal, ok := causeAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`cause expected to be ovhtypes.TfStringValue, was: %T`, causeAttribute))
+	}
+
+	changelogUrlAttribute, ok := attributes["changelog_url"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`changelog_url is missing from object`)
+
+		return nil, diags
+	}
+
+	changelogUrlVal, ok := changelogUrlAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`changelog_url expected to be ovhtypes.TfStringValue, was: %T`, changelogUrlAttribute))
+	}
+
+	messageAttribute, ok := attributes["message"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`message is missing from object`)
+
+		return nil, diags
+	}
+
+	messageVal, ok := messageAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`message expected to be ovhtypes.TfStringValue, was: %T`, messageAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return nil, diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	statusAttribute, ok := attributes["status"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`status is missing from object`)
+
+		return nil, diags
+	}
+
+	statusVal, ok := statusAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`status expected to be ovhtypes.TfStringValue, was: %T`, statusAttribute))
+	}
+
+	if diags.HasError() {
+		return nil, diags
+	}
+
+	return CloudProjectRancherCapabilitiesVersionValue{
+		Cause:        causeVal,
+		ChangelogUrl: changelogUrlVal,
+		Message:      messageVal,
+		Name:         nameVal,
+		Status:       statusVal,
+		state:        attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectRancherCapabilitiesVersionValueNull() CloudProjectRancherCapabilitiesVersionValue {
+	return CloudProjectRancherCapabilitiesVersionValue{
+		state: attr.ValueStateNull,
+	}
+}
+
+func NewCloudProjectRancherCapabilitiesVersionValueUnknown() CloudProjectRancherCapabilitiesVersionValue {
+	return CloudProjectRancherCapabilitiesVersionValue{
+		state: attr.ValueStateUnknown,
+	}
+}
+
+func NewCloudProjectRancherCapabilitiesVersionValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CloudProjectRancherCapabilitiesVersionValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
+	ctx := context.Background()
+
+	for name, attributeType := range attributeTypes {
+		attribute, ok := attributes[name]
+
+		if !ok {
+			diags.AddError(
+				"Missing CloudProjectRancherCapabilitiesVersionValue Attribute Value",
+				"While creating a CloudProjectRancherCapabilitiesVersionValue value, a missing attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesVersionValue must contain values for all attributes, even if null or unknown. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesVersionValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
+			)
+
+			continue
+		}
+
+		if !attributeType.Equal(attribute.Type(ctx)) {
+			diags.AddError(
+				"Invalid CloudProjectRancherCapabilitiesVersionValue Attribute Type",
+				"While creating a CloudProjectRancherCapabilitiesVersionValue value, an invalid attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesVersionValue must use a matching attribute type for the value. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesVersionValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
+					fmt.Sprintf("CloudProjectRancherCapabilitiesVersionValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
+			)
+		}
+	}
+
+	for name := range attributes {
+		_, ok := attributeTypes[name]
+
+		if !ok {
+			diags.AddError(
+				"Extra CloudProjectRancherCapabilitiesVersionValue Attribute Value",
+				"While creating a CloudProjectRancherCapabilitiesVersionValue value, an extra attribute value was detected. "+
+					"A CloudProjectRancherCapabilitiesVersionValue must not contain values beyond the expected attribute types. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("Extra CloudProjectRancherCapabilitiesVersionValue Attribute Name: %s", name),
+			)
+		}
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	causeAttribute, ok := attributes["cause"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`cause is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	causeVal, ok := causeAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`cause expected to be ovhtypes.TfStringValue, was: %T`, causeAttribute))
+	}
+
+	changelogUrlAttribute, ok := attributes["changelog_url"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`changelog_url is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	changelogUrlVal, ok := changelogUrlAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`changelog_url expected to be ovhtypes.TfStringValue, was: %T`, changelogUrlAttribute))
+	}
+
+	messageAttribute, ok := attributes["message"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`message is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	messageVal, ok := messageAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`message expected to be ovhtypes.TfStringValue, was: %T`, messageAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	statusAttribute, ok := attributes["status"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`status is missing from object`)
+
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	statusVal, ok := statusAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`status expected to be ovhtypes.TfStringValue, was: %T`, statusAttribute))
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), diags
+	}
+
+	return CloudProjectRancherCapabilitiesVersionValue{
+		Cause:        causeVal,
+		ChangelogUrl: changelogUrlVal,
+		Message:      messageVal,
+		Name:         nameVal,
+		Status:       statusVal,
+		state:        attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectRancherCapabilitiesVersionValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CloudProjectRancherCapabilitiesVersionValue {
+	object, diags := NewCloudProjectRancherCapabilitiesVersionValue(attributeTypes, attributes)
+
+	if diags.HasError() {
+		// This could potentially be added to the diag package.
+		diagsStrings := make([]string, 0, len(diags))
+
+		for _, diagnostic := range diags {
+			diagsStrings = append(diagsStrings, fmt.Sprintf(
+				"%s | %s | %s",
+				diagnostic.Severity(),
+				diagnostic.Summary(),
+				diagnostic.Detail()))
+		}
+
+		panic("NewCloudProjectRancherCapabilitiesVersionValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
+	}
+
+	return object
+}
+
+func (t CloudProjectRancherCapabilitiesVersionType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
+	if in.Type() == nil {
+		return NewCloudProjectRancherCapabilitiesVersionValueNull(), nil
+	}
+
+	if !in.Type().Equal(t.TerraformType(ctx)) {
+		return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
+	}
+
+	if !in.IsKnown() {
+		return NewCloudProjectRancherCapabilitiesVersionValueUnknown(), nil
+	}
+
+	if in.IsNull() {
+		return NewCloudProjectRancherCapabilitiesVersionValueNull(), nil
+	}
+
+	attributes := map[string]attr.Value{}
+
+	val := map[string]tftypes.Value{}
+
+	err := in.As(&val)
+
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range val {
+		a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
+
+		if err != nil {
+			return nil, err
+		}
+
+		attributes[k] = a
+	}
+
+	return NewCloudProjectRancherCapabilitiesVersionValueMust(CloudProjectRancherCapabilitiesVersionValue{}.AttributeTypes(ctx), attributes), nil
+}
+
+func (t CloudProjectRancherCapabilitiesVersionType) ValueType(ctx context.Context) attr.Value {
+	return CloudProjectRancherCapabilitiesVersionValue{}
+}
+
+var _ basetypes.ObjectValuable = CloudProjectRancherCapabilitiesVersionValue{}
+
+type CloudProjectRancherCapabilitiesVersionValue struct {
+	Cause        ovhtypes.TfStringValue `tfsdk:"cause" json:"cause"`
+	ChangelogUrl ovhtypes.TfStringValue `tfsdk:"changelog_url" json:"changelogUrl"`
+	Message      ovhtypes.TfStringValue `tfsdk:"message" json:"message"`
+	Name         ovhtypes.TfStringValue `tfsdk:"name" json:"name"`
+	Status       ovhtypes.TfStringValue `tfsdk:"status" json:"status"`
+	state        attr.ValueState
+}
+
+func (v *CloudProjectRancherCapabilitiesVersionValue) UnmarshalJSON(data []byte) error {
+	type JsonCloudProjectRancherCapabilitiesVersionValue CloudProjectRancherCapabilitiesVersionValue
+
+	var tmp JsonCloudProjectRancherCapabilitiesVersionValue
+	if err := json.Unmarshal(data, &tmp); err != nil {
+		return err
+	}
+	v.Cause = tmp.Cause
+	v.ChangelogUrl = tmp.ChangelogUrl
+	v.Message = tmp.Message
+	v.Name = tmp.Name
+	v.Status = tmp.Status
+
+	v.state = attr.ValueStateKnown
+
+	return nil
+}
+
+func (v *CloudProjectRancherCapabilitiesVersionValue) MergeWith(other *CloudProjectRancherCapabilitiesVersionValue) {
+
+	if (v.Cause.IsUnknown() || v.Cause.IsNull()) && !other.Cause.IsUnknown() {
+		v.Cause = other.Cause
+	}
+
+	if (v.ChangelogUrl.IsUnknown() || v.ChangelogUrl.IsNull()) && !other.ChangelogUrl.IsUnknown() {
+		v.ChangelogUrl = other.ChangelogUrl
+	}
+
+	if (v.Message.IsUnknown() || v.Message.IsNull()) && !other.Message.IsUnknown() {
+		v.Message = other.Message
+	}
+
+	if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() {
+		v.Name = other.Name
+	}
+
+	if (v.Status.IsUnknown() || v.Status.IsNull()) && !other.Status.IsUnknown() {
+		v.Status = other.Status
+	}
+
+	if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown {
+		v.state = other.state
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) Attributes() map[string]attr.Value {
+	return map[string]attr.Value{
+		"cause":        v.Cause,
+		"changelogUrl": v.ChangelogUrl,
+		"message":      v.Message,
+		"name":         v.Name,
+		"status":       v.Status,
+	}
+}
+func (v CloudProjectRancherCapabilitiesVersionValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
+	attrTypes := make(map[string]tftypes.Type, 5)
+
+	var val tftypes.Value
+	var err error
+
+	attrTypes["cause"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["changelog_url"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["message"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["name"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["status"] = basetypes.StringType{}.TerraformType(ctx)
+
+	objectType := tftypes.Object{AttributeTypes: attrTypes}
+
+	switch v.state {
+	case attr.ValueStateKnown:
+		vals := make(map[string]tftypes.Value, 5)
+
+		val, err = v.Cause.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["cause"] = val
+
+		val, err = v.ChangelogUrl.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["changelog_url"] = val
+
+		val, err = v.Message.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["message"] = val
+
+		val, err = v.Name.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["name"] = val
+
+		val, err = v.Status.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["status"] = val
+
+		if err := tftypes.ValidateValue(objectType, vals); err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		return tftypes.NewValue(objectType, vals), nil
+	case attr.ValueStateNull:
+		return tftypes.NewValue(objectType, nil), nil
+	case attr.ValueStateUnknown:
+		return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
+	default:
+		panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) IsNull() bool {
+	return v.state == attr.ValueStateNull
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) IsUnknown() bool {
+	return v.state == attr.ValueStateUnknown
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) String() string {
+	return "CloudProjectRancherCapabilitiesVersionValue"
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	objVal, diags := types.ObjectValue(
+		map[string]attr.Type{
+			"cause":         ovhtypes.TfStringType{},
+			"changelog_url": ovhtypes.TfStringType{},
+			"message":       ovhtypes.TfStringType{},
+			"name":          ovhtypes.TfStringType{},
+			"status":        ovhtypes.TfStringType{},
+		},
+		map[string]attr.Value{
+			"cause":         v.Cause,
+			"changelog_url": v.ChangelogUrl,
+			"message":       v.Message,
+			"name":          v.Name,
+			"status":        v.Status,
+		})
+
+	return objVal, diags
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) Equal(o attr.Value) bool {
+	other, ok := o.(CloudProjectRancherCapabilitiesVersionValue)
+
+	if !ok {
+		return false
+	}
+
+	if v.state != other.state {
+		return false
+	}
+
+	if v.state != attr.ValueStateKnown {
+		return true
+	}
+
+	if !v.Cause.Equal(other.Cause) {
+		return false
+	}
+
+	if !v.ChangelogUrl.Equal(other.ChangelogUrl) {
+		return false
+	}
+
+	if !v.Message.Equal(other.Message) {
+		return false
+	}
+
+	if !v.Name.Equal(other.Name) {
+		return false
+	}
+
+	if !v.Status.Equal(other.Status) {
+		return false
+	}
+
+	return true
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) Type(ctx context.Context) attr.Type {
+	return CloudProjectRancherCapabilitiesVersionType{
+		basetypes.ObjectType{
+			AttrTypes: v.AttributeTypes(ctx),
+		},
+	}
+}
+
+func (v CloudProjectRancherCapabilitiesVersionValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
+	return map[string]attr.Type{
+		"cause":         ovhtypes.TfStringType{},
+		"changelog_url": ovhtypes.TfStringType{},
+		"message":       ovhtypes.TfStringType{},
+		"name":          ovhtypes.TfStringType{},
+		"status":        ovhtypes.TfStringType{},
+	}
+}
diff --git a/ovh/data_cloud_project_region.go b/ovh/data_cloud_project_region.go
index ab09c3d2..9431409f 100644
--- a/ovh/data_cloud_project_region.go
+++ b/ovh/data_cloud_project_region.go
@@ -22,36 +22,76 @@ func dataSourceCloudProjectRegion() *schema.Resource {
 				Description: "Service name of the resource representing the id of the cloud project.",
 			},
 			"name": {
-				Type:     schema.TypeString,
-				Required: true,
-				ForceNew: true,
+				Type:        schema.TypeString,
+				Required:    true,
+				ForceNew:    true,
+				Description: "Name of the region",
+			},
+			"status": {
+				Type:        schema.TypeString,
+				Computed:    true,
+				Description: "Openstack region status",
+			},
+			"type": {
+				Type:        schema.TypeString,
+				Computed:    true,
+				Description: "Region type (localzone, region, region-3-az)",
 			},
 			"services": {
-				Type:     schema.TypeSet,
-				Set:      cloudServiceHash,
-				Computed: true,
+				Type:        schema.TypeSet,
+				Set:         cloudServiceHash,
+				Computed:    true,
+				Description: "Information about the different components available in the region",
 				Elem: &schema.Resource{
 					Schema: map[string]*schema.Schema{
 						"name": {
-							Type:     schema.TypeString,
-							Computed: true,
+							Type:        schema.TypeString,
+							Description: "Service name",
+							Computed:    true,
 						},
-
 						"status": {
-							Type:     schema.TypeString,
-							Computed: true,
+							Type:        schema.TypeString,
+							Description: "Service status",
+							Computed:    true,
+						},
+						"endpoint": {
+							Type:        schema.TypeString,
+							Description: "Endpoint URL",
+							Computed:    true,
 						},
 					},
 				},
 			},
-
 			"continent_code": {
-				Type:     schema.TypeString,
-				Computed: true,
+				Type:        schema.TypeString,
+				Computed:    true,
+				Description: "Region continent code",
+			},
+			"country_code": {
+				Type:        schema.TypeString,
+				Computed:    true,
+				Description: "Region country code",
 			},
 			"datacenter_location": {
-				Type:     schema.TypeString,
-				Computed: true,
+				Type:        schema.TypeString,
+				Computed:    true,
+				Description: "Location of the datacenter where the region is",
+			},
+			"availability_zones": {
+				Type:        schema.TypeSet,
+				Computed:    true,
+				Description: "Availability zones of the region",
+				Elem: &schema.Schema{
+					Type: schema.TypeString,
+				},
+			},
+			"ip_countries": {
+				Type:        schema.TypeSet,
+				Computed:    true,
+				Description: "Allowed countries for failover IP",
+				Elem: &schema.Schema{
+					Type: schema.TypeString,
+				},
 			},
 		},
 	}
@@ -72,16 +112,21 @@ func dataSourceCloudProjectRegionRead(d *schema.ResourceData, meta interface{})
 
 	d.Set("datacenter_location", region.DatacenterLocation)
 	d.Set("continent_code", region.ContinentCode)
+	d.Set("country_code", region.CountryCode)
+	d.Set("status", region.Status)
+	d.Set("type", region.Type)
+	d.Set("ip_countries", region.IPCountries)
+	d.Set("availability_zones", region.AvailabilityZones)
 
 	services := &schema.Set{
 		F: cloudServiceHash,
 	}
-	for i := range region.Services {
-		service := map[string]interface{}{
-			"name":   region.Services[i].Name,
-			"status": region.Services[i].Status,
-		}
-		services.Add(service)
+	for _, service := range region.Services {
+		services.Add(map[string]interface{}{
+			"name":     service.Name,
+			"status":   service.Status,
+			"endpoint": service.Endpoint,
+		})
 	}
 
 	d.Set("services", services)
diff --git a/ovh/data_cloud_project_ssh_key.go b/ovh/data_cloud_project_ssh_key.go
new file mode 100644
index 00000000..666387e9
--- /dev/null
+++ b/ovh/data_cloud_project_ssh_key.go
@@ -0,0 +1,69 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+var _ datasource.DataSourceWithConfigure = (*cloudProjectSshKeyDataSource)(nil)
+
+func NewCloudProjectSshKeyDataSource() datasource.DataSource {
+	return &cloudProjectSshKeyDataSource{}
+}
+
+type cloudProjectSshKeyDataSource struct {
+	config *Config
+}
+
+func (d *cloudProjectSshKeyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_cloud_project_ssh_key"
+}
+
+func (d *cloudProjectSshKeyDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *cloudProjectSshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = CloudProjectSshKeyDataSourceSchema(ctx)
+}
+
+func (d *cloudProjectSshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data CloudProjectSshKeyReadModel
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/cloud/project/" + url.PathEscape(data.ServiceName.ValueString()) + "/sshkey/" + url.PathEscape(data.Id.ValueString())
+
+	if err := d.config.OVHClient.Get(endpoint, &data); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_cloud_project_ssh_key_gen.go b/ovh/data_cloud_project_ssh_key_gen.go
new file mode 100644
index 00000000..4d19072b
--- /dev/null
+++ b/ovh/data_cloud_project_ssh_key_gen.go
@@ -0,0 +1,91 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func CloudProjectSshKeyDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"finger_print": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "SSH key fingerprint",
+			MarkdownDescription: "SSH key fingerprint",
+		},
+		"name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "SSH key name",
+			MarkdownDescription: "SSH key name",
+		},
+		"public_key": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Computed:            true,
+			Description:         "SSH public key",
+			MarkdownDescription: "SSH public key",
+		},
+		"regions": schema.ListAttribute{
+			CustomType:          ovhtypes.NewTfListNestedType[ovhtypes.TfStringValue](ctx),
+			Computed:            true,
+			Description:         "SSH key regions",
+			MarkdownDescription: "SSH key regions",
+		},
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"id": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "SSH key id",
+			MarkdownDescription: "SSH key id",
+		},
+	}
+
+	return schema.Schema{
+		Description: "Get SSH key",
+		Attributes:  attrs,
+	}
+}
+
+type CloudProjectSshKeyReadModel struct {
+	FingerPrint ovhtypes.TfStringValue                             `tfsdk:"finger_print" json:"fingerPrint"`
+	Id          ovhtypes.TfStringValue                             `tfsdk:"id" json:"id"`
+	Name        ovhtypes.TfStringValue                             `tfsdk:"name" json:"name"`
+	PublicKey   ovhtypes.TfStringValue                             `tfsdk:"public_key" json:"publicKey"`
+	Regions     ovhtypes.TfListNestedValue[ovhtypes.TfStringValue] `tfsdk:"regions" json:"regions"`
+	ServiceName ovhtypes.TfStringValue                             `tfsdk:"service_name" json:"serviceName"`
+}
+
+func (v *CloudProjectSshKeyReadModel) MergeWith(other *CloudProjectSshKeyReadModel) {
+	if (v.FingerPrint.IsUnknown() || v.FingerPrint.IsNull()) && !other.FingerPrint.IsUnknown() {
+		v.FingerPrint = other.FingerPrint
+	}
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() {
+		v.Name = other.Name
+	}
+
+	if (v.PublicKey.IsUnknown() || v.PublicKey.IsNull()) && !other.PublicKey.IsUnknown() {
+		v.PublicKey = other.PublicKey
+	}
+
+	if (v.Regions.IsUnknown() || v.Regions.IsNull()) && !other.Regions.IsUnknown() {
+		v.Regions = other.Regions
+	}
+
+	if (v.ServiceName.IsUnknown() || v.ServiceName.IsNull()) && !other.ServiceName.IsUnknown() {
+		v.ServiceName = other.ServiceName
+	}
+}
diff --git a/ovh/data_cloud_project_ssh_keys.go b/ovh/data_cloud_project_ssh_keys.go
new file mode 100644
index 00000000..0fc1b59e
--- /dev/null
+++ b/ovh/data_cloud_project_ssh_keys.go
@@ -0,0 +1,73 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+var _ datasource.DataSourceWithConfigure = (*cloudProjectSshKeysDataSource)(nil)
+
+func NewCloudProjectSshKeysDataSource() datasource.DataSource {
+	return &cloudProjectSshKeysDataSource{}
+}
+
+type cloudProjectSshKeysDataSource struct {
+	config *Config
+}
+
+func (d *cloudProjectSshKeysDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_cloud_project_ssh_keys"
+}
+
+func (d *cloudProjectSshKeysDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *cloudProjectSshKeysDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = CloudProjectSshKeysDataSourceSchema(ctx)
+}
+
+func (d *cloudProjectSshKeysDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var data CloudProjectSshKeysModel
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	query := ""
+	if !data.Region.IsNull() && !data.Region.IsUnknown() {
+		query = "?region=" + url.QueryEscape(data.Region.ValueString())
+	}
+	endpoint := "/cloud/project/" + url.PathEscape(data.ServiceName.ValueString()) + "/sshkey" + query
+
+	if err := d.config.OVHClient.Get(endpoint, &data.SshKeys); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_cloud_project_ssh_keys_gen.go b/ovh/data_cloud_project_ssh_keys_gen.go
new file mode 100644
index 00000000..46e347db
--- /dev/null
+++ b/ovh/data_cloud_project_ssh_keys_gen.go
@@ -0,0 +1,629 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/hashicorp/terraform-plugin-framework/attr"
+	"github.com/hashicorp/terraform-plugin-framework/diag"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+	"github.com/hashicorp/terraform-plugin-go/tftypes"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+	"strings"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func CloudProjectSshKeysDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"ssh_keys": schema.SetNestedAttribute{
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"id": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "SSH key id",
+						MarkdownDescription: "SSH key id",
+					},
+					"name": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "SSH key name",
+						MarkdownDescription: "SSH key name",
+					},
+					"public_key": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "SSH public key",
+						MarkdownDescription: "SSH public key",
+					},
+					"regions": schema.ListAttribute{
+						CustomType:          ovhtypes.NewTfListNestedType[ovhtypes.TfStringValue](ctx),
+						Computed:            true,
+						Description:         "SSH key regions",
+						MarkdownDescription: "SSH key regions",
+					},
+				},
+				CustomType: CloudProjectSshKeysType{
+					ObjectType: types.ObjectType{
+						AttrTypes: CloudProjectSshKeysValue{}.AttributeTypes(ctx),
+					},
+				},
+			},
+			CustomType: ovhtypes.NewTfListNestedType[CloudProjectSshKeysValue](ctx),
+			Computed:   true,
+		},
+		"region": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Optional:            true,
+			Computed:            true,
+			Description:         "Region",
+			MarkdownDescription: "Region",
+		},
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+	}
+
+	return schema.Schema{
+		Description: "Get SSH keys",
+		Attributes:  attrs,
+	}
+}
+
+type CloudProjectSshKeysModel struct {
+	SshKeys     ovhtypes.TfListNestedValue[CloudProjectSshKeysValue] `tfsdk:"ssh_keys" json:"sshKeys"`
+	Region      ovhtypes.TfStringValue                               `tfsdk:"region" json:"region"`
+	ServiceName ovhtypes.TfStringValue                               `tfsdk:"service_name" json:"serviceName"`
+}
+
+func (v *CloudProjectSshKeysModel) MergeWith(other *CloudProjectSshKeysModel) {
+
+	if (v.SshKeys.IsUnknown() || v.SshKeys.IsNull()) && !other.SshKeys.IsUnknown() {
+		v.SshKeys = other.SshKeys
+	}
+
+	if (v.Region.IsUnknown() || v.Region.IsNull()) && !other.Region.IsUnknown() {
+		v.Region = other.Region
+	}
+
+	if (v.ServiceName.IsUnknown() || v.ServiceName.IsNull()) && !other.ServiceName.IsUnknown() {
+		v.ServiceName = other.ServiceName
+	}
+
+}
+
+var _ basetypes.ObjectTypable = CloudProjectSshKeysType{}
+
+type CloudProjectSshKeysType struct {
+	basetypes.ObjectType
+}
+
+func (t CloudProjectSshKeysType) Equal(o attr.Type) bool {
+	other, ok := o.(CloudProjectSshKeysType)
+
+	if !ok {
+		return false
+	}
+
+	return t.ObjectType.Equal(other.ObjectType)
+}
+
+func (t CloudProjectSshKeysType) String() string {
+	return "CloudProjectSshKeysType"
+}
+
+func (t CloudProjectSshKeysType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	attributes := in.Attributes()
+
+	idAttribute, ok := attributes["id"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`id is missing from object`)
+
+		return nil, diags
+	}
+
+	idVal, ok := idAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return nil, diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	publicKeyAttribute, ok := attributes["public_key"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`public_key is missing from object`)
+
+		return nil, diags
+	}
+
+	publicKeyVal, ok := publicKeyAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`public_key expected to be ovhtypes.TfStringValue, was: %T`, publicKeyAttribute))
+	}
+
+	regionsAttribute, ok := attributes["regions"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`regions is missing from object`)
+
+		return nil, diags
+	}
+
+	regionsVal, ok := regionsAttribute.(ovhtypes.TfListNestedValue[ovhtypes.TfStringValue])
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`regions expected to be ovhtypes.TfListNestedValue[ovhtypes.TfStringValue], was: %T`, regionsAttribute))
+	}
+
+	if diags.HasError() {
+		return nil, diags
+	}
+
+	return CloudProjectSshKeysValue{
+		Id:        idVal,
+		Name:      nameVal,
+		PublicKey: publicKeyVal,
+		Regions:   regionsVal,
+		state:     attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectSshKeysValueNull() CloudProjectSshKeysValue {
+	return CloudProjectSshKeysValue{
+		state: attr.ValueStateNull,
+	}
+}
+
+func NewCloudProjectSshKeysValueUnknown() CloudProjectSshKeysValue {
+	return CloudProjectSshKeysValue{
+		state: attr.ValueStateUnknown,
+	}
+}
+
+func NewCloudProjectSshKeysValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CloudProjectSshKeysValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
+	ctx := context.Background()
+
+	for name, attributeType := range attributeTypes {
+		attribute, ok := attributes[name]
+
+		if !ok {
+			diags.AddError(
+				"Missing CloudProjectSshKeysValue Attribute Value",
+				"While creating a CloudProjectSshKeysValue value, a missing attribute value was detected. "+
+					"A CloudProjectSshKeysValue must contain values for all attributes, even if null or unknown. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectSshKeysValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
+			)
+
+			continue
+		}
+
+		if !attributeType.Equal(attribute.Type(ctx)) {
+			diags.AddError(
+				"Invalid CloudProjectSshKeysValue Attribute Type",
+				"While creating a CloudProjectSshKeysValue value, an invalid attribute value was detected. "+
+					"A CloudProjectSshKeysValue must use a matching attribute type for the value. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("CloudProjectSshKeysValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
+					fmt.Sprintf("CloudProjectSshKeysValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
+			)
+		}
+	}
+
+	for name := range attributes {
+		_, ok := attributeTypes[name]
+
+		if !ok {
+			diags.AddError(
+				"Extra CloudProjectSshKeysValue Attribute Value",
+				"While creating a CloudProjectSshKeysValue value, an extra attribute value was detected. "+
+					"A CloudProjectSshKeysValue must not contain values beyond the expected attribute types. "+
+					"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+					fmt.Sprintf("Extra CloudProjectSshKeysValue Attribute Name: %s", name),
+			)
+		}
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	idAttribute, ok := attributes["id"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`id is missing from object`)
+
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	idVal, ok := idAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute))
+	}
+
+	nameAttribute, ok := attributes["name"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`name is missing from object`)
+
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	nameVal, ok := nameAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute))
+	}
+
+	publicKeyAttribute, ok := attributes["public_key"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`public_key is missing from object`)
+
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	publicKeyVal, ok := publicKeyAttribute.(ovhtypes.TfStringValue)
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`public_key expected to be ovhtypes.TfStringValue, was: %T`, publicKeyAttribute))
+	}
+
+	regionsAttribute, ok := attributes["regions"]
+
+	if !ok {
+		diags.AddError(
+			"Attribute Missing",
+			`regions is missing from object`)
+
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	regionsVal, ok := regionsAttribute.(ovhtypes.TfListNestedValue[ovhtypes.TfStringValue])
+
+	if !ok {
+		diags.AddError(
+			"Attribute Wrong Type",
+			fmt.Sprintf(`regions expected to be ovhtypes.TfListNestedValue[ovhtypes.TfStringValue], was: %T`, regionsAttribute))
+	}
+
+	if diags.HasError() {
+		return NewCloudProjectSshKeysValueUnknown(), diags
+	}
+
+	return CloudProjectSshKeysValue{
+		Id:        idVal,
+		Name:      nameVal,
+		PublicKey: publicKeyVal,
+		Regions:   regionsVal,
+		state:     attr.ValueStateKnown,
+	}, diags
+}
+
+func NewCloudProjectSshKeysValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CloudProjectSshKeysValue {
+	object, diags := NewCloudProjectSshKeysValue(attributeTypes, attributes)
+
+	if diags.HasError() {
+		// This could potentially be added to the diag package.
+		diagsStrings := make([]string, 0, len(diags))
+
+		for _, diagnostic := range diags {
+			diagsStrings = append(diagsStrings, fmt.Sprintf(
+				"%s | %s | %s",
+				diagnostic.Severity(),
+				diagnostic.Summary(),
+				diagnostic.Detail()))
+		}
+
+		panic("NewCloudProjectSshKeysValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
+	}
+
+	return object
+}
+
+func (t CloudProjectSshKeysType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
+	if in.Type() == nil {
+		return NewCloudProjectSshKeysValueNull(), nil
+	}
+
+	if !in.Type().Equal(t.TerraformType(ctx)) {
+		return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
+	}
+
+	if !in.IsKnown() {
+		return NewCloudProjectSshKeysValueUnknown(), nil
+	}
+
+	if in.IsNull() {
+		return NewCloudProjectSshKeysValueNull(), nil
+	}
+
+	attributes := map[string]attr.Value{}
+
+	val := map[string]tftypes.Value{}
+
+	err := in.As(&val)
+
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range val {
+		a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
+
+		if err != nil {
+			return nil, err
+		}
+
+		attributes[k] = a
+	}
+
+	return NewCloudProjectSshKeysValueMust(CloudProjectSshKeysValue{}.AttributeTypes(ctx), attributes), nil
+}
+
+func (t CloudProjectSshKeysType) ValueType(ctx context.Context) attr.Value {
+	return CloudProjectSshKeysValue{}
+}
+
+var _ basetypes.ObjectValuable = CloudProjectSshKeysValue{}
+
+type CloudProjectSshKeysValue struct {
+	Id        ovhtypes.TfStringValue                             `tfsdk:"id" json:"id"`
+	Name      ovhtypes.TfStringValue                             `tfsdk:"name" json:"name"`
+	PublicKey ovhtypes.TfStringValue                             `tfsdk:"public_key" json:"publicKey"`
+	Regions   ovhtypes.TfListNestedValue[ovhtypes.TfStringValue] `tfsdk:"regions" json:"regions"`
+	state     attr.ValueState
+}
+
+func (v *CloudProjectSshKeysValue) UnmarshalJSON(data []byte) error {
+	type JsonCloudProjectSshKeysValue CloudProjectSshKeysValue
+
+	var tmp JsonCloudProjectSshKeysValue
+	if err := json.Unmarshal(data, &tmp); err != nil {
+		return err
+	}
+	v.Id = tmp.Id
+	v.Name = tmp.Name
+	v.PublicKey = tmp.PublicKey
+	v.Regions = tmp.Regions
+
+	v.state = attr.ValueStateKnown
+
+	return nil
+}
+
+func (v *CloudProjectSshKeysValue) MergeWith(other *CloudProjectSshKeysValue) {
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() {
+		v.Name = other.Name
+	}
+
+	if (v.PublicKey.IsUnknown() || v.PublicKey.IsNull()) && !other.PublicKey.IsUnknown() {
+		v.PublicKey = other.PublicKey
+	}
+
+	if (v.Regions.IsUnknown() || v.Regions.IsNull()) && !other.Regions.IsUnknown() {
+		v.Regions = other.Regions
+	}
+
+	if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown {
+		v.state = other.state
+	}
+}
+
+func (v CloudProjectSshKeysValue) Attributes() map[string]attr.Value {
+	return map[string]attr.Value{
+		"id":        v.Id,
+		"name":      v.Name,
+		"publicKey": v.PublicKey,
+		"regions":   v.Regions,
+	}
+}
+func (v CloudProjectSshKeysValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
+	attrTypes := make(map[string]tftypes.Type, 4)
+
+	var val tftypes.Value
+	var err error
+
+	attrTypes["id"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["name"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["public_key"] = basetypes.StringType{}.TerraformType(ctx)
+	attrTypes["regions"] = basetypes.ListType{
+		ElemType: types.StringType,
+	}.TerraformType(ctx)
+
+	objectType := tftypes.Object{AttributeTypes: attrTypes}
+
+	switch v.state {
+	case attr.ValueStateKnown:
+		vals := make(map[string]tftypes.Value, 4)
+
+		val, err = v.Id.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["id"] = val
+
+		val, err = v.Name.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["name"] = val
+
+		val, err = v.PublicKey.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["public_key"] = val
+
+		val, err = v.Regions.ToTerraformValue(ctx)
+
+		if err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		vals["regions"] = val
+
+		if err := tftypes.ValidateValue(objectType, vals); err != nil {
+			return tftypes.NewValue(objectType, tftypes.UnknownValue), err
+		}
+
+		return tftypes.NewValue(objectType, vals), nil
+	case attr.ValueStateNull:
+		return tftypes.NewValue(objectType, nil), nil
+	case attr.ValueStateUnknown:
+		return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
+	default:
+		panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
+	}
+}
+
+func (v CloudProjectSshKeysValue) IsNull() bool {
+	return v.state == attr.ValueStateNull
+}
+
+func (v CloudProjectSshKeysValue) IsUnknown() bool {
+	return v.state == attr.ValueStateUnknown
+}
+
+func (v CloudProjectSshKeysValue) String() string {
+	return "CloudProjectSshKeysValue"
+}
+
+func (v CloudProjectSshKeysValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
+	var diags diag.Diagnostics
+
+	objVal, diags := types.ObjectValue(
+		map[string]attr.Type{
+			"id":         ovhtypes.TfStringType{},
+			"name":       ovhtypes.TfStringType{},
+			"public_key": ovhtypes.TfStringType{},
+			"regions":    ovhtypes.NewTfListNestedType[ovhtypes.TfStringValue](ctx),
+		},
+		map[string]attr.Value{
+			"id":         v.Id,
+			"name":       v.Name,
+			"public_key": v.PublicKey,
+			"regions":    v.Regions,
+		})
+
+	return objVal, diags
+}
+
+func (v CloudProjectSshKeysValue) Equal(o attr.Value) bool {
+	other, ok := o.(CloudProjectSshKeysValue)
+
+	if !ok {
+		return false
+	}
+
+	if v.state != other.state {
+		return false
+	}
+
+	if v.state != attr.ValueStateKnown {
+		return true
+	}
+
+	if !v.Id.Equal(other.Id) {
+		return false
+	}
+
+	if !v.Name.Equal(other.Name) {
+		return false
+	}
+
+	if !v.PublicKey.Equal(other.PublicKey) {
+		return false
+	}
+
+	if !v.Regions.Equal(other.Regions) {
+		return false
+	}
+
+	return true
+}
+
+func (v CloudProjectSshKeysValue) Type(ctx context.Context) attr.Type {
+	return CloudProjectSshKeysType{
+		basetypes.ObjectType{
+			AttrTypes: v.AttributeTypes(ctx),
+		},
+	}
+}
+
+func (v CloudProjectSshKeysValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
+	return map[string]attr.Type{
+		"id":         ovhtypes.TfStringType{},
+		"name":       ovhtypes.TfStringType{},
+		"public_key": ovhtypes.TfStringType{},
+		"regions":    ovhtypes.NewTfListNestedType[ovhtypes.TfStringValue](ctx),
+	}
+}
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenter_extras.go b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras.go
new file mode 100644
index 00000000..8fdab9ae
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras.go
@@ -0,0 +1,88 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+var _ datasource.DataSourceWithConfigure = (*ovhcloudConnectConfigPopDatacenterExtrasDataSource)(nil)
+
+func NewOvhcloudConnectConfigPopDatacenterExtrasDataSource() datasource.DataSource {
+	return &ovhcloudConnectConfigPopDatacenterExtrasDataSource{}
+}
+
+type ovhcloudConnectConfigPopDatacenterExtrasDataSource struct {
+	config *Config
+}
+
+func (d *ovhcloudConnectConfigPopDatacenterExtrasDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_ovhcloud_connect_config_pop_datacenter_extras"
+}
+
+func (d *ovhcloudConnectConfigPopDatacenterExtrasDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *ovhcloudConnectConfigPopDatacenterExtrasDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = OvhcloudConnectConfigPopDatacenterExtrasDataSourceSchema(ctx)
+}
+
+func (d *ovhcloudConnectConfigPopDatacenterExtrasDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var (
+		data               OvhcloudConnectConfigPopDatacenterExtrasModel
+		datacenterExtraIDs []ovhtypes.TfInt64Value
+	)
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/" + url.PathEscape(data.ConfigPopId.String()) + "/datacenter/" + url.PathEscape(data.ConfigDatacenterId.String()) + "/extra/"
+
+	if err := d.config.OVHClient.Get(endpoint, &datacenterExtraIDs); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Fetch each occ datacenter config data
+	for _, extraID := range datacenterExtraIDs {
+		var extraConfig OvhcloudConnectConfigPopDatacenterExtraModel
+		endpoint = "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/" + url.PathEscape(data.ConfigPopId.String()) + "/datacenter/" + url.PathEscape(data.ConfigDatacenterId.String()) + "/extra/" + url.PathEscape(extraID.String())
+		if err := d.config.OVHClient.Get(endpoint, &extraConfig); err != nil {
+			resp.Diagnostics.AddError(
+				fmt.Sprintf("Error calling Get %s", endpoint),
+				err.Error(),
+			)
+			return
+		}
+
+		data.ExtraConfigs = append(data.ExtraConfigs, extraConfig)
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_gen.go b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_gen.go
new file mode 100644
index 00000000..ae8cac54
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_gen.go
@@ -0,0 +1,135 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func OvhcloudConnectConfigPopDatacenterExtrasDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"config_datacenter_id": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Required:            true,
+			Description:         "ID of the Datacenter Configuration",
+			MarkdownDescription: "ID of the Datacenter Configuration",
+		},
+		"config_pop_id": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Required:            true,
+			Description:         "ID of the Pop Configuration",
+			MarkdownDescription: "ID of the Pop Configuration",
+		},
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"extra_configs": schema.SetNestedAttribute{
+			Computed: true,
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"bgp_neighbor_area": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "BGP AS number",
+						MarkdownDescription: "BGP AS number",
+					},
+					"bgp_neighbor_ip": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Router IP for BGP",
+						MarkdownDescription: "Router IP for BGP",
+					},
+					"id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "ID of the extra configuration ",
+						MarkdownDescription: "ID of the extra configuration ",
+					},
+					"next_hop": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Static route next hop",
+						MarkdownDescription: "Static route next hop",
+					},
+					"status": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Status of the pop configuration",
+						MarkdownDescription: "Status of the pop configuration",
+					},
+					"subnet": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Static route ip",
+						MarkdownDescription: "Static route ip",
+					},
+					"type": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Type of the configuration",
+						MarkdownDescription: "Type of the configuration",
+					},
+				},
+			},
+		},
+	}
+
+	return schema.Schema{
+		Attributes: attrs,
+	}
+}
+
+type OvhcloudConnectConfigPopDatacenterExtrasModel struct {
+	ExtraConfigs       []OvhcloudConnectConfigPopDatacenterExtraModel `tfsdk:"extra_configs"`
+	ConfigDatacenterId ovhtypes.TfInt64Value                          `tfsdk:"config_datacenter_id" json:"datacenterId"`
+	ConfigPopId        ovhtypes.TfInt64Value                          `tfsdk:"config_pop_id" json:"popId"`
+	ServiceName        ovhtypes.TfStringValue                         `tfsdk:"service_name" json:"serviceName"`
+}
+
+type OvhcloudConnectConfigPopDatacenterExtraModel struct {
+	BgpNeighborArea ovhtypes.TfInt64Value  `tfsdk:"bgp_neighbor_area" json:"bgpNeighborArea"`
+	BgpNeighborIp   ovhtypes.TfStringValue `tfsdk:"bgp_neighbor_ip" json:"bgpNeighborIp"`
+	Id              ovhtypes.TfInt64Value  `tfsdk:"id" json:"id"`
+	NextHop         ovhtypes.TfStringValue `tfsdk:"next_hop" json:"nextHop"`
+	Status          ovhtypes.TfStringValue `tfsdk:"status" json:"status"`
+	Subnet          ovhtypes.TfStringValue `tfsdk:"subnet" json:"subnet"`
+	Type            ovhtypes.TfStringValue `tfsdk:"type" json:"type"`
+}
+
+func (v *OvhcloudConnectConfigPopDatacenterExtraModel) MergeWith(other *OvhcloudConnectConfigPopDatacenterExtraModel) {
+
+	if (v.BgpNeighborArea.IsUnknown() || v.BgpNeighborArea.IsNull()) && !other.BgpNeighborArea.IsUnknown() {
+		v.BgpNeighborArea = other.BgpNeighborArea
+	}
+
+	if (v.BgpNeighborIp.IsUnknown() || v.BgpNeighborIp.IsNull()) && !other.BgpNeighborIp.IsUnknown() {
+		v.BgpNeighborIp = other.BgpNeighborIp
+	}
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.NextHop.IsUnknown() || v.NextHop.IsNull()) && !other.NextHop.IsUnknown() {
+		v.NextHop = other.NextHop
+	}
+
+	if (v.Status.IsUnknown() || v.Status.IsNull()) && !other.Status.IsUnknown() {
+		v.Status = other.Status
+	}
+
+	if (v.Subnet.IsUnknown() || v.Subnet.IsNull()) && !other.Subnet.IsUnknown() {
+		v.Subnet = other.Subnet
+	}
+
+	if (v.Type.IsUnknown() || v.Type.IsNull()) && !other.Type.IsUnknown() {
+		v.Type = other.Type
+	}
+
+}
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenters.go b/ovh/data_ovhcloud_connect_config_pop_datacenters.go
new file mode 100644
index 00000000..25a34e9a
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenters.go
@@ -0,0 +1,88 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+var _ datasource.DataSourceWithConfigure = (*ovhcloudConnectConfigPopDatacentersDataSource)(nil)
+
+func NewOvhcloudConnectConfigPopDatacentersDataSource() datasource.DataSource {
+	return &ovhcloudConnectConfigPopDatacentersDataSource{}
+}
+
+type ovhcloudConnectConfigPopDatacentersDataSource struct {
+	config *Config
+}
+
+func (d *ovhcloudConnectConfigPopDatacentersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_ovhcloud_connect_config_pop_datacenters"
+}
+
+func (d *ovhcloudConnectConfigPopDatacentersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *ovhcloudConnectConfigPopDatacentersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = OvhcloudConnectConfigPopDatacentersDataSourceSchema(ctx)
+}
+
+func (d *ovhcloudConnectConfigPopDatacentersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var (
+		datacenterIDs []ovhtypes.TfInt64Value
+		data          OvhcloudConnectConfigPopDatacentersModel
+	)
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/" + url.PathEscape(data.ConfigPopId.String()) + "/datacenter/"
+
+	if err := d.config.OVHClient.Get(endpoint, &datacenterIDs); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Fetch each occ datacenter config data
+	for _, datacenterID := range datacenterIDs {
+		var datacenterConfig OvhcloudConnectConfigPopDatacenterModel
+		endpoint = "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/" + url.PathEscape(data.ConfigPopId.String()) + "/datacenter/" + url.PathEscape(datacenterID.String())
+		if err := d.config.OVHClient.Get(endpoint, &datacenterConfig); err != nil {
+			resp.Diagnostics.AddError(
+				fmt.Sprintf("Error calling Get %s", endpoint),
+				err.Error(),
+			)
+			return
+		}
+
+		data.DatacenterConfigs = append(data.DatacenterConfigs, datacenterConfig)
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenters_gen.go b/ovh/data_ovhcloud_connect_config_pop_datacenters_gen.go
new file mode 100644
index 00000000..05c308ae
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenters_gen.go
@@ -0,0 +1,106 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func OvhcloudConnectConfigPopDatacentersDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"config_pop_id": schema.Int64Attribute{
+			CustomType:          ovhtypes.TfInt64Type{},
+			Required:            true,
+			Description:         "ID of the Pop Configuration",
+			MarkdownDescription: "ID of the Pop Configuration",
+		},
+		"datacenter_configs": schema.SetNestedAttribute{
+			Computed: true,
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"datacenter_id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Required:            true,
+						Description:         "Datacenter ID",
+						MarkdownDescription: "Datacenter ID",
+					},
+					"id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "ID of the Datacenter configuration",
+						MarkdownDescription: "ID of the Datacenter configuration",
+					},
+					"ovh_bgp_area": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "OVH Private AS",
+						MarkdownDescription: "OVH Private AS",
+					},
+					"status": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Status of the pop configuration",
+						MarkdownDescription: "Status of the pop configuration",
+					},
+					"subnet": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Subnet should be a /28 min",
+						MarkdownDescription: "Subnet should be a /28 min",
+					},
+				},
+			},
+		},
+	}
+
+	return schema.Schema{
+		Attributes: attrs,
+	}
+}
+
+type OvhcloudConnectConfigPopDatacentersModel struct {
+	DatacenterConfigs []OvhcloudConnectConfigPopDatacenterModel `tfsdk:"datacenter_configs"`
+	ConfigPopId       ovhtypes.TfInt64Value                     `tfsdk:"config_pop_id" json:"popId"`
+	ServiceName       ovhtypes.TfStringValue                    `tfsdk:"service_name" json:"serviceName"`
+}
+
+type OvhcloudConnectConfigPopDatacenterModel struct {
+	DatacenterId ovhtypes.TfInt64Value  `tfsdk:"datacenter_id" json:"datacenterId"`
+	Id           ovhtypes.TfInt64Value  `tfsdk:"id" json:"id"`
+	OvhBgpArea   ovhtypes.TfInt64Value  `tfsdk:"ovh_bgp_area" json:"ovhBgpArea"`
+	Status       ovhtypes.TfStringValue `tfsdk:"status" json:"status"`
+	Subnet       ovhtypes.TfStringValue `tfsdk:"subnet" json:"subnet"`
+}
+
+func (v *OvhcloudConnectConfigPopDatacenterModel) MergeWith(other *OvhcloudConnectConfigPopDatacenterModel) {
+
+	if (v.DatacenterId.IsUnknown() || v.DatacenterId.IsNull()) && !other.DatacenterId.IsUnknown() {
+		v.DatacenterId = other.DatacenterId
+	}
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.OvhBgpArea.IsUnknown() || v.OvhBgpArea.IsNull()) && !other.OvhBgpArea.IsUnknown() {
+		v.OvhBgpArea = other.OvhBgpArea
+	}
+
+	if (v.Status.IsUnknown() || v.Status.IsNull()) && !other.Status.IsUnknown() {
+		v.Status = other.Status
+	}
+
+	if (v.Subnet.IsUnknown() || v.Subnet.IsNull()) && !other.Subnet.IsUnknown() {
+		v.Subnet = other.Subnet
+	}
+
+}
diff --git a/ovh/data_ovhcloud_connect_config_pops.go b/ovh/data_ovhcloud_connect_config_pops.go
new file mode 100644
index 00000000..bb57ae33
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pops.go
@@ -0,0 +1,89 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+var _ datasource.DataSourceWithConfigure = (*ovhcloudConnectConfigPopsDataSource)(nil)
+
+func NewOvhcloudConnectConfigPopsDataSource() datasource.DataSource {
+	return &ovhcloudConnectConfigPopsDataSource{}
+}
+
+type ovhcloudConnectConfigPopsDataSource struct {
+	config *Config
+}
+
+func (d *ovhcloudConnectConfigPopsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_ovhcloud_connect_config_pops"
+}
+
+func (d *ovhcloudConnectConfigPopsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *ovhcloudConnectConfigPopsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = OvhcloudConnectConfigPopsDataSourceSchema(ctx)
+}
+
+func (d *ovhcloudConnectConfigPopsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var (
+		popIDs []ovhtypes.TfInt64Value
+		data   OvhcloudConnectConfigPopsModel
+	)
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/"
+
+	// Retrieve list of pop config IDs
+	if err := d.config.OVHClient.Get(endpoint, &popIDs); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Fetch each occ pop config data
+	for _, popID := range popIDs {
+		var popConfig OvhcloudConnectConfigPopModel
+		endpoint = "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/config/pop/" + url.PathEscape(popID.String())
+		if err := d.config.OVHClient.Get(endpoint, &popConfig); err != nil {
+			resp.Diagnostics.AddError(
+				fmt.Sprintf("Error calling Get %s", endpoint),
+				err.Error(),
+			)
+			return
+		}
+
+		data.PopConfigs = append(data.PopConfigs, popConfig)
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_ovhcloud_connect_config_pops_gen.go b/ovh/data_ovhcloud_connect_config_pops_gen.go
new file mode 100644
index 00000000..51c65e44
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pops_gen.go
@@ -0,0 +1,121 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func OvhcloudConnectConfigPopsDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"pop_configs": schema.SetNestedAttribute{
+			Computed: true,
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"customer_bgp_area": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "Customer Private AS",
+						MarkdownDescription: "Customer Private AS",
+					},
+					"id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "ID of the Pop Configuration",
+						MarkdownDescription: "ID of the Pop Configuration",
+					},
+					"interface_id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "ID of the interface",
+						MarkdownDescription: "ID of the interface",
+					},
+					"ovh_bgp_area": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Computed:            true,
+						Description:         "OVH Private AS",
+						MarkdownDescription: "OVH Private AS",
+					},
+					"status": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Status of the pop configuration",
+						MarkdownDescription: "Status of the pop configuration",
+					},
+					"subnet": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Subnet should be a /30, first IP for OVH, second IP for customer",
+						MarkdownDescription: "Subnet should be a /30, first IP for OVH, second IP for customer",
+					},
+					"type": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "Type of the pop configuration",
+						MarkdownDescription: "Type of the pop configuration",
+					},
+				},
+			},
+		},
+	}
+
+	return schema.Schema{
+		Attributes: attrs,
+	}
+}
+
+type OvhcloudConnectConfigPopsModel struct {
+	PopConfigs  []OvhcloudConnectConfigPopModel `tfsdk:"pop_configs"`
+	ServiceName ovhtypes.TfStringValue          `tfsdk:"service_name" json:"serviceName"`
+}
+
+type OvhcloudConnectConfigPopModel struct {
+	CustomerBgpArea ovhtypes.TfInt64Value  `tfsdk:"customer_bgp_area" json:"customerBgpArea"`
+	Id              ovhtypes.TfInt64Value  `tfsdk:"id" json:"id"`
+	InterfaceId     ovhtypes.TfInt64Value  `tfsdk:"interface_id" json:"interfaceId"`
+	OvhBgpArea      ovhtypes.TfInt64Value  `tfsdk:"ovh_bgp_area" json:"ovhBgpArea"`
+	Status          ovhtypes.TfStringValue `tfsdk:"status" json:"status"`
+	Subnet          ovhtypes.TfStringValue `tfsdk:"subnet" json:"subnet"`
+	Type            ovhtypes.TfStringValue `tfsdk:"type" json:"type"`
+}
+
+func (v *OvhcloudConnectConfigPopModel) MergeWith(other *OvhcloudConnectConfigPopModel) {
+
+	if (v.CustomerBgpArea.IsUnknown() || v.CustomerBgpArea.IsNull()) && !other.CustomerBgpArea.IsUnknown() {
+		v.CustomerBgpArea = other.CustomerBgpArea
+	}
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.InterfaceId.IsUnknown() || v.InterfaceId.IsNull()) && !other.InterfaceId.IsUnknown() {
+		v.InterfaceId = other.InterfaceId
+	}
+
+	if (v.OvhBgpArea.IsUnknown() || v.OvhBgpArea.IsNull()) && !other.OvhBgpArea.IsUnknown() {
+		v.OvhBgpArea = other.OvhBgpArea
+	}
+
+	if (v.Status.IsUnknown() || v.Status.IsNull()) && !other.Status.IsUnknown() {
+		v.Status = other.Status
+	}
+
+	if (v.Subnet.IsUnknown() || v.Subnet.IsNull()) && !other.Subnet.IsUnknown() {
+		v.Subnet = other.Subnet
+	}
+
+	if (v.Type.IsUnknown() || v.Type.IsNull()) && !other.Type.IsUnknown() {
+		v.Type = other.Type
+	}
+
+}
diff --git a/ovh/data_ovhcloud_connect_datacenters.go b/ovh/data_ovhcloud_connect_datacenters.go
new file mode 100644
index 00000000..6427fa65
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_datacenters.go
@@ -0,0 +1,88 @@
+package ovh
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+)
+
+var _ datasource.DataSourceWithConfigure = (*ovhcloudConnectDatacentersDataSource)(nil)
+
+func NewOvhcloudConnectDatacentersDataSource() datasource.DataSource {
+	return &ovhcloudConnectDatacentersDataSource{}
+}
+
+type ovhcloudConnectDatacentersDataSource struct {
+	config *Config
+}
+
+func (d *ovhcloudConnectDatacentersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_ovhcloud_connect_datacenters"
+}
+
+func (d *ovhcloudConnectDatacentersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	if req.ProviderData == nil {
+		return
+	}
+
+	config, ok := req.ProviderData.(*Config)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Data Source Configure Type",
+			fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+
+	d.config = config
+}
+
+func (d *ovhcloudConnectDatacentersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = OvhcloudConnectDatacentersDataSourceSchema(ctx)
+}
+
+func (d *ovhcloudConnectDatacentersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	var (
+		datacenterIDs []ovhtypes.TfInt64Value
+		data          OvhcloudConnectDatacentersModel
+	)
+
+	// Read Terraform configuration data into the model
+	resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+	if resp.Diagnostics.HasError() {
+		return
+	}
+
+	// Read API call logic
+	endpoint := "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/datacenter/"
+
+	if err := d.config.OVHClient.Get(endpoint, &datacenterIDs); err != nil {
+		resp.Diagnostics.AddError(
+			fmt.Sprintf("Error calling Get %s", endpoint),
+			err.Error(),
+		)
+		return
+	}
+
+	// Fetch each occ datacenter config data
+	for _, datacenterID := range datacenterIDs {
+		var datacenter OvhcloudConnectDatacenterModel
+		endpoint = "/ovhCloudConnect/" + url.PathEscape(data.ServiceName.ValueString()) + "/datacenter/" + url.PathEscape(datacenterID.String())
+		if err := d.config.OVHClient.Get(endpoint, &datacenter); err != nil {
+			resp.Diagnostics.AddError(
+				fmt.Sprintf("Error calling Get %s", endpoint),
+				err.Error(),
+			)
+			return
+		}
+
+		data.Datacenters = append(data.Datacenters, datacenter)
+	}
+
+	// Save data into Terraform state
+	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/ovh/data_ovhcloud_connect_datacenters_gen.go b/ovh/data_ovhcloud_connect_datacenters_gen.go
new file mode 100644
index 00000000..ae340d96
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_datacenters_gen.go
@@ -0,0 +1,99 @@
+// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
+
+package ovh
+
+import (
+	"context"
+	ovhtypes "github.com/ovh/terraform-provider-ovh/v2/ovh/types"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func OvhcloudConnectDatacentersDataSourceSchema(ctx context.Context) schema.Schema {
+	attrs := map[string]schema.Attribute{
+		"service_name": schema.StringAttribute{
+			CustomType:          ovhtypes.TfStringType{},
+			Required:            true,
+			Description:         "Service name",
+			MarkdownDescription: "Service name",
+		},
+		"datacenters": schema.SetNestedAttribute{
+			Computed: true,
+			NestedObject: schema.NestedAttributeObject{
+				Attributes: map[string]schema.Attribute{
+					"available": schema.BoolAttribute{
+						CustomType:          ovhtypes.TfBoolType{},
+						Computed:            true,
+						Description:         "Get availability to add new configuration on it",
+						MarkdownDescription: "Get availability to add new configuration on it",
+					},
+					"id": schema.Int64Attribute{
+						CustomType:          ovhtypes.TfInt64Type{},
+						Required:            true,
+						Description:         "Id",
+						MarkdownDescription: "Id",
+					},
+					"name": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "name of the datacenter",
+						MarkdownDescription: "name of the datacenter",
+					},
+					"region": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "region of the datacenter",
+						MarkdownDescription: "region of the datacenter",
+					},
+					"region_type": schema.StringAttribute{
+						CustomType:          ovhtypes.TfStringType{},
+						Computed:            true,
+						Description:         "region type of the datacenter",
+						MarkdownDescription: "region type of the datacenter",
+					},
+				},
+			},
+		},
+	}
+
+	return schema.Schema{
+		Attributes: attrs,
+	}
+}
+
+type OvhcloudConnectDatacentersModel struct {
+	Datacenters []OvhcloudConnectDatacenterModel `tfsdk:"datacenters"`
+	ServiceName ovhtypes.TfStringValue           `tfsdk:"service_name" json:"serviceName"`
+}
+
+type OvhcloudConnectDatacenterModel struct {
+	Available  ovhtypes.TfBoolValue   `tfsdk:"available" json:"available"`
+	Id         ovhtypes.TfInt64Value  `tfsdk:"id" json:"id"`
+	Name       ovhtypes.TfStringValue `tfsdk:"name" json:"name"`
+	Region     ovhtypes.TfStringValue `tfsdk:"region" json:"region"`
+	RegionType ovhtypes.TfStringValue `tfsdk:"region_type" json:"regionType"`
+}
+
+func (v *OvhcloudConnectDatacenterModel) MergeWith(other *OvhcloudConnectDatacenterModel) {
+
+	if (v.Available.IsUnknown() || v.Available.IsNull()) && !other.Available.IsUnknown() {
+		v.Available = other.Available
+	}
+
+	if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() {
+		v.Id = other.Id
+	}
+
+	if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() {
+		v.Name = other.Name
+	}
+
+	if (v.Region.IsUnknown() || v.Region.IsNull()) && !other.Region.IsUnknown() {
+		v.Region = other.Region
+	}
+
+	if (v.RegionType.IsUnknown() || v.RegionType.IsNull()) && !other.RegionType.IsUnknown() {
+		v.RegionType = other.RegionType
+	}
+
+}
diff --git a/ovh/provider.go b/ovh/provider.go
index 7e247134..a4da0944 100644
--- a/ovh/provider.go
+++ b/ovh/provider.go
@@ -30,6 +30,9 @@ var (
 		// Authentication via oAuth2
 		"client_id":     "OAuth 2.0 application's ID",
 		"client_secret": "OAuth 2.0 application's secret",
+
+		// Extra info in user-agent
+		"user_agent_extra": "Extra information to append to the user-agent",
 	}
 )
 
@@ -72,6 +75,11 @@ func Provider() *schema.Provider {
 				Optional:    true,
 				Description: descriptions["client_secret"],
 			},
+			"user_agent_extra": {
+				Type:        schema.TypeString,
+				Optional:    true,
+				Description: descriptions["user_agent_extra"],
+			},
 		},
 
 		DataSourcesMap: map[string]*schema.Resource{
@@ -314,6 +322,9 @@ func ConfigureContextFunc(context context.Context, d *schema.ResourceData) (inte
 	if v, ok := d.GetOk("client_secret"); ok {
 		config.ClientSecret = v.(string)
 	}
+	if v, ok := d.GetOk("user_agent_extra"); ok {
+		config.UserAgentExtra = v.(string)
+	}
 
 	if err := config.loadAndValidate(); err != nil {
 		return nil, diag.FromErr(err)
diff --git a/ovh/provider_new.go b/ovh/provider_new.go
index af1e173a..fa649c36 100644
--- a/ovh/provider_new.go
+++ b/ovh/provider_new.go
@@ -63,6 +63,10 @@ func (p *OvhProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *
 				Optional:    true,
 				Description: descriptions["client_secret"],
 			},
+			"user_agent_extra": schema.StringAttribute{
+				Optional:    true,
+				Description: descriptions["user_agent_extra"],
+			},
 		},
 	}
 }
@@ -176,6 +180,9 @@ func (p *OvhProvider) Configure(ctx context.Context, req provider.ConfigureReque
 	if !config.ClientSecret.IsNull() {
 		clientConfig.ClientSecret = config.ClientSecret.ValueString()
 	}
+	if !config.UserAgentExtra.IsNull() {
+		clientConfig.UserAgentExtra = config.UserAgentExtra.ValueString()
+	}
 
 	if err := clientConfig.loadAndValidate(); err != nil {
 		resp.Diagnostics.AddError(err.Error(), "failed to init OVH API client")
@@ -190,6 +197,7 @@ func (p *OvhProvider) Configure(ctx context.Context, req provider.ConfigureReque
 func (p *OvhProvider) DataSources(_ context.Context) []func() datasource.DataSource {
 	return []func() datasource.DataSource{
 		NewCloudProjectDatabaseIPRestrictionsDataSource,
+		NewCloudProjectFlavorDataSource,
 		NewCloudProjectFlavorsDataSource,
 		NewCloudProjectFloatingipsDataSource,
 		NewCloudProjectGatewayInterfaceDataSource,
@@ -201,9 +209,13 @@ func (p *OvhProvider) DataSources(_ context.Context) []func() datasource.DataSou
 		NewCloudProjectNetworkPrivateDataSource,
 		NewCloudProjectNetworkPrivatesDataSource,
 		NewCloudProjectNetworkPrivateSubnetsDataSource,
+		NewCloudProjectRancherCapabilitiesPlanDataSource,
+		NewCloudProjectRancherCapabilitiesVersionDataSource,
 		NewCloudProjectRancherDataSource,
 		NewCloudProjectRancherPlanDataSource,
 		NewCloudProjectRancherVersionDataSource,
+		NewCloudProjectSshKeyDataSource,
+		NewCloudProjectSshKeysDataSource,
 		NewCloudProjectStorageDataSource,
 		NewCloudProjectStoragesDataSource,
 		NewCloudProjectStorageObjectDataSource,
@@ -225,6 +237,10 @@ func (p *OvhProvider) DataSources(_ context.Context) []func() datasource.DataSou
 		NewOkmsServiceKeyDataSource,
 		NewOkmsServiceKeyJwkDataSource,
 		NewOkmsServiceKeyPemDataSource,
+		NewOvhcloudConnectDatacentersDataSource,
+		NewOvhcloudConnectConfigPopDatacenterExtrasDataSource,
+		NewOvhcloudConnectConfigPopDatacentersDataSource,
+		NewOvhcloudConnectConfigPopsDataSource,
 		NewOvhcloudConnectDataSource,
 		NewOvhcloudConnectsDataSource,
 		NewVmwareCloudDirectorBackupDataSource,
@@ -277,4 +293,5 @@ type ovhProviderModel struct {
 	ConsumerKey       types.String `tfsdk:"consumer_key"`
 	ClientID          types.String `tfsdk:"client_id"`
 	ClientSecret      types.String `tfsdk:"client_secret"`
+	UserAgentExtra    types.String `tfsdk:"user_agent_extra"`
 }
diff --git a/ovh/resource_cloud_project_user.go b/ovh/resource_cloud_project_user.go
index 812f0e69..3bebad19 100644
--- a/ovh/resource_cloud_project_user.go
+++ b/ovh/resource_cloud_project_user.go
@@ -54,6 +54,11 @@ func resourceCloudProjectUser() *schema.Resource {
 				ForceNew: false,
 				Elem:     &schema.Schema{Type: schema.TypeString},
 			},
+			"password_reset": {
+				Type:        schema.TypeString,
+				Description: "Arbitrary string to change to trigger a password update",
+				Optional:    true,
+			},
 
 			// Computed
 			"creation_date": {
@@ -159,39 +164,77 @@ func resourceCloudProjectUserUpdate(d *schema.ResourceData, meta interface{}) er
 	config := meta.(*Config)
 	serviceName := d.Get("service_name").(string)
 	userId := d.Id()
-	role := d.Get("role_name")
-	roles, err := helpers.StringsFromSchema(d, "role_names")
-	res := &CloudProjectrolesResponse{}
 
-	res, err = validateCloudProjectUserRoleFunc(config, serviceName, roles, role.(string))
-	if err != nil {
-		return err
-	}
-	update := []string{}
-	for _, i := range res.Roles {
-		if slices.Contains(roles, i.Name) {
-			update = append(update, i.Id)
+	// Check if password_reset has been modified
+	if d.HasChange("password_reset") {
+		log.Printf("[DEBUG] password_reset has been modified, regenerating password for user %s", userId)
+
+		// Call the regeneratePassword endpoint
+		regenerate, err := cloudProjectUserRegeneratePassword(config, serviceName, userId)
+		if err != nil {
+			return fmt.Errorf("error regenerating password for user %s: %s", userId, err)
 		}
-		if role == i.Name && !slices.Contains(update, i.Name) {
-			update = append(update, i.Id)
+
+		// Update the password in the state
+		if err := d.Set("password", regenerate.Password); err != nil {
+			return fmt.Errorf("error setting password in state: %s", err)
+		}
+
+		log.Printf("[DEBUG] Successfully updated password in state for user %s", userId)
+
+		// Wait for user to be ok after password regeneration
+		log.Printf("[DEBUG] Waiting for User %s to be ok after password regeneration:", userId)
+		stateConf := &resource.StateChangeConf{
+			Pending:    []string{"creating", "deleted", "deleting"},
+			Target:     []string{"ok"},
+			Refresh:    waitForCloudProjectUser(config.OVHClient, serviceName, userId),
+			Timeout:    5 * time.Minute, // Adjust timeout as needed
+			Delay:      5 * time.Second,
+			MinTimeout: 3 * time.Second,
 		}
+		if _, err := stateConf.WaitForState(); err != nil {
+			return fmt.Errorf("waiting for user (%s) to be ok after password regeneration: %s", userId, err)
+		}
+		log.Printf("[DEBUG] User %s is ok after password regeneration", userId)
 	}
 
-	log.Printf("[DEBUG] roles IDs %s", update)
-	log.Printf("[DEBUG] user %s", userId)
-	endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/role",
-		url.PathEscape(serviceName),
-		url.PathEscape(userId),
-	)
+	// Update roles if they changed
+	if d.HasChange("role_name") || d.HasChange("role_names") {
+		role := d.Get("role_name")
+		roles, err := helpers.StringsFromSchema(d, "role_names")
+		res := &CloudProjectrolesResponse{}
 
-	r := &CloudProjectUser{}
-	data := &CloudProjectroleUpdate{
-		RolesIds: update,
-	}
-	if err := config.OVHClient.Put(endpoint, data, r); err != nil {
-		return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, data, err)
+		res, err = validateCloudProjectUserRoleFunc(config, serviceName, roles, role.(string))
+		if err != nil {
+			return err
+		}
+		update := []string{}
+		for _, i := range res.Roles {
+			if slices.Contains(roles, i.Name) {
+				update = append(update, i.Id)
+			}
+			if role == i.Name && !slices.Contains(update, i.Name) {
+				update = append(update, i.Id)
+			}
+		}
+
+		log.Printf("[DEBUG] roles IDs %s", update)
+		log.Printf("[DEBUG] user %s", userId)
+		endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/role",
+			url.PathEscape(serviceName),
+			url.PathEscape(userId),
+		)
+
+		r := &CloudProjectUser{}
+		data := &CloudProjectroleUpdate{
+			RolesIds: update,
+		}
+		if err := config.OVHClient.Put(endpoint, data, r); err != nil {
+			return fmt.Errorf("calling %s with params %s:\n\t %q", endpoint, data, err)
+		}
 	}
-	return nil
+
+	return resourceCloudProjectUserRead(d, meta)
 }
 
 func resourceCloudProjectUserCreate(d *schema.ResourceData, meta interface{}) error {
@@ -314,6 +357,25 @@ func resourceCloudProjectUserDelete(d *schema.ResourceData, meta interface{}) er
 	return nil
 }
 
+// cloudProjectUserRegeneratePassword regenerates the password for a cloud project user
+func cloudProjectUserRegeneratePassword(config *Config, serviceName, userId string) (*CloudProjectUserRegenerate, error) {
+	log.Printf("[DEBUG] Will regenerate password for user %s from project: %s", userId, serviceName)
+
+	endpoint := fmt.Sprintf(
+		"/cloud/project/%s/user/%s/regeneratePassword",
+		url.PathEscape(serviceName),
+		url.PathEscape(userId),
+	)
+
+	r := &CloudProjectUserRegenerate{}
+	if err := config.OVHClient.Post(endpoint, nil, r); err != nil {
+		return nil, fmt.Errorf("calling Post %s:\n\t %q", endpoint, err)
+	}
+
+	log.Printf("[DEBUG] Successfully regenerated password for user %s", userId)
+	return r, nil
+}
+
 var cloudUserOSTenantName = regexp.MustCompile("export OS_TENANT_NAME=\"?([[:alnum:]]+)\"?")
 var cloudUserOSTenantId = regexp.MustCompile("export OS_TENANT_ID=\"??([[:alnum:]]+)\"??")
 var cloudUserOSAuthURL = regexp.MustCompile("export OS_AUTH_URL=\"??([[:^space:]]+)\"??")
diff --git a/ovh/types_cloud.go b/ovh/types_cloud.go
index bc4f6a24..527fe18e 100644
--- a/ovh/types_cloud.go
+++ b/ovh/types_cloud.go
@@ -196,17 +196,19 @@ func (p *CloudProjectNetworkPrivatesResponse) String() string {
 
 // Opts
 type CloudProjectUserCreateOpts struct {
-	Description *string  `json:"description,omitempty"`
-	Role        *string  `json:"role,omitempty"`
-	Roles       []string `json:"roles"`
+	Description   *string  `json:"description,omitempty"`
+	Role          *string  `json:"role,omitempty"`
+	Roles         []string `json:"roles"`
+	PasswordReset string   `json:"-"` // This is only used for triggering recreation, not sent to the API
 }
 
 func (p *CloudProjectUserCreateOpts) String() string {
 	return fmt.Sprintf(
-		"CloudProjectUserCreateOpts[description:%v, role:%v, roles:%s]",
+		"CloudProjectUserCreateOpts[description:%v, role:%v, roles:%s, passwordReset:%s]",
 		p.Description,
 		p.Role,
 		p.Roles,
+		p.PasswordReset,
 	)
 }
 
@@ -214,6 +216,8 @@ func (opts *CloudProjectUserCreateOpts) FromResource(d *schema.ResourceData) *Cl
 	opts.Description = helpers.GetNilStringPointerFromData(d, "description")
 	opts.Role = helpers.GetNilStringPointerFromData(d, "role_name")
 	opts.Roles, _ = helpers.StringsFromSchema(d, "role_names")
+	opts.PasswordReset = d.Get("password_reset").(string)
+
 	return opts
 }
 
@@ -318,9 +322,14 @@ func (u CloudProjectUserS3CredentialSecret) ToMap() map[string]interface{} {
 }
 
 type CloudProjectRegionResponse struct {
+	AvailabilityZones  []string                     `json:"availabilityZones"`
 	ContinentCode      string                       `json:"continentCode"`
+	CountryCode        string                       `json:"countryCode"`
 	DatacenterLocation string                       `json:"datacenterLocation"`
+	IPCountries        []string                     `json:"ipCountries"`
 	Name               string                       `json:"name"`
+	Status             string                       `json:"status"`
+	Type               string                       `json:"type"`
 	Services           []CloudServiceStatusResponse `json:"services"`
 }
 
@@ -338,10 +347,22 @@ func (r *CloudProjectRegionResponse) HasServiceUp(service string) bool {
 }
 
 type CloudServiceStatusResponse struct {
-	Status string `json:"status"`
-	Name   string `json:"name"`
+	Status   string `json:"status"`
+	Name     string `json:"name"`
+	Endpoint string `json:"endpoint"`
 }
 
 func (s *CloudServiceStatusResponse) String() string {
 	return fmt.Sprintf("%s: %s", s.Name, s.Status)
 }
+
+type CloudProjectUserRegenerate struct {
+	CreationDate string                  `json:"creationDate"`
+	Description  string                  `json:"description"`
+	Id           int                     `json:"id"`
+	OpenstackId  string                  `json:"openstackId"`
+	Password     string                  `json:"password"`
+	Roles        []*CloudProjectUserRole `json:"roles"`
+	Status       string                  `json:"status"`
+	Username     string                  `json:"username"`
+}
diff --git a/templates/data-sources/cloud_project_flavor.md.tmpl b/templates/data-sources/cloud_project_flavor.md.tmpl
new file mode 100644
index 00000000..9ab58721
--- /dev/null
+++ b/templates/data-sources/cloud_project_flavor.md.tmpl
@@ -0,0 +1,55 @@
+---
+subcategory : "Cloud Project"
+---
+
+# ovh_cloud_project_flavor
+
+Retrieves the given flavor in a public cloud project.
+
+## Example Usage
+
+{{tffile "examples/data-sources/cloud_project_flavor/example_1.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+- `id` (String) Flavor ID
+
+### Read-Only
+
+- `available` (Boolean) Available in stock
+- `capabilities` (Attributes List) Capabilities of the flavor (see [below for nested schema](#nestedatt--capabilities))
+- `disk` (Number) Number of disks
+- `id` (String) Flavor id
+- `inbound_bandwidth` (Number) Max capacity of inbound traffic in Mbit/s
+- `name` (String) Flavor name
+- `os_type` (String) OS to install on
+- `outbound_bandwidth` (Number) Max capacity of outbound traffic in Mbit/s
+- `plan_codes` (Attributes) Plan codes to order instances (see [below for nested schema](#nestedatt--plan_codes))
+- `quota` (Number) Number instance you can spawn with your actual quota
+- `ram` (Number) Ram quantity (Gio)
+- `region` (String) Flavor region
+- `type` (String) Flavor type
+- `vcpus` (Number) Number of VCPUs
+
+<a id="nestedatt--capabilities"></a>
+
+### Nested Schema for `capabilities`
+
+Read-Only:
+
+- `enabled` (Boolean) Is the capability enabled
+- `name` (String) Name of the capability
+
+<a id="nestedatt--plan_codes"></a>
+
+### Nested Schema for `plan_codes`
+
+Read-Only:
+
+- `hourly` (String) Plan code to order hourly instance
+- `monthly` (String) Plan code to order monthly instance
diff --git a/templates/data-sources/cloud_project_flavors.md.tmpl b/templates/data-sources/cloud_project_flavors.md.tmpl
index ebffec5d..bbfd9edd 100644
--- a/templates/data-sources/cloud_project_flavors.md.tmpl
+++ b/templates/data-sources/cloud_project_flavors.md.tmpl
@@ -25,6 +25,7 @@ Retrieves the available flavors on the given public cloud project.
 ### Optional
 
 - `region` (String) Flavor region
+- `name_filter` (String) Filter flavors using the given name (strict equality, e.g. b2-7)
 
 ### Read-Only
 
diff --git a/templates/data-sources/cloud_project_rancher_capabilities_plan.md.tmpl b/templates/data-sources/cloud_project_rancher_capabilities_plan.md.tmpl
new file mode 100644
index 00000000..2ac5a6ae
--- /dev/null
+++ b/templates/data-sources/cloud_project_rancher_capabilities_plan.md.tmpl
@@ -0,0 +1,34 @@
+---
+subcategory: "Managed Rancher Service (MRS)"
+description: "List available and current plans for the given managed Rancher service"
+---
+
+# ovh_cloud_project_rancher_capabilities_plan (Data Source)
+
+List available and current plans for the given managed Rancher service
+
+## Example Usage
+
+{{tffile "examples/data-sources/cloud_project_rancher_capabilities_plan/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `project_id` (String) Project ID
+- `rancher_id` (String) Rancher ID
+
+### Read-Only
+
+- `plans` (Attributes Set) (see [below for nested schema](#nestedatt--plans))
+
+<a id="nestedatt--plans"></a>
+### Nested Schema for `plans`
+
+Read-Only:
+
+- `cause` (String) Cause for an unavailability
+- `message` (String) Human-readable description of the unavailability cause
+- `name` (String) Name of the plan
+- `status` (String) Status of the plan
diff --git a/templates/data-sources/cloud_project_rancher_capabilities_version.md.tmpl b/templates/data-sources/cloud_project_rancher_capabilities_version.md.tmpl
new file mode 100644
index 00000000..9174f424
--- /dev/null
+++ b/templates/data-sources/cloud_project_rancher_capabilities_version.md.tmpl
@@ -0,0 +1,35 @@
+---
+subcategory: "Managed Rancher Service (MRS)"
+description: "List available and current versions for the given managed Rancher service"
+---
+
+# ovh_cloud_project_rancher_capabilities_version (Data Source)
+
+List available and current versions for the given managed Rancher service
+
+## Example Usage
+
+{{tffile "examples/data-sources/cloud_project_rancher_capabilities_version/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `project_id` (String) Project ID
+- `rancher_id` (String) Rancher ID
+
+### Read-Only
+
+- `versions` (Attributes Set) (see [below for nested schema](#nestedatt--versions))
+
+<a id="nestedatt--versions"></a>
+### Nested Schema for `versions`
+
+Read-Only:
+
+- `cause` (String) Cause for an unavailability
+- `changelog_url` (String) Changelog URL of the version
+- `message` (String) Human-readable description of the unavailability cause
+- `name` (String) Name of the version
+- `status` (String) Status of the version
diff --git a/templates/data-sources/cloud_project_region.md.tmpl b/templates/data-sources/cloud_project_region.md.tmpl
index 05403ec9..e6cfa682 100644
--- a/templates/data-sources/cloud_project_region.md.tmpl
+++ b/templates/data-sources/cloud_project_region.md.tmpl
@@ -17,15 +17,20 @@ Use this data source to retrieve information about a region associated with a pu
 ## Argument Reference
 
 * `service_name` - (Required) The id of the public cloud project. If omitted, the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used.
-
-* `name` - (Required) The name of the region associated with the public cloud project.
+* `name`         - (Required) The name of the region associated with the public cloud project.
 
 ## Attributes Reference
 
 `id` is set to the ID of the project concatenated with the name of the region. In addition, the following attributes are exported:
 
-* `continent_code` - the code of the geographic continent the region is running. E.g.: EU for Europe, US for America...
+* `type`                - Region type (localzone | region | region-3-az)
+* `status`              - Openstack region status (DOWN | MAINTENANCE | UP)
+* `continent_code`      - The code of the geographic continent the region is running. E.g.: EU for Europe, US for America...
+* `country_code`        - Region country code
 * `datacenter_location` - The location code of the datacenter. E.g.: "GRA", meaning Gravelines, for region "GRA1"
-* `services` - The list of public cloud services running within the region
-  * `name` - the name of the public cloud service
-  * `status` - the status of the service
+* `availability_zones`  - Availability zones of the region
+* `ip_countries`        - Allowed countries for failover IP
+* `services`            - The list of public cloud services running within the region
+  * `name`              - the name of the public cloud service
+  * `status`            - the status of the service
+  * `endpoint`          - Endpoint URL
diff --git a/templates/data-sources/cloud_project_ssh_key.md.tmpl b/templates/data-sources/cloud_project_ssh_key.md.tmpl
new file mode 100644
index 00000000..7fa9a326
--- /dev/null
+++ b/templates/data-sources/cloud_project_ssh_key.md.tmpl
@@ -0,0 +1,27 @@
+---
+subcategory: "Cloud Project"
+description: "Get SSH key"
+---
+
+# ovh_cloud_project_ssh_key (Data Source)
+
+Retrieve information about a SSH key.
+
+## Example Usage
+
+{{tffile "examples/data-sources/cloud_project_ssh_key/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `id` (String) SSH key ID
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `finger_print` (String) SSH key fingerprint
+- `name` (String) SSH key name
+- `public_key` (String) SSH public key
+- `regions` (List of String) SSH key regions
diff --git a/templates/data-sources/cloud_project_ssh_keys.md.tmpl b/templates/data-sources/cloud_project_ssh_keys.md.tmpl
new file mode 100644
index 00000000..a9aea062
--- /dev/null
+++ b/templates/data-sources/cloud_project_ssh_keys.md.tmpl
@@ -0,0 +1,37 @@
+---
+subcategory: "Cloud Project"
+description: "Get SSH keys"
+---
+
+# ovh_cloud_project_ssh_keys (Data Source)
+
+Get SSH keys in a Public Cloud project.
+
+## Example Usage
+
+{{tffile "examples/data-sources/cloud_project_ssh_keys/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Optional
+
+- `region` (String) Region
+
+### Read-Only
+
+- `ssh_keys` (Attributes Set) (see [below for nested schema](#nestedatt--ssh_keys))
+
+<a id="nestedatt--ssh_keys"></a>
+### Nested Schema for `ssh_keys`
+
+Read-Only:
+
+- `id` (String) SSH key ID
+- `name` (String) SSH key name
+- `public_key` (String) SSH public key
+- `regions` (List of String) SSH key regions
diff --git a/templates/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md.tmpl b/templates/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md.tmpl
new file mode 100644
index 00000000..53674780
--- /dev/null
+++ b/templates/data-sources/ovhcloud_connect_config_pop_datacenter_extras.md.tmpl
@@ -0,0 +1,35 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pop_datacenter_extras (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Datacenter Extra configuration
+
+## Example Usage
+
+{{tffile "examples/data-sources/ovhcloud_connect_config_pop_datacenter_extras/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `config_datacenter_id` (Number) ID of the Datacenter Configuration
+- `config_pop_id` (Number) ID of the Pop Configuration
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `extra_configs` (Attributes Set) (see [below for nested schema](#nestedatt--extra_configs))
+
+<a id="nestedatt--extra_configs"></a>
+### Nested Schema for `extra_configs`
+
+- `id` (Number) ID of the extra configuration
+- `bgp_neighbor_area` (Number) BGP AS number
+- `bgp_neighbor_ip` (String) Router IP for BGP
+- `next_hop` (String) Static route next hop
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Static route ip
+- `type` (String) Type of the configuration
diff --git a/templates/data-sources/ovhcloud_connect_config_pop_datacenters.md.tmpl b/templates/data-sources/ovhcloud_connect_config_pop_datacenters.md.tmpl
new file mode 100644
index 00000000..cfe9a5f5
--- /dev/null
+++ b/templates/data-sources/ovhcloud_connect_config_pop_datacenters.md.tmpl
@@ -0,0 +1,32 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pop_datacenters (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Datacenter configuration
+
+## Example Usage
+
+{{tffile "examples/data-sources/ovhcloud_connect_config_pop_datacenters/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `config_pop_id` (Number) ID of the Pop Configuration
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `datacenter_configs` (Attributes Set) (see [below for nested schema](#nestedatt--datacenter_configs))
+
+<a id="nestedatt--datacenter_configs"></a>
+### Nested Schema for `datacenter_configs`
+
+- `id` (Number) ID of the Datacenter configuration
+- `datacenter_id` (Number) Datacenter ID
+- `ovh_bgp_area` (Number) OVH Private AS
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Subnet should be a /28 min
diff --git a/templates/data-sources/ovhcloud_connect_config_pops.md.tmpl b/templates/data-sources/ovhcloud_connect_config_pops.md.tmpl
new file mode 100644
index 00000000..b0395ddd
--- /dev/null
+++ b/templates/data-sources/ovhcloud_connect_config_pops.md.tmpl
@@ -0,0 +1,33 @@
+---
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_config_pops (Data Source)
+
+Use this data source to retrieve information about an Ovhcloud Connect Pop configuration
+
+## Example Usage
+
+{{tffile "examples/data-sources/ovhcloud_connect_config_pops/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `pop_configs` (Attributes Set) (see [below for nested schema](#nestedatt--pop_configs))
+
+<a id="nestedatt--pop_configs"></a>
+### Nested Schema for `pop_configs`
+
+- `id` (Number) ID of the Pop Configuration
+- `customer_bgp_area` (Number) Customer Private AS
+- `interface_id` (Number) ID of the interface
+- `ovh_bgp_area` (Number) OVH Private AS
+- `status` (String) Status of the pop configuration
+- `subnet` (String) Subnet should be a /30, first IP for OVH, second IP for customer
+- `type` (String) Type of the pop configuration
diff --git a/templates/data-sources/ovhcloud_connect_datacenters.md.tmpl b/templates/data-sources/ovhcloud_connect_datacenters.md.tmpl
new file mode 100644
index 00000000..1c3824fe
--- /dev/null
+++ b/templates/data-sources/ovhcloud_connect_datacenters.md.tmpl
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+subcategory: "Ovhcloud Connect (OCC)"
+---
+
+# ovh_ovhcloud_connect_datacenters (Data Source)
+
+Use this data source to retrieve information about Datacenters for an Ovhcloud Connect product.
+
+## Example Usage
+
+{{tffile "examples/data-sources/ovhcloud_connect_datacenters/data-source.tf"}}
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Required
+
+- `service_name` (String) Service name
+
+### Read-Only
+
+- `datacenters` (Attributes Set) (see [below for nested schema](#nestedatt--datacenters))
+
+<a id="nestedatt--datacenters"></a>
+### Nested Schema for `datacenters`
+
+Required:
+
+- `id` (Number) Id
+
+Read-Only:
+
+- `available` (Boolean) Get availability to add new configuration on it
+- `name` (String) name of the datacenter
+- `region` (String) region of the datacenter
+- `region_type` (String) region type of the datacenter
diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl
index fd99df7d..711b5049 100644
--- a/templates/index.md.tmpl
+++ b/templates/index.md.tmpl
@@ -138,6 +138,14 @@ The following arguments are supported:
 
 * `consumer_key` - (Optional) The API Consumer key. If omitted, the `OVH_CONSUMER_KEY` environment variable is used.
 
+* `client_id` - (Optional) The OAuth2 client ID. If omitted, the `OVH_CLIENT_ID` environment variable is used.
+
+* `client_id` - (Optional) The OAuth2 client secret. If omitted, the `OVH_CLIENT_SECRET` environment variable is used.
+
+* `access_token` - (Optional) The OVH API Access Token. If omitted, the `OVH_ACCESS_TOKEN` environment variable is used.
+
+* `user_agent_extra` - (Optional) Extra information to append at the end of the user-agent used when making API calls.
+
 ## Terraform State storage in an OVHcloud Object Storage (S3 compatibility)
 
 In order to store your Terraform states on a High Performance (S3) OVHcloud Object Storage, please follow the [guide](https://help.ovhcloud.com/csm/en-public-cloud-compute-terraform-high-perf-object-storage-backend-state?id=kb_article_view&sysparm_article=KB0051345).
diff --git a/templates/resources/cloud_project_user.md.tmpl b/templates/resources/cloud_project_user.md.tmpl
index 76f4778b..4e880746 100644
--- a/templates/resources/cloud_project_user.md.tmpl
+++ b/templates/resources/cloud_project_user.md.tmpl
@@ -14,6 +14,11 @@ Creates a user in a public cloud project.
 
 {{tffile "examples/resources/cloud_project_user/example_1.tf"}}
 
+{{tffile "examples/resources/cloud_project_user/example_2.tf"}}
+
+-> **NOTE** To reset password of the user previously created, update the `password_reset` attribute. Use the `terraform refresh` command after executing `terraform apply` to update the output with the new password. This attribute can be an arbitrary string but we recommend 2 formats:
+- a datetime to keep a trace of the last reset
+- a md5 of other variables to automatically trigger it based on this variable update
 ## Argument Reference
 
 The following arguments are supported:
@@ -25,7 +30,8 @@ The following arguments are supported:
 * `role_name` - The name of a role. See `role_names`.
 
 * `role_names` - A list of role names. Values can be:
-  - administrator,
+  - admin
+  - administrator
   - ai_training_operator
   - ai_training_read
   - authentication
@@ -33,11 +39,16 @@ The following arguments are supported:
   - compute_operator
   - image_operator
   - infrastructure_supervisor
+  - key-manager_operator
+  - key-manager_read
+  - load-balancer_operator
   - network_operator
   - network_security_operator
   - objectstore_operator
   - volume_operator
 
+* `password_reset` - (Optional) Arbitrary string to change to trigger a password update. Use the `terraform refresh` command after executing `terraform apply` to update the output with the new password.
+
 ## Attributes Reference
 
 The following attributes are exported:
@@ -46,6 +57,7 @@ The following attributes are exported:
 * `description` - See Argument Reference above.
 * `openstack_rc` - a convenient map representing an openstack_rc file. Note: no password nor sensitive token is set in this map.
 * `password` - (Sensitive) the password generated for the user. The password can be used with the Openstack API. This attribute is sensitive and will only be retrieve once during creation.
+* `password_reset` - See Argument Reference above.
 * `roles` - A list of roles associated with the user.
   * `description` - description of the role
   * `id` - id of the role



Test patch:
diff --git a/ovh/data_cloud_project_region_test.go b/ovh/data_cloud_project_region_test.go
index f210c7b0..ffe2523d 100644
--- a/ovh/data_cloud_project_region_test.go
+++ b/ovh/data_cloud_project_region_test.go
@@ -14,28 +14,20 @@ func TestAccCloudProjectRegionDataSource_basic(t *testing.T) {
 		Providers: testAccProviders,
 		Steps: []resource.TestStep{
 			{
-				Config: testAccCloudProjectRegionDatasourceConfig,
+				Config: fmt.Sprintf(`
+					data "ovh_cloud_project_region" "region" {
+						service_name = "%s"
+						name         = "EU-WEST-PAR"
+					}
+				`, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST")),
 				Check: resource.ComposeTestCheckFunc(
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.0", "name"),
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.0", "services.#"),
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.1", "name"),
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.1", "services.#"),
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.2", "name"),
-					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region_attr.2", "services.#"),
+					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region", "name"),
+					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region", "type"),
+					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region", "status"),
+					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region", "services.#"),
+					resource.TestCheckResourceAttrSet("data.ovh_cloud_project_region.region", "availability_zones.#"),
 				),
 			},
 		},
 	})
 }
-
-var testAccCloudProjectRegionDatasourceConfig = fmt.Sprintf(`
-data "ovh_cloud_project_regions" "regions" {
-  service_name = "%s"
-}
-
-data "ovh_cloud_project_region" "region_attr" {
-  count        = 3
-  service_name = data.ovh_cloud_project_regions.regions.service_name
-  name         = element(sort(data.ovh_cloud_project_regions.regions.names), count.index)
-}
-`, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"))
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_test.go b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_test.go
new file mode 100644
index 00000000..e9068808
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenter_extras_test.go
@@ -0,0 +1,45 @@
+package ovh
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDataSourceOvhCloudConnectConfigPopDatacenterExtras_basic(t *testing.T) {
+	serviceName := os.Getenv("OVH_OCC_SERVICE_TEST")
+
+	resource.Test(t, resource.TestCase{
+		PreCheck: func() {
+			checkEnvOrSkip(t, "OVH_OCC_SERVICE_TEST")
+			testAccPreCheckCredentials(t)
+		},
+		ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+		Steps: []resource.TestStep{
+			{
+				Config: fmt.Sprintf(`
+					data "ovh_ovhcloud_connect_config_pops" "pop_cfgs" {
+						service_name = "%s"
+					}
+					data "ovh_ovhcloud_connect_config_pop_datacenters" "datacenter_cfgs" {
+						service_name = data.ovh_ovhcloud_connect_config_pops.pop_cfgs.service_name
+						config_pop_id = tolist(data.ovh_ovhcloud_connect_config_pops.pop_cfgs.pop_configs)[0].id
+					}
+					data "ovh_ovhcloud_connect_config_pop_datacenter_extras" "extra_cfgs" {
+						service_name = data.ovh_ovhcloud_connect_config_pops.pop_cfgs.service_name
+						config_pop_id = tolist(data.ovh_ovhcloud_connect_config_pops.pop_cfgs.pop_configs)[0].id
+						config_datacenter_id = tolist(data.ovh_ovhcloud_connect_config_pop_datacenters.datacenter_cfgs.datacenter_configs)[0].id
+					}
+				`, serviceName),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("data.ovh_ovhcloud_connect_config_pop_datacenter_extras.extra_cfgs", "service_name", serviceName),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenter_extras.extra_cfgs", "extra_configs.0.id"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenter_extras.extra_cfgs", "extra_configs.0.status"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenter_extras.extra_cfgs", "extra_configs.0.type"),
+				),
+			},
+		},
+	})
+}
diff --git a/ovh/data_ovhcloud_connect_config_pop_datacenters_test.go b/ovh/data_ovhcloud_connect_config_pop_datacenters_test.go
new file mode 100644
index 00000000..e786f071
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pop_datacenters_test.go
@@ -0,0 +1,41 @@
+package ovh
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDataSourceOvhCloudConnectConfigPopDatacenters_basic(t *testing.T) {
+	serviceName := os.Getenv("OVH_OCC_SERVICE_TEST")
+
+	resource.Test(t, resource.TestCase{
+		PreCheck: func() {
+			checkEnvOrSkip(t, "OVH_OCC_SERVICE_TEST")
+			testAccPreCheckCredentials(t)
+		},
+		ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+		Steps: []resource.TestStep{
+			{
+				Config: fmt.Sprintf(`
+					data "ovh_ovhcloud_connect_config_pops" "pop_cfgs" {
+						service_name = "%s"
+					}
+
+					data "ovh_ovhcloud_connect_config_pop_datacenters" "datacenter_cfgs" {
+						service_name = data.ovh_ovhcloud_connect_config_pops.pop_cfgs.service_name
+						config_pop_id = tolist(data.ovh_ovhcloud_connect_config_pops.pop_cfgs.pop_configs)[0].id
+					}
+				`, serviceName),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("data.ovh_ovhcloud_connect_config_pop_datacenters.datacenter_cfgs", "service_name", serviceName),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenters.datacenter_cfgs", "datacenter_configs.0.id"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenters.datacenter_cfgs", "datacenter_configs.0.datacenter_id"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pop_datacenters.datacenter_cfgs", "datacenter_configs.0.status"),
+				),
+			},
+		},
+	})
+}
diff --git a/ovh/data_ovhcloud_connect_config_pops_test.go b/ovh/data_ovhcloud_connect_config_pops_test.go
new file mode 100644
index 00000000..3b2978d4
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_config_pops_test.go
@@ -0,0 +1,37 @@
+package ovh
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDataSourceOvhCloudConnectConfigPops_basic(t *testing.T) {
+	serviceName := os.Getenv("OVH_OCC_SERVICE_TEST")
+
+	resource.Test(t, resource.TestCase{
+		PreCheck: func() {
+			checkEnvOrSkip(t, "OVH_OCC_SERVICE_TEST")
+			testAccPreCheckCredentials(t)
+		},
+		ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+		Steps: []resource.TestStep{
+			{
+				Config: fmt.Sprintf(`
+					data "ovh_ovhcloud_connect_config_pops" "pop_configs" {
+						service_name = "%s"
+					}
+				`, serviceName),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("data.ovh_ovhcloud_connect_config_pops.pop_configs", "service_name", serviceName),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pops.pop_configs", "pop_configs.0.id"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pops.pop_configs", "pop_configs.0.interface_id"),
+					resource.TestCheckResourceAttr("data.ovh_ovhcloud_connect_config_pops.pop_configs", "pop_configs.0.status", "active"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_config_pops.pop_configs", "pop_configs.0.type"),
+				),
+			},
+		},
+	})
+}
diff --git a/ovh/data_ovhcloud_connect_datacenters_test.go b/ovh/data_ovhcloud_connect_datacenters_test.go
new file mode 100644
index 00000000..9bca5dc9
--- /dev/null
+++ b/ovh/data_ovhcloud_connect_datacenters_test.go
@@ -0,0 +1,38 @@
+package ovh
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDataSourceOvhCloudConnectDatacenters_basic(t *testing.T) {
+	serviceName := os.Getenv("OVH_OCC_SERVICE_TEST")
+
+	resource.Test(t, resource.TestCase{
+		PreCheck: func() {
+			checkEnvOrSkip(t, "OVH_OCC_SERVICE_TEST")
+			testAccPreCheckCredentials(t)
+		},
+		ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+		Steps: []resource.TestStep{
+			{
+				Config: fmt.Sprintf(`
+					data "ovh_ovhcloud_connect_datacenters" "dcs" {
+						service_name = "%s"
+					}
+				`, serviceName),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr("data.ovh_ovhcloud_connect_datacenters.dcs", "service_name", serviceName),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_datacenters.dcs", "datacenters.0.available"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_datacenters.dcs", "datacenters.0.id"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_datacenters.dcs", "datacenters.0.name"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_datacenters.dcs", "datacenters.0.region"),
+					resource.TestCheckResourceAttrSet("data.ovh_ovhcloud_connect_datacenters.dcs", "datacenters.0.region_type"),
+				),
+			},
+		},
+	})
+}
diff --git a/ovh/resource_cloud_project_user_test.go b/ovh/resource_cloud_project_user_test.go
index 57908bdd..a47c3a92 100644
--- a/ovh/resource_cloud_project_user_test.go
+++ b/ovh/resource_cloud_project_user_test.go
@@ -102,6 +102,98 @@ func TestAccCloudProjectUser_withRoles(t *testing.T) {
 	})
 }
 
+var testAccCloudProjectUserWithRotateConfig = fmt.Sprintf(`
+resource "ovh_cloud_project_user" "user_rotate" {
+ service_name = "%s"
+ description  = "my user for acceptance tests with rotation"
+ password_reset =  "2025-04-29"
+}
+`, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"))
+
+var testAccCloudProjectUserWithRotateUpdatedConfig = fmt.Sprintf(`
+resource "ovh_cloud_project_user" "user_rotate" {
+ service_name = "%s"
+ description  = "my user for acceptance tests with rotation"
+ password_reset =  "2025-04-30"
+}
+`, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"))
+
+func TestAccCloudProjectUser_withRotate(t *testing.T) {
+	var oldPassword, newPassword string
+	var userId string
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheckCloud(t); testAccCheckCloudProjectExists(t) },
+		Providers: testAccProviders,
+		Steps: []resource.TestStep{
+			{
+				Config: testAccCloudProjectUserWithRotateConfig,
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr(
+						"ovh_cloud_project_user.user_rotate", "description", "my user for acceptance tests with rotation"),
+					testAccCheckCloudProjectUserOpenRC("ovh_cloud_project_user.user_rotate", t),
+					testAccCheckCloudProjectUserPassword("ovh_cloud_project_user.user_rotate", &oldPassword),
+					testAccCheckCloudProjectUserId("ovh_cloud_project_user.user_rotate", &userId),
+				),
+			},
+			{
+				Config: testAccCloudProjectUserWithRotateUpdatedConfig,
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr(
+						"ovh_cloud_project_user.user_rotate", "description", "my user for acceptance tests with rotation"),
+					testAccCheckCloudProjectUserOpenRC("ovh_cloud_project_user.user_rotate", t),
+					testAccCheckCloudProjectUserPassword("ovh_cloud_project_user.user_rotate", &newPassword), // Store the second password
+					testAccCheckCloudProjectUserId("ovh_cloud_project_user.user_rotate", &userId),            // Same user ID (not recreated)
+					testAccCheckCloudProjectUserDifferentPasswords(&oldPassword, &newPassword),               // Compare first and second passwords
+				),
+			},
+		},
+	})
+}
+
+func testAccCheckCloudProjectUserPassword(n string, password *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")
+		}
+
+		if rs.Primary.Attributes["password"] == "" {
+			return fmt.Errorf("No password is set")
+		}
+
+		*password = rs.Primary.Attributes["password"]
+		return nil
+	}
+}
+
+// testAccCheckCloudProjectUserDifferentPasswords creates a test check function that verifies
+// if the password has been changed during rotation by comparing old and new password values
+func testAccCheckCloudProjectUserDifferentPasswords(oldPassword *string, newPassword *string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		// Verify that we have actually captured the old password
+		if *oldPassword == "" {
+			return fmt.Errorf("Old password was not captured")
+		}
+
+		// Verify that we have actually captured the new password
+		if *newPassword == "" {
+			return fmt.Errorf("New password was not captured")
+		}
+
+		// Check that the password has actually changed
+		if *oldPassword == *newPassword {
+			return fmt.Errorf("Password did not change after rotation (both are '%s')", *oldPassword)
+		}
+
+		return nil
+	}
+}
+
 func testAccCheckCloudProjectUserOpenRC(n string, t *testing.T) resource.TestCheckFunc {
 	return func(s *terraform.State) error {
 		rs, ok := s.RootModule().Resources[n]
@@ -132,3 +224,19 @@ func testAccCheckCloudProjectUserOpenRC(n string, t *testing.T) resource.TestChe
 		return nil
 	}
 }
+
+func testAccCheckCloudProjectUserId(n string, userId *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")
+		}
+
+		*userId = rs.Primary.ID
+		return nil
+	}
+}