From abd59f15e43cd37bcad3fd969cbae2f88dd0ccfe Mon Sep 17 00:00:00 2001 From: laszlojau <49835454+laszlojau@users.noreply.github.com> Date: Sun, 26 Sep 2021 13:20:16 +0930 Subject: [PATCH 1/2] Return values from multiple secret fields --- README.md | 15 +++++++------ datasource/tss/data.go | 37 +++++++++++++++------------------ datasource/tss/data.hcl2spec.go | 32 ++++++++++++++-------------- example/data.pkr.hcl | 8 +++++-- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 98cde2a..2a00c3c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ packer { required_plugins { tss = { version = ">= 0.1.0" - source = "github.com/breed808/packer-plugin-tss" + source = "github.com/breed808/tss" } } } @@ -54,7 +54,8 @@ data "tss" "mock-data" { password = "test123" # TSS password server_url = "https://my-thycotic-server.example.com/SecretServer" - secret_id = "500" # ID of TSS secret to retrieve + secret_id = 500 # ID of TSS secret to retrieve + secret_fields = ["username", "password"] # Fields to retrieve from the TSS secret } ``` @@ -67,7 +68,8 @@ data "tss" "mock-data" { server_url = "https://my-thycotic-server.example.com/SecretServer" domain = "example.com" # Domain of user. I.E. testing@example.com - secret_id = "500" # ID of TSS secret to retrieve + secret_id = 500 # ID of TSS secret to retrieve + secret_fields = ["username", "password"] # Fields to retrieve from the TSS secret } ``` @@ -84,7 +86,8 @@ data "tss" "mock-data" { password = "test123" # TSS password server_url = "https://my-thycotic-server.example.com/SecretServer" - secret_id = "500" # ID of TSS secret to retrieve + secret_id = 500 # ID of TSS secret to retrieve + secret_fields = ["username", "password"] # Fields to retrieve from the TSS secret } build { @@ -105,8 +108,8 @@ build { datacenter = "PackerDatacenter" datastore = "datastore1" host = "123.45.678.9" - password = data.mock-data.password - username = data.mock-data.username + password = data.mock-data.fields.username + username = data.mock-data.fields.password } } } diff --git a/datasource/tss/data.go b/datasource/tss/data.go index 565b85e..4757acb 100644 --- a/datasource/tss/data.go +++ b/datasource/tss/data.go @@ -3,7 +3,6 @@ package tss import ( "fmt" - "packer-plugin-tss/common" "github.com/hashicorp/hcl/v2/hcldec" @@ -14,7 +13,8 @@ import ( type Config struct { common.AuthConfig `mapstructure:",squash"` - SecretID int `mapstructure:"secret_id" required:"true"` + SecretID int `mapstructure:"secret_id" required:"true"` + SecretFields []string `mapstructure:"secret_fields" required:"true"` } type Datasource struct { @@ -22,11 +22,10 @@ type Datasource struct { } type DatasourceOutput struct { + // Secret ID in TSS. ID int `mapstructure:"id"` - - // Though TSS stores other fields, retrieve only credential details (username & password) for now. - Username string `mapstructure:"username"` - Password string `mapstructure:"password"` + // Values of the requested Secret Fields. + Fields map[string]string `mapstructure:"fields"` } func (d *Datasource) ConfigSpec() hcldec.ObjectSpec { @@ -50,32 +49,30 @@ func (d *Datasource) OutputSpec() hcldec.ObjectSpec { } func (d *Datasource) Execute() (cty.Value, error) { - output := DatasourceOutput{} - - emptyOutput := hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()) - client, err := d.config.CreateClient() if err != nil { - return emptyOutput, err + return cty.NullVal(cty.EmptyObject), err } // TSS SDK only supports retrieving secrets by ID secret, err := client.Secret(d.config.SecretID) if err != nil { - return emptyOutput, err + return cty.NullVal(cty.EmptyObject), err } - output.ID = secret.ID + secretFields := make(map[string]string, len(d.config.SecretFields)) - var success bool - output.Username, success = secret.Field("username") - if !success { - output.Username = "" + for _, field := range d.config.SecretFields { + var success bool + secretFields[field], success = secret.Field(field) + if !success { + secretFields[field] = "" + } } - output.Password, success = secret.Field("password") - if !success { - output.Password = "" + output := DatasourceOutput{ + ID: secret.ID, + Fields: secretFields, } return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil diff --git a/datasource/tss/data.hcl2spec.go b/datasource/tss/data.hcl2spec.go index 4f68efa..974cfee 100644 --- a/datasource/tss/data.hcl2spec.go +++ b/datasource/tss/data.hcl2spec.go @@ -10,11 +10,12 @@ import ( // FlatConfig is an auto-generated flat version of Config. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatConfig struct { - Username *string `mapstructure:"username" require:"true" cty:"username" hcl:"username"` - Password *string `mapstructure:"password" require:"true" cty:"password" hcl:"password"` - ServerURL *string `mapstructure:"server_url" require:"true" cty:"server_url" hcl:"server_url"` - Domain *string `mapstructure:"domain" cty:"domain" hcl:"domain"` - SecretID *int `mapstructure:"secret_id" required:"true" cty:"secret_id" hcl:"secret_id"` + Username *string `mapstructure:"username" require:"true" cty:"username" hcl:"username"` + Password *string `mapstructure:"password" require:"true" cty:"password" hcl:"password"` + ServerURL *string `mapstructure:"server_url" require:"true" cty:"server_url" hcl:"server_url"` + Domain *string `mapstructure:"domain" cty:"domain" hcl:"domain"` + SecretID *int `mapstructure:"secret_id" required:"true" cty:"secret_id" hcl:"secret_id"` + SecretFields []string `mapstructure:"secret_fields" required:"true" cty:"secret_fields" hcl:"secret_fields"` } // FlatMapstructure returns a new FlatConfig. @@ -29,11 +30,12 @@ func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } // The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ - "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, - "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, - "server_url": &hcldec.AttrSpec{Name: "server_url", Type: cty.String, Required: false}, - "domain": &hcldec.AttrSpec{Name: "domain", Type: cty.String, Required: false}, - "secret_id": &hcldec.AttrSpec{Name: "secret_id", Type: cty.Number, Required: false}, + "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, + "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, + "server_url": &hcldec.AttrSpec{Name: "server_url", Type: cty.String, Required: false}, + "domain": &hcldec.AttrSpec{Name: "domain", Type: cty.String, Required: false}, + "secret_id": &hcldec.AttrSpec{Name: "secret_id", Type: cty.Number, Required: false}, + "secret_fields": &hcldec.AttrSpec{Name: "secret_fields", Type: cty.List(cty.String), Required: false}, } return s } @@ -41,9 +43,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { // FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatDatasourceOutput struct { - ID *int `mapstructure:"id" cty:"id" hcl:"id"` - Username *string `mapstructure:"username" cty:"username" hcl:"username"` - Password *string `mapstructure:"password" cty:"password" hcl:"password"` + ID *int `mapstructure:"id" cty:"id" hcl:"id"` + Fields map[string]string `mapstructure:"fields" cty:"fields" hcl:"fields"` } // FlatMapstructure returns a new FlatDatasourceOutput. @@ -58,9 +59,8 @@ func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcl // The decoded values from this spec will then be applied to a FlatDatasourceOutput. func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ - "id": &hcldec.AttrSpec{Name: "id", Type: cty.Number, Required: false}, - "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, - "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, + "id": &hcldec.AttrSpec{Name: "id", Type: cty.Number, Required: false}, + "fields": &hcldec.AttrSpec{Name: "fields", Type: cty.Map(cty.String), Required: false}, } return s } diff --git a/example/data.pkr.hcl b/example/data.pkr.hcl index 3340c27..851d521 100644 --- a/example/data.pkr.hcl +++ b/example/data.pkr.hcl @@ -1,7 +1,11 @@ data "tss" "mock-data" { - username = "testing" - password = "test123" + username = "testing" + password = "test123" server_url = "https://my-thycotic-server.example.com/SecretServer" secret_id = "500" + secret_fields = [ + "password", + "username", + ] } From e70a998f57741da72c204f1b7a31e19fefac1f06 Mon Sep 17 00:00:00 2001 From: laszlojau <49835454+laszlojau@users.noreply.github.com> Date: Sun, 26 Sep 2021 13:22:47 +0930 Subject: [PATCH 2/2] Add instructions for using sensitive values --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 2a00c3c..dff6811 100644 --- a/README.md +++ b/README.md @@ -115,5 +115,14 @@ build { } ``` +**NOTE:** Packer does not seem to support sensitive values from custom data sources yet. If you are passing the variables to provisioners and wish to keep them sensitive, you can create a sensitive local. + +```hcl +local "my_secret_password" { + expression = "${data.mock-data.fields.password}" + sensitive = true +} +``` + ## Packer Compatibility This template is compatible with Packer >= v1.7.0