Files

147 lines
4.6 KiB
Go

//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config,Secret,UniversalAuth
package secrets
import (
"fmt"
"os"
kms "github.com/hanzokms/packer-plugin/client"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/zclconf/go-cty/cty"
)
type Datasource struct {
config Config
client *kms.Client
}
type UniversalAuth struct {
// The Client ID for Hanzo KMS Universal Authentication.
ClientID string `mapstructure:"client_id" required:"true"`
// The Client Secret for Hanzo KMS Universal Authentication. You may use KMS_UNIVERSAL_AUTH_CLIENT_SECRET env variable instead.
ClientSecret string `mapstructure:"client_secret"`
}
type Config struct {
// The host URL of your Hanzo KMS instance. If a value isn't provided, KMS_HOST may be used. Default: https://kms.hanzo.ai
Host string `mapstructure:"host"`
// The project to list secrets from.
ProjectId string `mapstructure:"project_id" required:"true"`
// The secret path to list secrets from. Default: /
FolderPath string `mapstructure:"folder_path"`
// The environment to list secrets from. (e.g. dev, staging, prod)
EnvSlug string `mapstructure:"env_slug" required:"true"`
// Configuration for Hanzo KMS Universal Authentication.
UniversalAuth UniversalAuth `mapstructure:"universal_auth"`
}
type Secret struct {
Version int `mapstructure:"version" required:"true"`
Workspace string `mapstructure:"workspace" required:"true"`
Type string `mapstructure:"type" required:"true"`
Environment string `mapstructure:"environment" required:"true"`
SecretKey string `mapstructure:"secret_key" required:"true"`
SecretValue string `mapstructure:"secret_value" required:"true"`
SecretComment string `mapstructure:"secret_comment" required:"true"`
}
type DatasourceOutput struct {
Secrets map[string]Secret `mapstructure:"secrets" required:"true"`
}
func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
return d.config.FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Configure(raws ...interface{}) error {
err := config.Decode(&d.config, nil, raws...)
if err != nil {
return err
}
// Verify required variables are passed
if d.config.ProjectId == "" {
return fmt.Errorf("project_id is required")
}
if d.config.EnvSlug == "" {
return fmt.Errorf("env_slug is required")
}
if d.config.FolderPath == "" {
d.config.FolderPath = "/"
}
if d.config.Host == "" {
envHost := os.Getenv("KMS_HOST")
if envHost == "" {
d.config.Host = "https://kms.hanzo.ai"
} else {
d.config.Host = envHost
}
}
// Validate UniversalAuth credentials
if d.config.UniversalAuth.ClientID == "" {
return fmt.Errorf("universal_auth.client_id is required")
}
if d.config.UniversalAuth.ClientSecret == "" {
clientSecret := os.Getenv("KMS_UNIVERSAL_AUTH_CLIENT_SECRET")
if clientSecret == "" {
return fmt.Errorf("universal_auth.client_secret config variable or KMS_UNIVERSAL_AUTH_CLIENT_SECRET env variable required")
}
d.config.UniversalAuth.ClientSecret = clientSecret
}
clientCfg := kms.Config{
HostURL: d.config.Host,
ClientId: d.config.UniversalAuth.ClientID,
ClientSecret: d.config.UniversalAuth.ClientSecret,
}
client, client_err := kms.NewClient(clientCfg)
if client_err != nil {
return fmt.Errorf("failed to initialize Hanzo KMS client using Universal Authentication: %w", client_err)
}
d.client = client
return nil
}
func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Execute() (cty.Value, error) {
secrets, err := d.client.GetRawSecrets(d.config.FolderPath, d.config.EnvSlug, d.config.ProjectId)
if err != nil {
outputSchemaType := hcldec.ImpliedType(d.OutputSpec())
return cty.NullVal(outputSchemaType), fmt.Errorf("failed to retrieve secrets from Hanzo KMS API (folder: '%s', environment: '%s'): %w", d.config.FolderPath, d.config.EnvSlug, err)
}
ctySecretsMap := make(map[string]cty.Value)
for _, s := range secrets {
secretVal := cty.ObjectVal(map[string]cty.Value{
"version": cty.NumberIntVal(int64(s.Version)),
"workspace": cty.StringVal(s.Workspace),
"type": cty.StringVal(s.Type),
"environment": cty.StringVal(s.Environment),
"secret_key": cty.StringVal(s.SecretKey),
"secret_value": cty.StringVal(s.SecretValue),
"secret_comment": cty.StringVal(s.SecretComment),
})
ctySecretsMap[s.SecretKey] = secretVal
}
outputVal := cty.ObjectVal(map[string]cty.Value{
"secrets": cty.MapVal(ctySecretsMap),
})
return outputVal, nil
}