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

provider "github" {
token = var.github_token
owner = var.enterprise_slug
}

variable "github_token" {
description = "GitHub classic personal access token (PAT) for an enterprise admin"
type = string
sensitive = true
}

variable "enterprise_slug" {
description = "The GitHub Enterprise slug"
type = string
}

variable "cost_center_name" {
description = "Name for the cost center"
type = string
}

variable "users" {
description = "Usernames to assign to the cost center"
type = list(string)
default = []
}

variable "organizations" {
description = "Organization logins to assign to the cost center"
type = list(string)
default = []
}

variable "repositories" {
description = "Repositories (full name, e.g. org/repo) to assign to the cost center"
type = list(string)
default = []
}

resource "github_enterprise_cost_center" "example" {
enterprise_slug = var.enterprise_slug
name = var.cost_center_name

# Authoritative assignments: Terraform will add/remove to match these lists.
users = var.users
organizations = var.organizations
repositories = var.repositories
}

data "github_enterprise_cost_center" "by_id" {
enterprise_slug = var.enterprise_slug
cost_center_id = github_enterprise_cost_center.example.id
}

data "github_enterprise_cost_centers" "active" {
enterprise_slug = var.enterprise_slug
state = "active"

depends_on = [github_enterprise_cost_center.example]
}

output "cost_center" {
description = "Created cost center"
value = {
id = github_enterprise_cost_center.example.id
name = github_enterprise_cost_center.example.name
state = github_enterprise_cost_center.example.state
azure_subscription = github_enterprise_cost_center.example.azure_subscription
}
}

output "cost_center_resources" {
description = "Effective assignments (read from API)"
value = {
users = sort(tolist(github_enterprise_cost_center.example.users))
organizations = sort(tolist(github_enterprise_cost_center.example.organizations))
repositories = sort(tolist(github_enterprise_cost_center.example.repositories))
}
}

output "cost_center_from_data_source" {
description = "Cost center fetched by data source"
value = {
id = data.github_enterprise_cost_center.by_id.cost_center_id
name = data.github_enterprise_cost_center.by_id.name
state = data.github_enterprise_cost_center.by_id.state
users = sort(tolist(data.github_enterprise_cost_center.by_id.users))
organizations = sort(tolist(data.github_enterprise_cost_center.by_id.organizations))
repositories = sort(tolist(data.github_enterprise_cost_center.by_id.repositories))
}
}
94 changes: 94 additions & 0 deletions github/data_source_github_enterprise_cost_center.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package github

import (
"context"
"sort"
"strings"

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

func dataSourceGithubEnterpriseCostCenter() *schema.Resource {
return &schema.Resource{
Description: "Use this data source to retrieve information about a specific enterprise cost center.",
ReadContext: dataSourceGithubEnterpriseCostCenterRead,

Schema: map[string]*schema.Schema{
"enterprise_slug": {
Type: schema.TypeString,
Required: true,
Description: "The slug of the enterprise.",
},
"cost_center_id": {
Type: schema.TypeString,
Required: true,
Description: "The ID of the cost center.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "The name of the cost center.",
},
"state": {
Type: schema.TypeString,
Computed: true,
Description: "The state of the cost center.",
},
"azure_subscription": {
Type: schema.TypeString,
Computed: true,
Description: "The Azure subscription associated with the cost center.",
},
"users": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "The usernames assigned to this cost center.",
},
"organizations": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "The organization logins assigned to this cost center.",
},
"repositories": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "The repositories (full name) assigned to this cost center.",
},
},
}
}

func dataSourceGithubEnterpriseCostCenterRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client
enterpriseSlug := d.Get("enterprise_slug").(string)
costCenterID := d.Get("cost_center_id").(string)

cc, _, err := client.Enterprise.GetCostCenter(ctx, enterpriseSlug, costCenterID)
if err != nil {
return diag.FromErr(err)
}

d.SetId(costCenterID)
_ = d.Set("name", cc.Name)

state := strings.ToLower(cc.GetState())
if state == "" {
state = "active"
}
_ = d.Set("state", state)
_ = d.Set("azure_subscription", cc.GetAzureSubscription())

users, organizations, repositories := costCenterSplitResources(cc.Resources)
sort.Strings(users)
sort.Strings(organizations)
sort.Strings(repositories)
_ = d.Set("users", flattenStringList(users))
_ = d.Set("organizations", flattenStringList(organizations))
_ = d.Set("repositories", flattenStringList(repositories))

return nil
}
53 changes: 53 additions & 0 deletions github/data_source_github_enterprise_cost_center_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package github

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccGithubEnterpriseCostCenterDataSource(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

if testAccConf.username == "" {
t.Skip("Skipping because `GITHUB_USERNAME` is not set")
}

// Use username for testing
user := testAccConf.username

config := fmt.Sprintf(`
data "github_enterprise" "enterprise" {
slug = "%s"
}

resource "github_enterprise_cost_center" "test" {
enterprise_slug = data.github_enterprise.enterprise.slug
name = "tf-acc-test-%s"

users = [%q]
}

data "github_enterprise_cost_center" "test" {
enterprise_slug = data.github_enterprise.enterprise.slug
cost_center_id = github_enterprise_cost_center.test.id
}
`, testAccConf.enterpriseSlug, randomID, user)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessEnterprise(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair("data.github_enterprise_cost_center.test", "cost_center_id", "github_enterprise_cost_center.test", "id"),
resource.TestCheckResourceAttrPair("data.github_enterprise_cost_center.test", "name", "github_enterprise_cost_center.test", "name"),
resource.TestCheckResourceAttr("data.github_enterprise_cost_center.test", "state", "active"),
resource.TestCheckResourceAttr("data.github_enterprise_cost_center.test", "users.#", "1"),
resource.TestCheckTypeSetElemAttr("data.github_enterprise_cost_center.test", "users.*", user),
),
}},
})
}
96 changes: 96 additions & 0 deletions github/data_source_github_enterprise_cost_centers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package github

import (
"context"

"github.com/google/go-github/v81/github"
"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 dataSourceGithubEnterpriseCostCenters() *schema.Resource {
return &schema.Resource{
Description: "Use this data source to retrieve a list of enterprise cost centers.",
ReadContext: dataSourceGithubEnterpriseCostCentersRead,

Schema: map[string]*schema.Schema{
"enterprise_slug": {
Type: schema.TypeString,
Required: true,
Description: "The slug of the enterprise.",
},
"state": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"active", "deleted"}, false), "state"),
Description: "Filter cost centers by state.",
},
"cost_centers": {
Type: schema.TypeSet,
Computed: true,
Description: "The list of cost centers.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
Description: "The cost center ID.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "The name of the cost center.",
},
"state": {
Type: schema.TypeString,
Computed: true,
Description: "The state of the cost center.",
},
"azure_subscription": {
Type: schema.TypeString,
Computed: true,
Description: "The Azure subscription associated with the cost center.",
},
},
},
},
},
}
}

func dataSourceGithubEnterpriseCostCentersRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client
enterpriseSlug := d.Get("enterprise_slug").(string)
var state *string
if v, ok := d.GetOk("state"); ok {
s := v.(string)
state = &s
}

result, _, err := client.Enterprise.ListCostCenters(ctx, enterpriseSlug, &github.ListCostCenterOptions{State: state})
if err != nil {
return diag.FromErr(err)
}

items := make([]any, 0, len(result.CostCenters))
for _, cc := range result.CostCenters {
if cc == nil {
continue
}
items = append(items, map[string]any{
"id": cc.ID,
"name": cc.Name,
"state": cc.GetState(),
"azure_subscription": cc.GetAzureSubscription(),
})
}

stateStr := ""
if state != nil {
stateStr = *state
}
d.SetId(buildTwoPartID(enterpriseSlug, stateStr))
_ = d.Set("cost_centers", items)
return nil
}
Loading