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 @@ -3,28 +3,39 @@ package github
import (
"context"
"errors"
"net/http"

"github.com/google/go-github/v85/github"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"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,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
SchemaVersion: 1,
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateV0().CoreConfigSchema().ImpliedType(),
Upgrade: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateStateUpgradeV0,
Version: 0,
},
},

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: "ID of the repository.",
},
"use_default": {
Type: schema.TypeBool,
Required: true,
Expand All @@ -40,10 +51,22 @@ func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplate() *sch
},
},
},

CustomizeDiff: customdiff.All(
diffRepository,
),

CreateContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateOrUpdate,
ReadContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead,
UpdateContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateOrUpdate,
DeleteContext: resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
}
}

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

repository := d.Get("repository").(string)
Expand All @@ -53,77 +76,98 @@ func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateCreateO
includeClaimKeys, hasClaimKeys := d.GetOk("include_claim_keys")

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

customOIDCSubjectClaimTemplate := &github.OIDCSubjectClaimCustomTemplate{
UseDefault: &useDefault,
}

if includeClaimKeys != nil {

includeClaimKeysVal := includeClaimKeys.([]any)

claimsStr := make([]string, len(includeClaimKeysVal))

for i, v := range includeClaimKeysVal {
claimsStr[i] = v.(string)
}

customOIDCSubjectClaimTemplate.IncludeClaimKeys = claimsStr
}

ctx := context.Background()
_, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository, customOIDCSubjectClaimTemplate)
if err != nil {
return err
return diag.FromErr(err)
}

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

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

return resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(ctx, d, meta)
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
ctx = tflog.SetField(ctx, "id", d.Id())

repository := d.Id()
client := meta.(*Owner).v3client
owner := meta.(*Owner).name

ctx := context.Background()
template, _, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository)
repoName := d.Get("repository").(string)

repo, _, err := client.Repositories.Get(ctx, owner, repoName)
if err != nil {
return deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "actions repository oidc subject claim customization template (%s, %s)", owner, repository)
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) && ghErr.Response.StatusCode == http.StatusNotFound {
tflog.Info(ctx, "Repository not found, removing from state.", map[string]any{"repository": repoName})
d.SetId("")
return nil
}
return diag.FromErr(err)
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I could use the same wrapper deleteResourceOn404AndSwallow304OtherwiseReturn I guess


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

if err = d.Set("repository", repository); err != nil {
return err
if err = d.Set("repository", repo.GetName()); err != nil {
return diag.FromErr(err)
}
if err = d.Set("repository_id", int(repo.GetID())); err != nil {
return diag.FromErr(err)
}
if err = d.Set("use_default", template.UseDefault); err != nil {
return err
return diag.FromErr(err)
}
if err = d.Set("include_claim_keys", template.IncludeClaimKeys); err != nil {
return err
return diag.FromErr(err)
}

return nil
}

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete(d *schema.ResourceData, meta any) error {
func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
// 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

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

useDefault := true
customOIDCSubjectClaimTemplate := &github.OIDCSubjectClaimCustomTemplate{
UseDefault: new(true),
UseDefault: &useDefault,
}

ctx := context.Background()
_, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, owner, repository, customOIDCSubjectClaimTemplate)
if err != nil {
return err
return diag.FromErr(err)
}

return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package github

import (
"context"
"fmt"
"log"

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

func resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateV0() *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 resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplateStateUpgradeV0(ctx context.Context, rawState map[string]any, m any) (map[string]any, error) {
meta := m.(*Owner)
client := meta.v3client
owner := meta.name

log.Printf("[DEBUG] OIDC Subject Claim Customization Template state before migration: %#v", rawState)

repoName, ok := rawState["repository"].(string)
if !ok {
return nil, fmt.Errorf("repository not found or is not a string")
}

repo, _, err := client.Repositories.Get(ctx, owner, repoName)
if err != nil {
return nil, fmt.Errorf("failed to retrieve repository %s: %w", repoName, err)
}

rawState["repository_id"] = int(repo.GetID())

log.Printf("[DEBUG] OIDC Subject Claim Customization Template state after migration: %#v", rawState)

return rawState, nil
}