Skip to content

feat(project): add the projects datasource #3084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions docs/data-sources/account_projects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
subcategory: "Account"
page_title: "Scaleway: scaleway_account_projects"
---

# scaleway_account_projects

The `scaleway_account_projects` data source is used to list all Scaleway projects in an Organization.

Refer to the Organizations and Projects [documentation](https://www.scaleway.com/en/docs/organizations-and-projects/) and [API documentation](https://www.scaleway.com/en/developers/api/account/project-api/) for more information.


## Retrieve a Scaleway Projects

The following commands allow you to:

- retrieve all Projects in an Organization

```hcl
# Get all Projects in an Organization
data scaleway_account_projects "all" {
organization_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

## Example Usage

### Deploy an SSH key in all your organization's projects

```hcl
data scaleway_account_projects "all" {}

resource "scaleway_account_ssh_key" "main" {
name = "main"
public_key = local.public_key
count = length(data.scaleway_account_projects.all.projects)
project_id = data.scaleway_account_projects.all.projects[count.index].id
}
```

## Argument Reference

- `organization_id` - (Optional) The unique identifier of the Organization with which the Projects are associated.
If no default `organization_id` is set, one must be set explicitly in this datasource


## Attribute reference

The `scaleway_account_projects` data source exports the following attributes:

- `projects` - (Computed) A list of projects. Each project has the following attributes:
- `id` - (Computed) The unique identifier of the project.
- `name` - (Computed) The name of the project.
- `organization_id` - (Computed) The unique identifier of the organization with which the project is associated.
- `created_at` - (Computed) The date and time when the project was created.
- `updated_at` - (Computed) The date and time when the project was updated.
- `description` - (Computed) The description of the project.
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ func Provider(config *Config) plugin.ProviderFunc {

DataSourcesMap: map[string]*schema.Resource{
"scaleway_account_project": account.DataSourceProject(),
"scaleway_account_projects": account.DataSourceProjects(),
"scaleway_account_ssh_key": iam.DataSourceSSHKey(),
"scaleway_availability_zones": az.DataSourceAvailabilityZones(),
"scaleway_baremetal_offer": baremetal.DataSourceOffer(),
Expand Down
105 changes: 104 additions & 1 deletion internal/services/account/project_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package account
import (
"context"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
accountSDK "github.com/scaleway/scaleway-sdk-go/api/account/v3"
Expand All @@ -22,7 +23,7 @@ func DataSourceProject() *schema.Resource {
Type: schema.TypeString,
Computed: true,
Optional: true,
Description: "The ID of the SSH key",
Description: "The ID of the project",
ValidateDiagFunc: verify.IsUUID(),
}

Expand Down Expand Up @@ -85,3 +86,105 @@ func DataSourceAccountProjectRead(ctx context.Context, d *schema.ResourceData, m

return nil
}

func DataSourceProjects() *schema.Resource {
dsSchema := datasource.SchemaFromResourceSchema(ResourceProject().Schema)
datasource.AddOptionalFieldsToSchema(dsSchema, "organization_id")

dsSchema["organization_id"] = &schema.Schema{
Type: schema.TypeString,
Computed: true,
Optional: true,
Description: "The ID of the organization",
ValidateDiagFunc: verify.IsUUID(),
}
dsSchema["projects"] = &schema.Schema{
Type: schema.TypeList,
Computed: true,
Description: "The list of projects",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
Description: "ID of the Project",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the Project",
},
"organization_id": {
Type: schema.TypeString,
Computed: true,
Description: "Organization ID of the Project",
},
"created_at": {
Type: schema.TypeString,
Computed: true,
Description: "Creation date of the Project",
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
Description: "Update date of the Project",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "Description of the Project",
},
},
},
}

return &schema.Resource{
ReadContext: DataSourceAccountProjectsRead,
Schema: dsSchema,
}
}

func DataSourceAccountProjectsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
accountAPI := NewProjectAPI(m)

var orgID *string

if v, orgIDExists := d.GetOk("organization_id"); orgIDExists {
orgID = types.ExpandStringPtr(v)
} else {
orgID = GetOrganizationID(m, d)
}

if orgID == nil {
return diag.Errorf("organization_id was not specified nor found in the provider configuration")
}

res, err := accountAPI.ListProjects(&accountSDK.ProjectAPIListProjectsRequest{
OrganizationID: *orgID,
}, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

d.SetId(uuid.New().String())
_ = d.Set("projects", flattenProjects(res.Projects))
_ = d.Set("organization_id", orgID)

return nil
}

func flattenProjects(projects []*accountSDK.Project) []map[string]interface{} {
flattenedProjects := make([]map[string]interface{}, len(projects))
for i, project := range projects {
flattenedProjects[i] = map[string]interface{}{
"id": project.ID,
"name": project.Name,
"organization_id": project.OrganizationID,
"created_at": types.FlattenTime(project.CreatedAt),
"updated_at": types.FlattenTime(project.UpdatedAt),
"description": project.Description,
}
}

return flattenedProjects
}
41 changes: 38 additions & 3 deletions internal/services/account/project_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import (
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
)

const dummyOrgID = "AB7BD9BF-E1BD-41E8-9F1D-F16A2E3F3925"

func TestAccDataSourceProject_Basic(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

orgID, orgIDExists := tt.Meta.ScwClient().GetDefaultOrganizationID()
if !orgIDExists {
orgID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
orgID = dummyOrgID
}

resource.ParallelTest(t, resource.TestCase{
Expand All @@ -34,7 +36,7 @@ func TestAccDataSourceProject_Basic(t *testing.T) {
name = scaleway_account_project.project.name
organization_id = "%s"
}

data scaleway_account_project "by_id" {
project_id = scaleway_account_project.project.id
}`, orgID),
Expand All @@ -55,7 +57,7 @@ func TestAccDataSourceProject_Default(t *testing.T) {

orgID, orgIDExists := tt.Meta.ScwClient().GetDefaultOrganizationID()
if !orgIDExists {
orgID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
orgID = dummyOrgID
}

resource.ParallelTest(t, resource.TestCase{
Expand Down Expand Up @@ -116,3 +118,36 @@ func TestAccDataSourceProject_Extract(t *testing.T) {
},
})
}

func TestAccDataSourceProject_List(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

orgID, orgIDExists := tt.Meta.ScwClient().GetDefaultOrganizationID()
if !orgIDExists {
orgID = dummyOrgID
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
isProjectDestroyed(tt),
),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
data scaleway_account_projects "projects" {
organization_id = "%s"
}`, orgID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.scaleway_account_projects.projects", "projects.#", "8"),
resource.TestCheckResourceAttr("data.scaleway_account_projects.projects", "projects.0.id", "6867048b-fe12-4e96-835e-41c79a39604b"),
resource.TestCheckResourceAttr("data.scaleway_account_projects.projects", "projects.1.id", "8cc8dd4d-a094-407a-89a3-9d004674e936"),
resource.TestCheckResourceAttr("data.scaleway_account_projects.projects", "projects.0.name", "default"),
resource.TestCheckResourceAttr("data.scaleway_account_projects.projects", "projects.1.name", "tf_tests_container_trigger_sqs"),
),
},
},
})
}
Loading
Loading