Create and Manage GCP Projects

The gcp:organizations/project:Project resource, part of the Pulumi GCP provider, creates a GCP project and places it within an organization or folder hierarchy. This guide focuses on three capabilities: organization-level project creation, folder-based organization, and resource manager tagging.

Projects must belong to a GCP organization and require IAM permissions (roles/resourcemanager.projectCreator). The examples are intentionally small. Combine them with your own billing accounts, network configuration, and labels.

Create a project under an organization

Most deployments start by creating a project directly under an organization, establishing the top-level container for resources and billing.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const myProject = new gcp.organizations.Project("my_project", {
    name: "My Project",
    projectId: "your-project-id",
    orgId: "1234567",
});
import pulumi
import pulumi_gcp as gcp

my_project = gcp.organizations.Project("my_project",
    name="My Project",
    project_id="your-project-id",
    org_id="1234567")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := organizations.NewProject(ctx, "my_project", &organizations.ProjectArgs{
			Name:      pulumi.String("My Project"),
			ProjectId: pulumi.String("your-project-id"),
			OrgId:     pulumi.String("1234567"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var myProject = new Gcp.Organizations.Project("my_project", new()
    {
        Name = "My Project",
        ProjectId = "your-project-id",
        OrgId = "1234567",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var myProject = new Project("myProject", ProjectArgs.builder()
            .name("My Project")
            .projectId("your-project-id")
            .orgId("1234567")
            .build());

    }
}
resources:
  myProject:
    type: gcp:organizations:Project
    name: my_project
    properties:
      name: My Project
      projectId: your-project-id
      orgId: '1234567'

The projectId must be globally unique across all GCP projects. The orgId places the project at the organization’s top level. The name property sets the human-readable display name shown in the console.

Create a project under a folder

Organizations often group projects into folders to reflect departmental structure or environment boundaries.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const department1 = new gcp.organizations.Folder("department1", {
    displayName: "Department 1",
    parent: "organizations/1234567",
});
const myProject_in_a_folder = new gcp.organizations.Project("my_project-in-a-folder", {
    name: "My Project",
    projectId: "your-project-id",
    folderId: department1.name,
});
import pulumi
import pulumi_gcp as gcp

department1 = gcp.organizations.Folder("department1",
    display_name="Department 1",
    parent="organizations/1234567")
my_project_in_a_folder = gcp.organizations.Project("my_project-in-a-folder",
    name="My Project",
    project_id="your-project-id",
    folder_id=department1.name)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		department1, err := organizations.NewFolder(ctx, "department1", &organizations.FolderArgs{
			DisplayName: pulumi.String("Department 1"),
			Parent:      pulumi.String("organizations/1234567"),
		})
		if err != nil {
			return err
		}
		_, err = organizations.NewProject(ctx, "my_project-in-a-folder", &organizations.ProjectArgs{
			Name:      pulumi.String("My Project"),
			ProjectId: pulumi.String("your-project-id"),
			FolderId:  department1.Name,
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var department1 = new Gcp.Organizations.Folder("department1", new()
    {
        DisplayName = "Department 1",
        Parent = "organizations/1234567",
    });

    var myProject_in_a_folder = new Gcp.Organizations.Project("my_project-in-a-folder", new()
    {
        Name = "My Project",
        ProjectId = "your-project-id",
        FolderId = department1.Name,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Folder;
import com.pulumi.gcp.organizations.FolderArgs;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var department1 = new Folder("department1", FolderArgs.builder()
            .displayName("Department 1")
            .parent("organizations/1234567")
            .build());

        var myProject_in_a_folder = new Project("myProject-in-a-folder", ProjectArgs.builder()
            .name("My Project")
            .projectId("your-project-id")
            .folderId(department1.name())
            .build());

    }
}
resources:
  myProject-in-a-folder:
    type: gcp:organizations:Project
    name: my_project-in-a-folder
    properties:
      name: My Project
      projectId: your-project-id
      folderId: ${department1.name}
  department1:
    type: gcp:organizations:Folder
    properties:
      displayName: Department 1
      parent: organizations/1234567

The folderId property places the project within a folder hierarchy instead of directly under the organization. Only one of orgId or folderId may be specified. The Folder resource creates the parent folder, which itself references the organization.

Apply resource manager tags at creation

Resource manager tags provide organization-wide metadata for governance, cost allocation, and conditional access policies.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const myProject = new gcp.organizations.Project("my_project", {
    name: "My Project",
    projectId: "your-project-id",
    orgId: "1234567",
    tags: {
        "1234567/env": "staging",
    },
});
import pulumi
import pulumi_gcp as gcp

my_project = gcp.organizations.Project("my_project",
    name="My Project",
    project_id="your-project-id",
    org_id="1234567",
    tags={
        "1234567/env": "staging",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := organizations.NewProject(ctx, "my_project", &organizations.ProjectArgs{
			Name:      pulumi.String("My Project"),
			ProjectId: pulumi.String("your-project-id"),
			OrgId:     pulumi.String("1234567"),
			Tags: pulumi.StringMap{
				"1234567/env": pulumi.String("staging"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var myProject = new Gcp.Organizations.Project("my_project", new()
    {
        Name = "My Project",
        ProjectId = "your-project-id",
        OrgId = "1234567",
        Tags = 
        {
            { "1234567/env", "staging" },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var myProject = new Project("myProject", ProjectArgs.builder()
            .name("My Project")
            .projectId("your-project-id")
            .orgId("1234567")
            .tags(Map.of("1234567/env", "staging"))
            .build());

    }
}
resources:
  myProject:
    type: gcp:organizations:Project
    name: my_project
    properties:
      name: My Project
      projectId: your-project-id
      orgId: '1234567'
      tags:
        1234567/env: staging

The tags property applies resource manager tags at project creation. Tag keys must follow the format tagKeys/{tag_key_id} and values must use tagValues/{tag_value_id}. Tags are immutable; changing them after creation triggers project recreation. For mutable metadata, use the labels property instead.

Beyond these examples

These snippets focus on specific project-level features: organization and folder placement, and resource manager tags. They’re intentionally minimal rather than full project configurations.

The examples require pre-existing infrastructure such as a GCP organization with numeric ID, IAM permissions (roles/resourcemanager.projectCreator), and resource manager tag keys for the tagging example. They focus on project placement and identity rather than provisioning billing or networking.

To keep things focused, common project patterns are omitted, including:

  • Billing account attachment (billingAccount)
  • Default network control (autoCreateNetwork)
  • Labels for resource-level metadata
  • Deletion policies and lifecycle management

These omissions are intentional: the goal is to illustrate how project placement and tagging are wired, not provide drop-in project modules. See the GCP Project resource reference for all available configuration options.

Let's create and Manage GCP Projects

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Project Creation & Hierarchy
What properties can't I change after creating a project?
The projectId and tags properties are immutable. Changing projectId forces a new project to be created, and modifying tags triggers resource replacement. To apply tags to an existing project, use the gcp.tags.TagValue resource instead.
What's the difference between orgId and folderId?
Only one can be specified. Use orgId to create the project at the organization’s top level, or folderId to create it under a specific folder. Changing orgId forces a new project, while changing folderId migrates the project to the new folder.
Can I move a project between organizations or folders?
Yes, but with different behaviors. Changing orgId forces a new project to be created, while changing folderId migrates the existing project to the new folder.
Permissions & Billing
What IAM permissions do I need to create a project?
You need roles/resourcemanager.projectCreator on the organization and at least roles/billing.user on the billing account.
Why do I need billing account permissions for every pulumi up?
This resource reads the billing account on every pulumi up and pulumi preview operation, so you must maintain permissions on the billing account throughout the project’s lifecycle.
Networking & Default Resources
Why does setting autoCreateNetwork to false still require a network quota slot?
Even when autoCreateNetwork is false, you need 1 network slot available because the default network exists momentarily before being deleted. The recommended approach is to use the constraints/compute.skipDefaultNetworkCreation organization policy constraint instead.
What's the recommended way to prevent default network creation?
Use the constraints/compute.skipDefaultNetworkCreation organization policy constraint rather than setting autoCreateNetwork to false.
Labels & Tags
What does it mean that labels are non-authoritative?
The labels field only manages labels present in your Pulumi configuration. Other labels applied through different clients or services won’t be removed. Use effectiveLabels to see all labels present on the resource.
How long does it take to delete a project with tags?
Tag bindings may take a while to delete after the project is scheduled for deletion, potentially extending the deletion time.
How do I add tags to an existing project?
You can’t modify the tags property after creation, as it’s immutable and triggers resource replacement. Instead, use the gcp.tags.TagValue resource to apply tags to existing projects.

Using a different cloud?

Explore integration guides for other cloud providers: