Skip to content

Commit

Permalink
feat(GROW-2929): update lwgenerate to support GCP generation
Browse files Browse the repository at this point in the history
Signed-off-by: Lei Jin <lei.jin@lacework.net>
  • Loading branch information
leijin-lw committed May 30, 2024
1 parent 67c7bbc commit e5f093c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 5 deletions.
67 changes: 63 additions & 4 deletions lwgenerate/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ type GenerateGcpTfConfigurationArgs struct {
WaitTime string

Projects []string

// Add custom blocks to the root `terraform{}` block. Can be used for advanced configuration. Things like backend, etc
ExtraBlocksRootTerraform []*hclwrite.Block

// ExtraProviderArguments allows adding more arguments to the provider block as needed (custom use cases)
ExtraProviderArguments map[string]interface{}

// ExtraBlocks allows adding more hclwrite.Block to the root terraform document (advanced use cases)
ExtraBlocks []*hclwrite.Block

// Custom outputs
CustomOutputs []lwgenerate.HclOutput
}

// Ensure all combinations of inputs are valid for supported spec
Expand Down Expand Up @@ -223,6 +235,36 @@ func WithGcpServiceAccountCredentials(path string) GcpTerraformModifier {
}
}

// WithConfigOutputs Set Custom Terraform Outputs
func WithCustomOutputs(outputs []lwgenerate.HclOutput) GcpTerraformModifier {
return func(c *GenerateGcpTfConfigurationArgs) {
c.CustomOutputs = outputs
}
}

// WithExtraRootBlocks allows adding generic hcl blocks to the root `terraform{}` block
// this enables custom use cases
func WithExtraRootBlocks(blocks []*hclwrite.Block) GcpTerraformModifier {
return func(c *GenerateGcpTfConfigurationArgs) {
c.ExtraBlocksRootTerraform = blocks
}
}

// WithExtraProviderArguments enables adding additional arguments into the `gcp` provider block
// this enables custom use cases
func WithExtraProviderArguments(arguments map[string]interface{}) GcpTerraformModifier {
return func(c *GenerateGcpTfConfigurationArgs) {
c.ExtraProviderArguments = arguments
}
}

// WithExtraBlocks enables adding additional arbitrary blocks to the root hcl document
func WithExtraBlocks(blocks []*hclwrite.Block) GcpTerraformModifier {
return func(c *GenerateGcpTfConfigurationArgs) {
c.ExtraBlocks = blocks
}
}

// WithLaceworkProfile Set the Lacework Profile to utilize when integrating
func WithLaceworkProfile(name string) GcpTerraformModifier {
return func(c *GenerateGcpTfConfigurationArgs) {
Expand Down Expand Up @@ -417,12 +459,12 @@ func (args *GenerateGcpTfConfigurationArgs) Generate() (string, error) {
}

// Create blocks
requiredProviders, err := createRequiredProviders(false)
requiredProviders, err := createRequiredProviders(false, args.ExtraBlocksRootTerraform)
if err != nil {
return "", errors.Wrap(err, "failed to generate required providers")
}

gcpProvider, err := createGcpProvider(args.ServiceAccountCredentials, args.GcpProjectId, args.Regions, "")
gcpProvider, err := createGcpProvider(args.ExtraProviderArguments, args.ServiceAccountCredentials, args.GcpProjectId, args.Regions, "")
if err != nil {
return "", errors.Wrap(err, "failed to generate gcp provider")
}
Expand All @@ -447,6 +489,15 @@ func (args *GenerateGcpTfConfigurationArgs) Generate() (string, error) {
return "", errors.Wrap(err, "failed to generate gcp audit log module")
}

outputBlocks := []*hclwrite.Block{}
for _, output := range args.CustomOutputs {
outputBlock, err := output.ToBlock()
if err != nil {
return "", errors.Wrap(err, "failed to add custom output")
}
outputBlocks = append(outputBlocks, outputBlock)
}

// Render
hclBlocks := lwgenerate.CreateHclStringOutput(
lwgenerate.CombineHclBlocks(
Expand All @@ -456,16 +507,19 @@ func (args *GenerateGcpTfConfigurationArgs) Generate() (string, error) {
agentlessModule,
configurationModule,
auditLogModule,
outputBlocks,
args.ExtraBlocks,
),
)
return hclBlocks, nil
}

func createRequiredProviders(useExistingRequiredProviders bool) (*hclwrite.Block, error) {
func createRequiredProviders(useExistingRequiredProviders bool, extraBlocks []*hclwrite.Block) (*hclwrite.Block, error) {
if useExistingRequiredProviders {
return nil, nil
}
return lwgenerate.CreateRequiredProviders(
return lwgenerate.CreateRequiredProvidersWithCustomBlocks(
extraBlocks,
lwgenerate.NewRequiredProvider(
"lacework",
lwgenerate.HclRequiredProviderWithSource(lwgenerate.LaceworkProviderSource),
Expand All @@ -485,6 +539,7 @@ func createLaceworkProvider(laceworkProfile string) (*hclwrite.Block, error) {
}

func createGcpProvider(
extraProvidedArguments map[string]interface{},
serviceAccountCredentials string,
projectId string,
regionsArg []string,
Expand All @@ -500,6 +555,10 @@ func createGcpProvider(
for _, region := range regions {
attrs := map[string]interface{}{}

// set custom args before the required ones below to ensure expected behavior (i.e., no overrides)
for k, v := range extraProvidedArguments {
attrs[k] = v
}
if serviceAccountCredentials != "" {
attrs["credentials"] = serviceAccountCredentials
}
Expand Down
91 changes: 91 additions & 0 deletions lwgenerate/gcp/gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package gcp_test

import (
"fmt"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/lacework/go-sdk/lwgenerate"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -795,6 +797,70 @@ var RequiredProviders = `terraform {
}
`

var requiredProvidersWithCustomBlock = `terraform {
required_providers {
lacework = {
source = "lacework/lacework"
version = "~> 1.0"
}
}
backend "s3" {
}
}
`

func TestGenerationConfigWithExtraBlocks(t *testing.T) {
extraBlock, err := lwgenerate.HclCreateGenericBlock("variable", []string{"var_name"}, nil)
assert.NoError(t, err)

hcl, err := gcp.NewTerraform(false, false, true, false,
gcp.WithGcpServiceAccountCredentials("/path/to/credentials"),
gcp.WithProjectId(projectName),
gcp.WithExtraBlocks([]*hclwrite.Block{extraBlock}),
).Generate()
assert.Nil(t, err)
assert.NotNil(t, hcl)
assert.Equal(t, RequiredProviders+"\n"+gcpProvider+"\n"+moduleImportProjectLevelAuditLogWithoutConfiguration+"\n"+testVariable, hcl)
}

func TestGenerationConfigWithCustomBackendBlock(t *testing.T) {
customBlock, err := lwgenerate.HclCreateGenericBlock("backend", []string{"s3"}, nil)
assert.NoError(t, err)
hcl, err := gcp.NewTerraform(false, false, true, false,
gcp.WithGcpServiceAccountCredentials("/path/to/credentials"),
gcp.WithProjectId(projectName),
gcp.WithExtraRootBlocks([]*hclwrite.Block{customBlock}),
).Generate()
assert.Nil(t, err)
assert.NotNil(t, hcl)
assert.Equal(t, requiredProvidersWithCustomBlock+"\n"+gcpProvider+"\n"+moduleImportProjectLevelAuditLogWithoutConfiguration, hcl)
}

func TestGenerationConfigWithCustomProviderAttributes(t *testing.T) {
hcl, err := gcp.NewTerraform(false, false, true, false,
gcp.WithGcpServiceAccountCredentials("/path/to/credentials"),
gcp.WithProjectId(projectName),
gcp.WithRegions([]string{"us-east1"}),
gcp.WithExtraProviderArguments(map[string]interface{}{"foo": "bar"}),
).Generate()
assert.Nil(t, err)
assert.NotNil(t, hcl)
assert.Equal(t, RequiredProviders+"\n"+gcpProviderWithExtraArguments+"\n"+moduleImportProjectLevelAuditLogWithoutConfiguration, hcl)
}

func TestGenerationConfigWithOutputs(t *testing.T) {
hcl, err := gcp.NewTerraform(
false, false, true, false,
gcp.WithGcpServiceAccountCredentials("/path/to/credentials"),
gcp.WithProjectId(projectName),
gcp.WithCustomOutputs([]lwgenerate.HclOutput{
*lwgenerate.NewOutput("test", []string{"module", "gcp_config", "lacework_integration_guid"}, "test description"),
})).Generate()
assert.Nil(t, err)
assert.NotNil(t, hcl)
assert.Equal(t, RequiredProviders+"\n"+gcpProvider+"\n"+moduleImportProjectLevelAuditLogWithoutConfiguration+"\n"+customOutput, hcl)
}

func ProviderWithCredentials(projectName string) string {
return fmt.Sprintf(`provider "google" {
credentials = "/path/to/credentials"
Expand Down Expand Up @@ -823,6 +889,31 @@ var gcpProviderWithoutCredentialsAndProject = `provider "google" {
}
`

var gcpProvider = `provider "google" {
credentials = "/path/to/credentials"
project = "project1"
}
`

var gcpProviderWithExtraArguments = `provider "google" {
alias = "us-east1"
credentials = "/path/to/credentials"
foo = "bar"
project = "project1"
region = "us-east1"
}
`

var testVariable = `variable "var_name" {
}
`

var customOutput = `output "test" {
description = "test description"
value = module.gcp_config.lacework_integration_guid
}
`

var laceworkProvider = `provider "lacework" {
profile = "test-profile"
}
Expand Down
5 changes: 4 additions & 1 deletion lwgenerate/gcp/gke.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type GenerateGkeTfConfigurationArgs struct {
PubSubTopicLabels map[string]string
ServiceAccountCredentials string
WaitTime string
// Add custom blocks to the root `terraform{}` block. Can be used for advanced configuration. Things like backend, etc
ExtraBlocksRootTerraform []*hclwrite.Block
}

type Modifier func(c *GenerateGkeTfConfigurationArgs)
Expand All @@ -33,12 +35,13 @@ func (args *GenerateGkeTfConfigurationArgs) Generate() (string, error) {
return "", errors.Wrap(err, "invalid inputs")
}

requiredProviders, err := createRequiredProviders(args.UseExistingRequiredProviders)
requiredProviders, err := createRequiredProviders(args.UseExistingRequiredProviders, args.ExtraBlocksRootTerraform)
if err != nil {
return "", errors.Wrap(err, "failed to generate required providers")
}

gcpProvider, err := createGcpProvider(
map[string]interface{}{},
args.ServiceAccountCredentials,
args.ProjectId,
[]string{},
Expand Down

0 comments on commit e5f093c

Please sign in to comment.