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
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ resource "github_actions_repository_oidc_subject_claim_customization_template" "

The following arguments are supported:

- `repository` - (Required) The name of the repository.
- `use_default` - (Required) Whether to use the default template or not. If `true`, `include_claim_keys` must not be set.
- `include_claim_keys` - (Optional) A list of OpenID Connect claims.

## Attributes Reference

- `repository_id` - The ID of the repository.

## Import

This resource can be imported using the repository's name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,50 @@ package github
import (
"context"
"errors"
"net/http"

"github.com/google/go-github/v92/github"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplate() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateOrUpdate,
Read: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead,
Update: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateOrUpdate,
Delete: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete,
CreateContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreate,
ReadContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead,
UpdateContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateUpdate,
DeleteContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
StateContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateImport,
},

CustomizeDiff: diffRepository,

SchemaVersion: 1,
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateResourceV0().CoreConfigSchema().ImpliedType(),
Upgrade: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateInstanceStateUpgradeV0,
Version: 0,
},
},

Description: "Creates and manages an OpenID Connect subject claim customization template for a repository.",

Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
Description: "The name of the repository.",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringLenBetween(1, 100)),
},
"repository_id": {
Type: schema.TypeInt,
Computed: true,
Description: "The ID of the repository.",
},
"use_default": {
Type: schema.TypeBool,
Required: true,
Expand All @@ -43,88 +65,152 @@ func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplate() *sch
}
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateOrUpdate(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

repository := d.Get("repository").(string)
owner := meta.(*Owner).name
repoName := d.Get("repository").(string)

useDefault := d.Get("use_default").(bool)
includeClaimKeys, hasClaimKeys := d.GetOk("include_claim_keys")
template, err := expandRepositoryOIDCSubjectClaimCustomTemplate(d)
if err != nil {
return diag.FromErr(err)
}

if useDefault && hasClaimKeys {
return errors.New("include_claim_keys cannot be set when use_default is true")
tflog.Debug(ctx, "Creating repository OIDC subject claim customization template", map[string]any{"repository": repoName})

if _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repoName, template); err != nil {
return diag.FromErr(err)
}

repo, _, err := client.Repositories.Get(ctx, owner, repoName)
if err != nil {
return diag.FromErr(err)
}

customOIDCSubjectClaimTemplate := github.OIDCSubjectClaimCustomTemplate{
UseDefault: &useDefault,
d.SetId(repoName)

if err := d.Set("repository_id", int(repo.GetID())); err != nil {
return diag.FromErr(err)
}

if includeClaimKeys != nil {
return nil
}

includeClaimKeysVal := includeClaimKeys.([]any)
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

claimsStr := make([]string, len(includeClaimKeysVal))
repoName := d.Get("repository").(string)

for i, v := range includeClaimKeysVal {
claimsStr[i] = v.(string)
template, _, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repoName)
if err != nil {
if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response.StatusCode == http.StatusNotFound {
tflog.Info(ctx, "Removing repository OIDC subject claim customization template from state because it no longer exists in GitHub", map[string]any{"repository": repoName})
d.SetId("")
return nil
}

customOIDCSubjectClaimTemplate.IncludeClaimKeys = claimsStr
return diag.FromErr(err)
}

ctx := context.Background()
_, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository, customOIDCSubjectClaimTemplate)
if err != nil {
return err
if err := d.Set("use_default", template.UseDefault); err != nil {
return diag.FromErr(err)
}
if err := d.Set("include_claim_keys", template.IncludeClaimKeys); err != nil {
return diag.FromErr(err)
}

d.SetId(repository)
return resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(d, meta)
return nil
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

repository := d.Id()
owner := meta.(*Owner).name
repoName := d.Get("repository").(string)

ctx := context.Background()
template, _, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository)
template, err := expandRepositoryOIDCSubjectClaimCustomTemplate(d)
if err != nil {
return deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "actions repository oidc subject claim customization template (%s, %s)", owner, repository)
return diag.FromErr(err)
}

if err = d.Set("repository", repository); err != nil {
return err
}
if err = d.Set("use_default", template.UseDefault); err != nil {
return err
}
if err = d.Set("include_claim_keys", template.IncludeClaimKeys); err != nil {
return err
tflog.Debug(ctx, "Updating repository OIDC subject claim customization template", map[string]any{"repository": repoName})

if _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repoName, template); err != nil {
return diag.FromErr(err)
}

d.SetId(repoName)

return nil
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete(d *schema.ResourceData, meta any) error {
// Reset the repository to use the default claims
// https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#using-the-default-subject-claims
client := meta.(*Owner).v3client
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

repoName := d.Get("repository").(string)

repository := d.Get("repository").(string)
owner := meta.(*Owner).name
tflog.Debug(ctx, "Resetting repository OIDC subject claim customization template to default", map[string]any{"repository": repoName})

customOIDCSubjectClaimTemplate := github.OIDCSubjectClaimCustomTemplate{
// Delete resets the repository to the default claims.
// https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#using-the-default-subject-claims
template := github.OIDCSubjectClaimCustomTemplate{
UseDefault: new(true),
}

ctx := context.Background()
_, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository, customOIDCSubjectClaimTemplate)
if err != nil {
return err
if _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repoName, template); err != nil {
if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response.StatusCode == http.StatusNotFound {
tflog.Info(ctx, "Repository no longer exists, nothing to reset", map[string]any{"repository": repoName})
return nil
}
return diag.FromErr(err)
}

return nil
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

repoName := d.Id()

repo, _, err := client.Repositories.Get(ctx, owner, repoName)
if err != nil {
return nil, err
}

if err := d.Set("repository", repo.GetName()); err != nil {
return nil, err
}
if err := d.Set("repository_id", int(repo.GetID())); err != nil {
return nil, err
}
d.SetId(repo.GetName())

return []*schema.ResourceData{d}, nil
}

func expandRepositoryOIDCSubjectClaimCustomTemplate(d *schema.ResourceData) (github.OIDCSubjectClaimCustomTemplate, error) {
useDefault := d.Get("use_default").(bool)
includeClaimKeys, hasClaimKeys := d.GetOk("include_claim_keys")

if useDefault && hasClaimKeys {
return github.OIDCSubjectClaimCustomTemplate{}, errors.New("include_claim_keys cannot be set when use_default is true")
}

template := github.OIDCSubjectClaimCustomTemplate{
UseDefault: new(useDefault),
}

if hasClaimKeys {
template.IncludeClaimKeys = expandStringList(includeClaimKeys.([]any))
}

return template, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package github

import (
"context"

"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateResourceV0() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
Description: "The name of the repository.",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringLenBetween(1, 100)),
},
"use_default": {
Type: schema.TypeBool,
Required: true,
Description: "Whether to use the default template or not. If 'true', 'include_claim_keys' must not be set.",
},
"include_claim_keys": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
Description: "A list of OpenID Connect claims.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateInstanceStateUpgradeV0(ctx context.Context, rawState map[string]any, m any) (map[string]any, error) {
tflog.Debug(ctx, "Migrating repository OIDC subject claim customization template from v0 to v1.", rawState)

meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name

migratedState, err := migrateRepositoryWithID(ctx, client, owner, rawState)
if err != nil {
return nil, err
}

tflog.Debug(ctx, "Migrated repository OIDC subject claim customization template to v1.", migratedState)
return migratedState, nil
}
Loading
Loading