Skip to content

Commit 5440c57

Browse files
author
Matt Cadorette
authored
feat(GROW-2876): support terraform output blocks in lwgenerate (#1609)
* feat(GROW-2876): support terraform output blocks in lwgenerate * Add generic functionality for hcl output blocks * Add hooks to AWS to add outputs Outputs are arbitrary, meaning the author needs to understand the resultant traversal (and that it is valid) otherwise the resultant code will be unusable. * chore: fix comment
1 parent 8271a9f commit 5440c57

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

lwgenerate/aws/aws.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ type GenerateAwsTfConfigurationArgs struct {
207207
// Config resource prefix
208208
ConfigOrgCfResourcePrefix string
209209

210+
// Custom outputs
211+
CustomOutputs []lwgenerate.HclOutput
212+
210213
// Supply an AWS region for where to find the cloudtrail resources
211214
// TODO @ipcrm future: support split regions for resources (s3 one place, sns another, etc)
212215
AwsRegion string
@@ -538,6 +541,13 @@ func WithConfigOrgUnits(orgUnits []string) AwsTerraformModifier {
538541
}
539542
}
540543

544+
// WithConfigOutputs Set Custom Terraform Outputs
545+
func WithCustomOutputs(outputs []lwgenerate.HclOutput) AwsTerraformModifier {
546+
return func(c *GenerateAwsTfConfigurationArgs) {
547+
c.CustomOutputs = outputs
548+
}
549+
}
550+
541551
// WithConfigOrgCfResourcePrefix Set Config org resource prefix
542552
func WithConfigOrgCfResourcePrefix(resourcePrefix string) AwsTerraformModifier {
543553
return func(c *GenerateAwsTfConfigurationArgs) {
@@ -750,6 +760,15 @@ func (args *GenerateAwsTfConfigurationArgs) Generate() (string, error) {
750760
return "", errors.Wrap(err, "failed to generate aws agentless global module")
751761
}
752762

763+
outputBlocks := []*hclwrite.Block{}
764+
for _, output := range args.CustomOutputs {
765+
outputBlock, err := output.ToBlock()
766+
if err != nil {
767+
return "", errors.Wrap(err, "failed to add custom output")
768+
}
769+
outputBlocks = append(outputBlocks, outputBlock)
770+
}
771+
753772
// Render
754773
hclBlocks := lwgenerate.CreateHclStringOutput(
755774
lwgenerate.CombineHclBlocks(
@@ -758,7 +777,8 @@ func (args *GenerateAwsTfConfigurationArgs) Generate() (string, error) {
758777
laceworkProvider,
759778
configModule,
760779
cloudTrailModule,
761-
agentlessModule),
780+
agentlessModule,
781+
outputBlocks),
762782
)
763783
return hclBlocks, nil
764784
}

lwgenerate/aws/aws_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/lacework/go-sdk/lwgenerate"
78
"github.com/stretchr/testify/assert"
89
)
910

@@ -77,6 +78,17 @@ func TestGenerationConfig(t *testing.T) {
7778
assert.Equal(t, reqProviderAndRegion(moduleImportConfig), hcl)
7879
}
7980

81+
func TestGenerationConfigWithOutputs(t *testing.T) {
82+
hcl, err := NewTerraform(
83+
false, false, true, false, WithAwsRegion("us-east-2"),
84+
WithCustomOutputs([]lwgenerate.HclOutput{
85+
*lwgenerate.NewOutput("test", []string{"module", "aws_config", "lacework_integration_guid"}, "test description"),
86+
})).Generate()
87+
assert.Nil(t, err)
88+
assert.NotNil(t, hcl)
89+
assert.Equal(t, reqProviderAndRegion(moduleImportConfig)+"\n"+customOutput, hcl)
90+
}
91+
8092
func TestGenerationConfigWithMultipleAccounts(t *testing.T) {
8193
hcl, err := NewTerraform(false, false, true, false,
8294
WithAwsProfile("main"),
@@ -135,8 +147,7 @@ func TestGenerationCloudtrailConsolidated(t *testing.T) {
135147
ConsolidatedCloudtrail: true,
136148
})
137149
assert.Nil(t, err)
138-
assert.Equal(t,
139-
"consolidated_trail=true\n",
150+
assert.Equal(t, "consolidated_trail=true\n",
140151
string(data.Body().GetAttribute("consolidated_trail").BuildTokens(nil).Bytes()))
141152
}
142153

@@ -674,6 +685,12 @@ var moduleImportConfig = `module "aws_config" {
674685
}
675686
`
676687

688+
var customOutput = `output "test" {
689+
description = "test description"
690+
value = module.aws_config.lacework_integration_guid
691+
}
692+
`
693+
677694
var moduleImportConfigWithMultipleAccounts = `terraform {
678695
required_providers {
679696
lacework = {

lwgenerate/hcl.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,48 @@ type ForEach struct {
108108
value map[string]string
109109
}
110110

111+
type HclOutput struct {
112+
// required, name of the resultant output
113+
name string
114+
115+
// required, converted into a traversal
116+
// e.g. []string{"a", "b", "c"} as input results in traversal having value a.b.c
117+
value []string
118+
119+
// optional
120+
description string
121+
}
122+
123+
func (m *HclOutput) ToBlock() (*hclwrite.Block, error) {
124+
if m.value == nil {
125+
return nil, errors.New("value must be supplied")
126+
}
127+
128+
attributes := map[string]interface{}{
129+
"value": CreateSimpleTraversal(m.value),
130+
}
131+
132+
if m.description != "" {
133+
attributes["description"] = m.description
134+
}
135+
136+
block, err := HclCreateGenericBlock(
137+
"output",
138+
[]string{m.name},
139+
attributes,
140+
)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
return block, nil
146+
}
147+
148+
// NewOutput Create a provider statement in the HCL output
149+
func NewOutput(name string, value []string, description string) *HclOutput {
150+
return &HclOutput{name: name, description: description, value: value}
151+
}
152+
111153
type HclModule struct {
112154
// Required, module name
113155
name string

lwgenerate/hcl_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,23 @@ func TestModuleBlockWithComplexAttributes(t *testing.T) {
175175
assert.NoError(t, err)
176176
}
177177

178+
func TestOutputBlockCreation(t *testing.T) {
179+
t.Run("should generate correct block for simple output with no description", func(t *testing.T) {
180+
o := lwgenerate.NewOutput("test", []string{"test", "one", "two"}, "")
181+
b, err := o.ToBlock()
182+
assert.NoError(t, err)
183+
str := lwgenerate.CreateHclStringOutput([]*hclwrite.Block{b})
184+
assert.Equal(t, "output \"test\" {\n value = test.one.two\n}\n", str)
185+
})
186+
t.Run("should generate correct block for simple output with description", func(t *testing.T) {
187+
o := lwgenerate.NewOutput("test", []string{"test", "one", "two"}, "test description")
188+
b, err := o.ToBlock()
189+
assert.NoError(t, err)
190+
str := lwgenerate.CreateHclStringOutput([]*hclwrite.Block{b})
191+
assert.Equal(t, "output \"test\" {\n description = \"test description\"\n value = test.one.two\n}\n", str)
192+
})
193+
}
194+
178195
var testRequiredProvider = `terraform {
179196
required_providers {
180197
bar = {

0 commit comments

Comments
 (0)