Skip to content

Commit

Permalink
feat(google_service_account): Add validation for account_id
Browse files Browse the repository at this point in the history
  • Loading branch information
jai authored Feb 4, 2024
1 parent d74fbfe commit c8f9403
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 44 deletions.
2 changes: 0 additions & 2 deletions .github/semantic.yml

This file was deleted.

27 changes: 0 additions & 27 deletions .github/workflows/shiftleft-terraform.yaml

This file was deleted.

5 changes: 2 additions & 3 deletions .github/workflows/terratest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: true
- name: Set up Go (1.18)
Expand All @@ -43,8 +43,7 @@ jobs:
cd test
go test -v -timeout 5m
- name: Release
if: github.event_name == 'push'
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v4
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
Expand Down
1 change: 0 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

14 changes: 7 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
# See https://pre-commit.com/hooks.html for more hooks

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0 # Get the latest from: https://github.com/pre-commit/pre-commit-hooks/releases
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.75.0 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
hooks:
Expand All @@ -23,3 +16,10 @@ repos:
exclude: (test/|examples/)
- id: terraform_checkov
exclude: (test/|examples/)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0 # Get the latest from: https://github.com/pre-commit/pre-commit-hooks/releases
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ Account. Inputs and outputs below are for the wrapper - for the actual module to
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 5.14 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_random"></a> [random](#provider\_random) | 3.3.2 |
| <a name="provider_random"></a> [random](#provider\_random) | 3.6.0 |

## Modules

Expand Down
1 change: 1 addition & 0 deletions examples/service_account_with_keys/inputs.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
variable "account_id" {
type = string
default = "something-"
description = "The ID of the GCP service account."
}

Expand Down
5 changes: 3 additions & 2 deletions modules/google_service_account/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ Module for managing Google Service Accounts plus:

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.12 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | >= 4.12 |
| <a name="provider_google"></a> [google](#provider\_google) | 4.20.0 |

## Modules

Expand All @@ -40,7 +41,7 @@ No modules.

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | The account id that is used to generate the service account email address and a stable unique id. It is unique within a project, must be 6-30 characters long, and match the regular expression [a-z]([-a-z0-9]*[a-z0-9]) to comply with RFC1035. Changing this forces a new service account to be created. | `string` | n/a | yes |
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | The account id that is used to generate the service account email address and a stable unique id. It is unique within a project, must be 6-30 characters long, and match the regular expression ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$ to comply with RFC1035. Changing this forces a new service account to be created. | `string` | n/a | yes |
| <a name="input_cross_project_iam_memberships"></a> [cross\_project\_iam\_memberships](#input\_cross\_project\_iam\_memberships) | A map of project IDs with a list of IAM roles with optional conditions to add memberships for. | <pre>map(list(object({<br> role = string<br> conditions = optional(list(object({<br> description = string<br> expression = string<br> title = string<br> })))<br> })))</pre> | `{}` | no |
| <a name="input_description"></a> [description](#input\_description) | A text description of the service account. Must be less than or equal to 256 UTF-8 bytes. | `string` | n/a | yes |
| <a name="input_display_name"></a> [display\_name](#input\_display\_name) | The display name for the service account. Can be updated without creating a new resource. | `string` | n/a | yes |
Expand Down
6 changes: 5 additions & 1 deletion modules/google_service_account/inputs.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
variable "account_id" {
description = "The account id that is used to generate the service account email address and a stable unique id. It is unique within a project, must be 6-30 characters long, and match the regular expression [a-z]([-a-z0-9]*[a-z0-9]) to comply with RFC1035. Changing this forces a new service account to be created."
description = "The account id that is used to generate the service account email address and a stable unique id. It is unique within a project, must be 6-30 characters long, and match the regular expression ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$ to comply with RFC1035. Changing this forces a new service account to be created."
type = string
validation {
condition = length(var.account_id) >= 6 && length(var.account_id) <= 30 && regex("^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$", var.account_id) == var.account_id
error_message = "The account_id must be 6-30 characters long, and match the regular expression ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$ to comply with RFC1035."
}
}

variable "cross_project_iam_memberships" {
Expand Down
14 changes: 14 additions & 0 deletions terraform-gcp-iam-wrapper.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
terraform {
required_version = "~> 1.0"

required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.14"
}

random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}

provider "random" {}

provider "google" {
region = var.google_region
project = var.google_project
Expand Down
69 changes: 69 additions & 0 deletions test/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package test

import (
"context"
"github.com/stretchr/testify/assert"
"strings"
"testing"

Expand Down Expand Up @@ -81,3 +82,71 @@ func TestServiceAccountWithMembershipsExample(t *testing.T) {

// Write test covering different membership types with and without conditions.
}

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

runId := strings.ToLower(random.UniqueId())
exampleModuleName := "service_account_with_keys"
terraformOptions := &terraform.Options{}

nameEndingInHyphen := runId + "-"
nameStartingWithHyphen := "-" + runId

shortName := "short" + runId
if len(shortName) > 5 {
shortName = shortName[len(shortName)-5:]
}

longName := "thishasmorethanthirtycharacters" + runId
if len(longName) > 31 {
longName = longName[len(longName)-31:]
}

invalidAccountIds := []string{nameEndingInHyphen, nameStartingWithHyphen, shortName, longName}

for _, invalidAccountId := range invalidAccountIds {

test_structure.RunTestStage(t, exampleModuleName+"_create_test_copy", func() {
tempTestDir := test_structure.CopyTerraformFolderToTemp(t, "..", "examples/"+exampleModuleName)

copyErr := copySupportingFiles(
[]string{
"providers.tf",
}, tempTestDir)

if copyErr != nil {
t.Fatal("Failed to copy supporting files: " + copyErr.Error())
}

dummyGoogleCredentials := `{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\n(private-key)\n-----END PRIVATE KEY-----\n",
"client_email": "service-account-email",
"client_id": "client-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
}`

terraformOptions = &terraform.Options{
TerraformDir: tempTestDir,
Vars: map[string]interface{}{
"account_id": invalidAccountId,
"project_id": "dummy",
"google_credentials": dummyGoogleCredentials,
"google_region": "asia-southeast2",
},
}
})

test_structure.RunTestStage(t, exampleModuleName+"_validate", func() {
//err := terraform.InitAndPlanE(t, terraformOptions)
_, err := terraform.InitAndPlanE(t, terraformOptions)
assert.NotNil(t, err, "All given account IDs should be invalid")
})
}
}

0 comments on commit c8f9403

Please sign in to comment.