Skip to content

Commit

Permalink
Add google_impersonated_credential datasource
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
chrisst authored and modular-magician committed Apr 1, 2019
1 parent 4d2ade9 commit ef60ecc
Show file tree
Hide file tree
Showing 9 changed files with 1,932 additions and 5 deletions.
9 changes: 9 additions & 0 deletions google/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
dnsBeta "google.golang.org/api/dns/v1beta2"
file "google.golang.org/api/file/v1beta1"
"google.golang.org/api/iam/v1"
iamcredentials "google.golang.org/api/iamcredentials/v1"
cloudlogging "google.golang.org/api/logging/v2"
"google.golang.org/api/pubsub/v1"
runtimeconfig "google.golang.org/api/runtimeconfig/v1beta1"
Expand Down Expand Up @@ -73,6 +74,7 @@ type Config struct {
clientDns *dns.Service
clientDnsBeta *dnsBeta.Service
clientFilestore *file.Service
clientIamCredentials *iamcredentials.Service
clientKms *cloudkms.Service
clientLogging *cloudlogging.Service
clientPubsub *pubsub.Service
Expand Down Expand Up @@ -240,6 +242,13 @@ func (c *Config) LoadAndValidate() error {
}
c.clientIAM.UserAgent = userAgent

log.Printf("[INFO] Instantiating Google Cloud IAMCredentials Client...")
c.clientIamCredentials, err = iamcredentials.New(client)
if err != nil {
return err
}
c.clientIamCredentials.UserAgent = userAgent

log.Printf("[INFO] Instantiating Google Cloud Service Management Client...")
c.clientServiceMan, err = servicemanagement.New(client)
if err != nil {
Expand Down
79 changes: 79 additions & 0 deletions google/data_source_google_service_account_access_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package google

import (
"fmt"
"log"

"strings"
"time"

"github.com/hashicorp/terraform/helper/schema"
iamcredentials "google.golang.org/api/iamcredentials/v1"
)

func dataSourceGoogleServiceAccountAccessToken() *schema.Resource {

return &schema.Resource{
Read: dataSourceGoogleServiceAccountAccessTokenRead,
Schema: map[string]*schema.Schema{
"target_service_account": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateRegexp("(" + strings.Join(PossibleServiceAccountNames, "|") + ")"),
},
"access_token": {
Type: schema.TypeString,
Sensitive: true,
Computed: true,
},
"scopes": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
StateFunc: func(v interface{}) string {
return canonicalizeServiceScope(v.(string))
},
},
// ValidateFunc is not yet supported on lists or sets.
},
"delegates": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateRegexp(ServiceAccountLinkRegex),
},
},
"lifetime": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateDuration(), // duration <=3600s; TODO: support validteDuration(min,max)
Default: "3600s",
},
},
}
}

func dataSourceGoogleServiceAccountAccessTokenRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
log.Printf("[INFO] Acquire Service Account AccessToken for %s", d.Get("target_service_account").(string))

service := config.clientIamCredentials

name := fmt.Sprintf("projects/-/serviceAccounts/%s", d.Get("target_service_account").(string))
tokenRequest := &iamcredentials.GenerateAccessTokenRequest{
Lifetime: d.Get("lifetime").(string),
Delegates: convertStringSet(d.Get("delegates").(*schema.Set)),
Scope: canonicalizeServiceScopes(convertStringSet(d.Get("scopes").(*schema.Set))),
}
at, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, tokenRequest).Do()
if err != nil {
return err
}

d.SetId(time.Now().UTC().String())
d.Set("access_token", at.AccessToken)

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

import (
"testing"

"fmt"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func testAccCheckServiceAccountAccessTokenValue(name, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ms := s.RootModule()
rs, ok := ms.Outputs[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

// TODO: validate the token belongs to the service account
if rs.Value == "" {
return fmt.Errorf("%s Cannot be empty", name)
}

return nil
}
}

func TestAccDataSourceGoogleServiceAccountAccessToken_basic(t *testing.T) {
t.Parallel()

resourceName := "data.google_service_account_access_token.default"

targetServiceAccountEmail := getTestServiceAccountFromEnv(t)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleServiceAccountAccessToken_datasource(targetServiceAccountEmail),
Destroy: true,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "target_service_account", targetServiceAccountEmail),
testAccCheckServiceAccountAccessTokenValue("access_token", targetServiceAccountEmail),
),
},
},
})
}

func testAccCheckGoogleServiceAccountAccessToken_datasource(targetServiceAccountID string) string {

return fmt.Sprintf(`
data "google_service_account_access_token" "default" {
target_service_account = "%s"
scopes = ["userinfo-email", "https://www.googleapis.com/auth/cloud-platform"]
lifetime = "30s"
}
output "access_token" {
value = "${data.google_service_account_access_token.default.access_token}"
}
`, targetServiceAccountID)
}
9 changes: 5 additions & 4 deletions google/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import (

const (
// Copied from the official Google Cloud auto-generated client.
ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
ProjectRegexWildCard = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)|-)"
RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"

SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$"

Expand All @@ -35,7 +36,7 @@ var (
// 4 and 28 since the first and last character are excluded.
ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28)

ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegex + "/serviceAccounts/"
ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegexWildCard + "/serviceAccounts/"
PossibleServiceAccountNames = []string{
AppEngineServiceAccountNameRegex,
ComputeServiceAccountNameRegex,
Expand Down
Loading

0 comments on commit ef60ecc

Please sign in to comment.