Skip to content
Open
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

## Unreleased

### Improvements

- Added Service resource [#522](https://github.com/pulumi/pulumi-pulumiservice/issues/522)

### Bug Fixes

- Fixed OIDC issuer examples: removed unsupported runner token type and updated Pulumi OIDC thumbprint
- Fixed OIDC issuer examples: removed unsupported runner token type and updated Pulumi OIDC thumbprint

## 0.31.0

Expand Down
1 change: 1 addition & 0 deletions examples/examples_nodejs_test.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: seems like an accidental change here.

Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,4 @@ func TestNodejsApprovalRulesExample(t *testing.T) {
},
})
}

16 changes: 16 additions & 0 deletions examples/examples_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,19 @@ func getCwd(t *testing.T) string {
func generateRandomFiveDigits() string {
return fmt.Sprintf("%05d", rand.Intn(100000))
}

func getOrgName(t *testing.T) string {
orgName := os.Getenv("PULUMI_TEST_OWNER")
if orgName == "" {
orgName = "service-provider-test-org"
}
return orgName
}

func getOwnerName(t *testing.T) string {
ownerName := os.Getenv("PULUMI_TEST_SERVICE_OWNER_USER_NAME")
if ownerName == "" {
t.Skip("PULUMI_TEST_SERVICE_OWNER_USER_NAME not set, skipping Service example test")
}
return ownerName
}
15 changes: 15 additions & 0 deletions examples/examples_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,21 @@ func TestYamlOidcIssuerExample(t *testing.T) {
})
}

func TestYamlServiceExample(t *testing.T) {
cwd := getCwd(t)
orgName := getOrgName(t)
ownerName := getOwnerName(t)
digits := generateRandomFiveDigits()
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: path.Join(cwd, ".", "yaml-service"),
StackName: "test-stack-" + digits,
Config: map[string]string{
"orgName": orgName,
"ownerName": ownerName,
},
})
}

func writePulumiYaml(t *testing.T, yamlContents interface{}) string {
tmpdir := t.TempDir()
b, err := yaml.Marshal(yamlContents)
Expand Down
12 changes: 4 additions & 8 deletions examples/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ import (
)

func TestMain(m *testing.M) {
// Set default test owner if not already set
if os.Getenv("PULUMI_TEST_OWNER") == "" {
if err := os.Setenv("PULUMI_TEST_OWNER", "service-provider-test-org"); err != nil {
panic("failed to set PULUMI_TEST_OWNER: " + err.Error())
}
}
if err := os.Setenv("PULUMI_TEST_USE_SERVICE", "true"); err != nil {
panic("failed to set PULUMI_TEST_USE_SERVICE: " + err.Error())
testOwner := os.Getenv("PULUMI_TEST_OWNER")
if testOwner == "" {
_ = os.Setenv("PULUMI_TEST_OWNER", "service-provider-test-org")
}
_ = os.Setenv("PULUMI_TEST_USE_SERVICE", "true")
m.Run()
}
73 changes: 73 additions & 0 deletions examples/yaml-service/Pulumi.yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Service's main purpose is to group entities within it. Should the example include at least one or two entities?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to add parts that would be part of the service then come back to this PR so we can tie it all together. This makes a lot of sense to show a full working example.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: yaml-service
runtime: yaml
description: |
Example of provisioning a Service with items via Pulumi YAML.

This example demonstrates THREE ways to specify the organization:

1. AUTOMATIC (provider default): Omit organizationName - provider uses the first
organization from your Pulumi Cloud account where you are a member/admin.

2. EXPLICIT API (getCurrentUser): Call getCurrentUser() to get your default org
from Pulumi Cloud API and reference it explicitly.

3. STACK CONTEXT (pulumi.organization): Use the organization from your current
stack deployment context (e.g., if deploying org/project/stack, uses "org").
Note: This requires using the Pulumi SDK function in your language, not shown
in this YAML example. See TypeScript/Python examples for usage of
pulumi.getOrganization().

configuration:
ownerName:
type: string
projectName:
type: string
stackName:
type: string

variables:
# Get current user info from Pulumi Cloud API
# Returns the user's default organization (first org where they're a member/admin)
currentUser:
fn::invoke:
function: pulumiservice:index:getCurrentUser
return: defaultOrganization

resources:
# Example 1: Explicit API approach using getCurrentUser()
# Good for: When you want to be explicit about which org from your account to use
myServiceExplicit:
type: pulumiservice:index:Service
properties:
organizationName: ${currentUser}
ownerType: user
ownerName: ${ownerName}
name: example-service-explicit
description: Service using explicit defaultOrganization from getCurrentUser
properties:
environment: development
team: platform
items:
- itemType: stack
name: ${currentUser}/${projectName}/${stackName}

# Example 2: Automatic approach (provider infers from API)
# Good for: Quick prototyping, single-org scenarios
myServiceAutomatic:
type: pulumiservice:index:Service
properties:
# organizationName omitted - provider automatically uses default org from your account
ownerType: user
ownerName: ${ownerName}
name: example-service-automatic
description: Service using automatic default organization
properties:
environment: development
team: platform

outputs:
explicitServiceName: ${myServiceExplicit.name}
explicitOrganization: ${myServiceExplicit.organizationName}
automaticServiceName: ${myServiceAutomatic.name}
automaticOrganization: ${myServiceAutomatic.organizationName}
userDefaultOrg: ${currentUser}
115 changes: 115 additions & 0 deletions examples/yaml-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Service Resource Example

This example demonstrates how to create and manage a Service resource in Pulumi Cloud.

## Overview

Services in Pulumi Cloud allow you to group and organize related stacks and environments. This example shows three different approaches for specifying the organization:

### 1. Automatic Default (Provider Infers)
The simplest approach - omit `organizationName` and the provider will automatically use the first organization from your Pulumi Cloud account where you are a member, admin, or billing manager.

```yaml
resources:
myService:
type: pulumiservice:index:Service
properties:
# organizationName omitted - uses default from your account
ownerType: user
ownerName: ${ownerName}
name: my-service
```

### 2. Explicit API Call (`getCurrentUser`)
Use the `getCurrentUser()` function to explicitly retrieve and reference your default organization from the Pulumi Cloud API:

```yaml
variables:
currentUser:
fn::invoke:
function: pulumiservice:index:getCurrentUser
return: defaultOrganization

resources:
myService:
type: pulumiservice:index:Service
properties:
organizationName: ${currentUser} # Explicitly reference
ownerType: user
ownerName: ${ownerName}
name: my-service
```

### 3. Stack Context (`pulumi.getOrganization()`)
In TypeScript, Python, and other SDK languages, you can use the stack's deployment context:

**TypeScript:**
```typescript
const service = new pulumiservice.Service("myService", {
organizationName: pulumi.getOrganization(), // Uses org from stack context
ownerType: "user",
ownerName: ownerName,
name: "my-service",
});
```

**Python:**
```python
service = pulumi_service.Service("my-service",
organization_name=pulumi.get_organization(), # Uses org from stack context
owner_type="user",
owner_name=owner_name,
name="my-service"
)
```

## When to Use Each Approach

- **Automatic**: Best for quick prototyping or when you only have one organization
- **getCurrentUser()**: Best when you want to be explicit and reference your default org from multiple places
- **pulumi.getOrganization()**: Best when deploying to different orgs (the stack context determines the org)

## Difference Between API Default and Stack Context

- **getCurrentUser() / Automatic**: Returns the first org from your **Pulumi Cloud account** (e.g., `personal-org`)
- **pulumi.getOrganization()**: Returns the org from your **current stack deployment** (e.g., if deploying `work-org/infra/prod`, returns `work-org`)

If you're deploying `work-org/project/stack` but your default account org is `personal-org`:
- `getCurrentUser()` → `personal-org`
- `pulumi.getOrganization()` → `work-org`

## Running the Example

The example requires minimal configuration since most values are derived automatically:

```bash
# Only set the owner username (should be your Pulumi Cloud username)
pulumi config set ownerName <your-pulumi-username>

# The rest are handled automatically:
# - organizationName: Derived from getCurrentUser() or omitted for automatic default
# - projectName/stackName: Used only in the item name, can use pulumi.getProject()/getStack() in other languages

pulumi up
```

**Note**: In a real-world scenario using TypeScript or Python, you'd typically use:
```typescript
ownerName: pulumi.getOrganization() // Gets current user/org from stack context
projectName: pulumi.getProject() // Gets project from stack
stackName: pulumi.getStack() // Gets stack name
```

So you wouldn't need to configure these at all - they come from your deployment context automatically.

## Converting to Other Languages

This YAML example can be converted to other Pulumi languages using:

```bash
pulumi convert --language typescript --out ts-service
pulumi convert --language python --out py-service
pulumi convert --language go --out go-service
```

See the [Pulumi convert documentation](https://www.pulumi.com/docs/iac/cli/commands/pulumi_convert/) for more details.
Loading