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
39 changes: 39 additions & 0 deletions examples/oidc_custom_property/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
terraform {
required_providers {
github = {
source = "integrations/github"
}
}
}

provider "github" {
owner = var.github_owner
}

variable "github_owner" {
description = "The GitHub organization to manage"
type = string
}

# Step 1: Define an org-level custom property
resource "github_organization_custom_properties" "environment" {
property_name = "environment"
value_type = "single_select"
required = false
allowed_values = ["production", "staging", "development"]
}

# Step 2: Include the custom property in OIDC tokens
resource "github_actions_organization_oidc_custom_property_inclusion" "environment" {
custom_property_name = "environment"
depends_on = [github_organization_custom_properties.environment]
}

# Step 3: Read back the inclusions via data source
data "github_actions_organization_oidc_custom_property_inclusions" "current" {
depends_on = [github_actions_organization_oidc_custom_property_inclusion.environment]
}

output "included_properties" {
value = data.github_actions_organization_oidc_custom_property_inclusions.current.custom_property_names
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package github

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceGithubActionsOrganizationOIDCCustomPropertyInclusions() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceGithubActionsOrganizationOIDCCustomPropertyInclusionsRead,

Schema: map[string]*schema.Schema{
"custom_property_names": {
Type: schema.TypeList,
Computed: true,
Description: "A list of custom property names included in the OIDC token.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func dataSourceGithubActionsOrganizationOIDCCustomPropertyInclusionsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name

err := checkOrganization(meta)
if err != nil {
return diag.FromErr(err)
}

inclusions, err := listOrgOIDCCustomPropertyInclusions(ctx, client, orgName)
if err != nil {
return diag.FromErr(err)
}

propertyNames := make([]string, len(inclusions))
for i, inclusion := range inclusions {
propertyNames[i] = inclusion.PropertyName
}

d.SetId(orgName)
if err := d.Set("custom_property_names", propertyNames); err != nil {
return diag.FromErr(err)
}

return nil
}
2 changes: 2 additions & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func Provider() *schema.Provider {
"github_actions_environment_secret": resourceGithubActionsEnvironmentSecret(),
"github_actions_environment_variable": resourceGithubActionsEnvironmentVariable(),
"github_actions_organization_oidc_subject_claim_customization_template": resourceGithubActionsOrganizationOIDCSubjectClaimCustomizationTemplate(),
"github_actions_organization_oidc_custom_property_inclusion": resourceGithubActionsOrganizationOIDCCustomPropertyInclusion(),
"github_actions_organization_permissions": resourceGithubActionsOrganizationPermissions(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_organization_secret_repositories": resourceGithubActionsOrganizationSecretRepositories(),
Expand Down Expand Up @@ -225,6 +226,7 @@ func Provider() *schema.Provider {
"github_actions_environment_secrets": dataSourceGithubActionsEnvironmentSecrets(),
"github_actions_environment_variables": dataSourceGithubActionsEnvironmentVariables(),
"github_actions_organization_oidc_subject_claim_customization_template": dataSourceGithubActionsOrganizationOIDCSubjectClaimCustomizationTemplate(),
"github_actions_organization_oidc_custom_property_inclusions": dataSourceGithubActionsOrganizationOIDCCustomPropertyInclusions(),
"github_actions_organization_public_key": dataSourceGithubActionsOrganizationPublicKey(),
"github_actions_organization_registration_token": dataSourceGithubActionsOrganizationRegistrationToken(),
"github_actions_organization_secrets": dataSourceGithubActionsOrganizationSecrets(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package github

import (
"context"
"fmt"
"log"

"github.com/google/go-github/v84/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

const oidcCustomPropertyAPIVersion = "2026-03-10"

func resourceGithubActionsOrganizationOIDCCustomPropertyInclusion() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsOrganizationOIDCCustomPropertyInclusionCreate,
Read: resourceGithubActionsOrganizationOIDCCustomPropertyInclusionRead,
Delete: resourceGithubActionsOrganizationOIDCCustomPropertyInclusionDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
"custom_property_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the custom property to include in the OIDC token.",
},
},
}
}

func resourceGithubActionsOrganizationOIDCCustomPropertyInclusionCreate(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.Background()

err := checkOrganization(meta)
if err != nil {
return err
}

customPropertyName := d.Get("custom_property_name").(string)

body := map[string]string{
"custom_property_name": customPropertyName,
}

req, err := client.NewRequest("POST", fmt.Sprintf("orgs/%s/actions/oidc/customization/properties/repo", orgName), body)
if err != nil {
return fmt.Errorf("error creating request to add OIDC custom property inclusion: %w", err)
}
req.Header.Set("X-GitHub-Api-Version", oidcCustomPropertyAPIVersion)

_, err = client.Do(ctx, req, nil)
if err != nil {
return fmt.Errorf("error adding OIDC custom property inclusion %q for organization %q: %w", customPropertyName, orgName, err)
}

log.Printf("[DEBUG] Successfully added OIDC custom property inclusion %q for organization %q", customPropertyName, orgName)

d.SetId(buildTwoPartID(orgName, customPropertyName))

return resourceGithubActionsOrganizationOIDCCustomPropertyInclusionRead(d, meta)
}

func resourceGithubActionsOrganizationOIDCCustomPropertyInclusionRead(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
ctx := context.Background()

orgName, customPropertyName, err := parseTwoPartID(d.Id(), "organization", "custom_property_name")
if err != nil {
return err
}

err = checkOrganization(meta)
if err != nil {
return err
}

inclusions, err := listOrgOIDCCustomPropertyInclusions(ctx, client, orgName)
if err != nil {
return fmt.Errorf("error reading OIDC custom property inclusions for organization %q: %w", orgName, err)
}

found := false
for _, inclusion := range inclusions {
if inclusion.PropertyName == customPropertyName {
found = true
break
}
}

if !found {
d.SetId("")
return nil
}

if err := d.Set("custom_property_name", customPropertyName); err != nil {
return err
}

return nil
}

func resourceGithubActionsOrganizationOIDCCustomPropertyInclusionDelete(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
ctx := context.Background()

orgName, customPropertyName, err := parseTwoPartID(d.Id(), "organization", "custom_property_name")
if err != nil {
return err
}

err = checkOrganization(meta)
if err != nil {
return err
}

req, err := client.NewRequest("DELETE", fmt.Sprintf("orgs/%s/actions/oidc/customization/properties/repo/%s", orgName, customPropertyName), nil)
if err != nil {
return fmt.Errorf("error creating request to delete OIDC custom property inclusion: %w", err)
}
req.Header.Set("X-GitHub-Api-Version", oidcCustomPropertyAPIVersion)

_, err = client.Do(ctx, req, nil)
if err != nil {
return fmt.Errorf("error deleting OIDC custom property inclusion %q for organization %q: %w", customPropertyName, orgName, err)
}

return nil
}

// OIDCCustomPropertyInclusion represents a custom property included in OIDC tokens.
type OIDCCustomPropertyInclusion struct {
PropertyName string `json:"custom_property_name"`
InclusionSource string `json:"inclusion_source,omitempty"`
}

// listOrgOIDCCustomPropertyInclusions lists all custom properties included in OIDC tokens for an organization.
func listOrgOIDCCustomPropertyInclusions(ctx context.Context, client *github.Client, orgName string) ([]*OIDCCustomPropertyInclusion, error) {
req, err := client.NewRequest("GET", fmt.Sprintf("orgs/%s/actions/oidc/customization/properties/repo", orgName), nil)
if err != nil {
return nil, err
}
req.Header.Set("X-GitHub-Api-Version", oidcCustomPropertyAPIVersion)

var inclusions []*OIDCCustomPropertyInclusion
_, err = client.Do(ctx, req, &inclusions)
if err != nil {
return nil, err
}

return inclusions, nil
}
Loading