Add HCL2 aws_secretsmanager function (#10124)
* refactor aws get secrets function out to reuse it else where * add aws_secretsmanager func and docs for HCL2 * fix GetSecret: allow to pick secret version
This commit is contained in:
parent
584fea678b
commit
6d4fae0f2d
|
@ -9,6 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
consulapi "github.com/hashicorp/consul/api"
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
|
awssmapi "github.com/hashicorp/packer/template/interpolate/aws/secretsmanager"
|
||||||
vaultapi "github.com/hashicorp/vault/api"
|
vaultapi "github.com/hashicorp/vault/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,3 +87,23 @@ func Consul(k string) (string, error) {
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAWSSecret(name, key string) (string, error) {
|
||||||
|
// Check if at least 1 parameter has been used
|
||||||
|
if len(name) == 0 {
|
||||||
|
return "", errors.New("At least one secret name must be provided")
|
||||||
|
}
|
||||||
|
// client uses AWS SDK CredentialChain method. So,credentials can
|
||||||
|
// be loaded from credential file, environment variables, or IAM
|
||||||
|
// roles.
|
||||||
|
client := awssmapi.New(
|
||||||
|
&awssmapi.AWSConfig{},
|
||||||
|
)
|
||||||
|
|
||||||
|
spec := &awssmapi.SecretSpec{
|
||||||
|
Name: name,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.GetSecret(spec)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package function
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
|
|
||||||
|
commontpl "github.com/hashicorp/packer/common/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AWSSecret constructs a function that retrieves secrets from aws secrets
|
||||||
|
// manager. If Key field is not set then we will return first secret key stored
|
||||||
|
// in secret name.
|
||||||
|
var AWSSecret = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Type: cty.String,
|
||||||
|
AllowNull: false,
|
||||||
|
AllowUnknown: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "key",
|
||||||
|
Type: cty.String,
|
||||||
|
AllowNull: true,
|
||||||
|
AllowUnknown: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||||
|
name := args[0].AsString()
|
||||||
|
var key string
|
||||||
|
if !args[1].IsNull() && args[1].IsWhollyKnown() {
|
||||||
|
key = args[1].AsString()
|
||||||
|
}
|
||||||
|
val, err := commontpl.GetAWSSecret(name, key)
|
||||||
|
|
||||||
|
return cty.StringVal(val), err
|
||||||
|
},
|
||||||
|
})
|
|
@ -28,90 +28,91 @@ import (
|
||||||
func Functions(basedir string) map[string]function.Function {
|
func Functions(basedir string) map[string]function.Function {
|
||||||
|
|
||||||
funcs := map[string]function.Function{
|
funcs := map[string]function.Function{
|
||||||
"abs": stdlib.AbsoluteFunc,
|
"abs": stdlib.AbsoluteFunc,
|
||||||
"abspath": filesystem.AbsPathFunc,
|
"abspath": filesystem.AbsPathFunc,
|
||||||
"basename": filesystem.BasenameFunc,
|
"aws_secretsmanager": pkrfunction.AWSSecret,
|
||||||
"base64decode": encoding.Base64DecodeFunc,
|
"basename": filesystem.BasenameFunc,
|
||||||
"base64encode": encoding.Base64EncodeFunc,
|
"base64decode": encoding.Base64DecodeFunc,
|
||||||
"bcrypt": crypto.BcryptFunc,
|
"base64encode": encoding.Base64EncodeFunc,
|
||||||
"can": tryfunc.CanFunc,
|
"bcrypt": crypto.BcryptFunc,
|
||||||
"ceil": stdlib.CeilFunc,
|
"can": tryfunc.CanFunc,
|
||||||
"chomp": stdlib.ChompFunc,
|
"ceil": stdlib.CeilFunc,
|
||||||
"chunklist": stdlib.ChunklistFunc,
|
"chomp": stdlib.ChompFunc,
|
||||||
"cidrhost": cidr.HostFunc,
|
"chunklist": stdlib.ChunklistFunc,
|
||||||
"cidrnetmask": cidr.NetmaskFunc,
|
"cidrhost": cidr.HostFunc,
|
||||||
"cidrsubnet": cidr.SubnetFunc,
|
"cidrnetmask": cidr.NetmaskFunc,
|
||||||
"cidrsubnets": cidr.SubnetsFunc,
|
"cidrsubnet": cidr.SubnetFunc,
|
||||||
"coalesce": collection.CoalesceFunc,
|
"cidrsubnets": cidr.SubnetsFunc,
|
||||||
"coalescelist": stdlib.CoalesceListFunc,
|
"coalesce": collection.CoalesceFunc,
|
||||||
"compact": stdlib.CompactFunc,
|
"coalescelist": stdlib.CoalesceListFunc,
|
||||||
"concat": stdlib.ConcatFunc,
|
"compact": stdlib.CompactFunc,
|
||||||
"consul_key": pkrfunction.ConsulFunc,
|
"concat": stdlib.ConcatFunc,
|
||||||
"contains": stdlib.ContainsFunc,
|
"consul_key": pkrfunction.ConsulFunc,
|
||||||
"convert": typeexpr.ConvertFunc,
|
"contains": stdlib.ContainsFunc,
|
||||||
"csvdecode": stdlib.CSVDecodeFunc,
|
"convert": typeexpr.ConvertFunc,
|
||||||
"dirname": filesystem.DirnameFunc,
|
"csvdecode": stdlib.CSVDecodeFunc,
|
||||||
"distinct": stdlib.DistinctFunc,
|
"dirname": filesystem.DirnameFunc,
|
||||||
"element": stdlib.ElementFunc,
|
"distinct": stdlib.DistinctFunc,
|
||||||
"file": filesystem.MakeFileFunc(basedir, false),
|
"element": stdlib.ElementFunc,
|
||||||
"fileexists": filesystem.MakeFileExistsFunc(basedir),
|
"file": filesystem.MakeFileFunc(basedir, false),
|
||||||
"fileset": filesystem.MakeFileSetFunc(basedir),
|
"fileexists": filesystem.MakeFileExistsFunc(basedir),
|
||||||
"flatten": stdlib.FlattenFunc,
|
"fileset": filesystem.MakeFileSetFunc(basedir),
|
||||||
"floor": stdlib.FloorFunc,
|
"flatten": stdlib.FlattenFunc,
|
||||||
"format": stdlib.FormatFunc,
|
"floor": stdlib.FloorFunc,
|
||||||
"formatdate": stdlib.FormatDateFunc,
|
"format": stdlib.FormatFunc,
|
||||||
"formatlist": stdlib.FormatListFunc,
|
"formatdate": stdlib.FormatDateFunc,
|
||||||
"indent": stdlib.IndentFunc,
|
"formatlist": stdlib.FormatListFunc,
|
||||||
"index": stdlib.IndexFunc,
|
"indent": stdlib.IndentFunc,
|
||||||
"join": stdlib.JoinFunc,
|
"index": stdlib.IndexFunc,
|
||||||
"jsondecode": stdlib.JSONDecodeFunc,
|
"join": stdlib.JoinFunc,
|
||||||
"jsonencode": stdlib.JSONEncodeFunc,
|
"jsondecode": stdlib.JSONDecodeFunc,
|
||||||
"keys": stdlib.KeysFunc,
|
"jsonencode": stdlib.JSONEncodeFunc,
|
||||||
"length": stdlib.LengthFunc,
|
"keys": stdlib.KeysFunc,
|
||||||
"log": stdlib.LogFunc,
|
"length": stdlib.LengthFunc,
|
||||||
"lookup": stdlib.LookupFunc,
|
"log": stdlib.LogFunc,
|
||||||
"lower": stdlib.LowerFunc,
|
"lookup": stdlib.LookupFunc,
|
||||||
"max": stdlib.MaxFunc,
|
"lower": stdlib.LowerFunc,
|
||||||
"md5": crypto.Md5Func,
|
"max": stdlib.MaxFunc,
|
||||||
"merge": stdlib.MergeFunc,
|
"md5": crypto.Md5Func,
|
||||||
"min": stdlib.MinFunc,
|
"merge": stdlib.MergeFunc,
|
||||||
"parseint": stdlib.ParseIntFunc,
|
"min": stdlib.MinFunc,
|
||||||
"pathexpand": filesystem.PathExpandFunc,
|
"parseint": stdlib.ParseIntFunc,
|
||||||
"pow": stdlib.PowFunc,
|
"pathexpand": filesystem.PathExpandFunc,
|
||||||
"range": stdlib.RangeFunc,
|
"pow": stdlib.PowFunc,
|
||||||
"reverse": stdlib.ReverseFunc,
|
"range": stdlib.RangeFunc,
|
||||||
"replace": stdlib.ReplaceFunc,
|
"reverse": stdlib.ReverseFunc,
|
||||||
"regex_replace": stdlib.RegexReplaceFunc,
|
"replace": stdlib.ReplaceFunc,
|
||||||
"rsadecrypt": crypto.RsaDecryptFunc,
|
"regex_replace": stdlib.RegexReplaceFunc,
|
||||||
"setintersection": stdlib.SetIntersectionFunc,
|
"rsadecrypt": crypto.RsaDecryptFunc,
|
||||||
"setproduct": stdlib.SetProductFunc,
|
"setintersection": stdlib.SetIntersectionFunc,
|
||||||
"setunion": stdlib.SetUnionFunc,
|
"setproduct": stdlib.SetProductFunc,
|
||||||
"sha1": crypto.Sha1Func,
|
"setunion": stdlib.SetUnionFunc,
|
||||||
"sha256": crypto.Sha256Func,
|
"sha1": crypto.Sha1Func,
|
||||||
"sha512": crypto.Sha512Func,
|
"sha256": crypto.Sha256Func,
|
||||||
"signum": stdlib.SignumFunc,
|
"sha512": crypto.Sha512Func,
|
||||||
"slice": stdlib.SliceFunc,
|
"signum": stdlib.SignumFunc,
|
||||||
"sort": stdlib.SortFunc,
|
"slice": stdlib.SliceFunc,
|
||||||
"split": stdlib.SplitFunc,
|
"sort": stdlib.SortFunc,
|
||||||
"strrev": stdlib.ReverseFunc,
|
"split": stdlib.SplitFunc,
|
||||||
"substr": stdlib.SubstrFunc,
|
"strrev": stdlib.ReverseFunc,
|
||||||
"timestamp": pkrfunction.TimestampFunc,
|
"substr": stdlib.SubstrFunc,
|
||||||
"timeadd": stdlib.TimeAddFunc,
|
"timestamp": pkrfunction.TimestampFunc,
|
||||||
"title": stdlib.TitleFunc,
|
"timeadd": stdlib.TimeAddFunc,
|
||||||
"trim": stdlib.TrimFunc,
|
"title": stdlib.TitleFunc,
|
||||||
"trimprefix": stdlib.TrimPrefixFunc,
|
"trim": stdlib.TrimFunc,
|
||||||
"trimspace": stdlib.TrimSpaceFunc,
|
"trimprefix": stdlib.TrimPrefixFunc,
|
||||||
"trimsuffix": stdlib.TrimSuffixFunc,
|
"trimspace": stdlib.TrimSpaceFunc,
|
||||||
"try": tryfunc.TryFunc,
|
"trimsuffix": stdlib.TrimSuffixFunc,
|
||||||
"upper": stdlib.UpperFunc,
|
"try": tryfunc.TryFunc,
|
||||||
"urlencode": encoding.URLEncodeFunc,
|
"upper": stdlib.UpperFunc,
|
||||||
"uuidv4": uuid.V4Func,
|
"urlencode": encoding.URLEncodeFunc,
|
||||||
"uuidv5": uuid.V5Func,
|
"uuidv4": uuid.V4Func,
|
||||||
"values": stdlib.ValuesFunc,
|
"uuidv5": uuid.V5Func,
|
||||||
"vault": pkrfunction.VaultFunc,
|
"values": stdlib.ValuesFunc,
|
||||||
"yamldecode": ctyyaml.YAMLDecodeFunc,
|
"vault": pkrfunction.VaultFunc,
|
||||||
"yamlencode": ctyyaml.YAMLEncodeFunc,
|
"yamldecode": ctyyaml.YAMLDecodeFunc,
|
||||||
"zipmap": stdlib.ZipmapFunc,
|
"yamlencode": ctyyaml.YAMLEncodeFunc,
|
||||||
|
"zipmap": stdlib.ZipmapFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
return funcs
|
return funcs
|
||||||
|
|
|
@ -53,6 +53,9 @@ func (c *Client) GetSecret(spec *SecretSpec) (string, error) {
|
||||||
SecretId: aws.String(spec.Name),
|
SecretId: aws.String(spec.Name),
|
||||||
VersionStage: aws.String("AWSCURRENT"),
|
VersionStage: aws.String("AWSCURRENT"),
|
||||||
}
|
}
|
||||||
|
if spec.Name != "" {
|
||||||
|
params.VersionStage = aws.String(spec.Key)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := c.api.GetSecretValue(params)
|
resp, err := c.api.GetSecretValue(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
commontpl "github.com/hashicorp/packer/common/template"
|
commontpl "github.com/hashicorp/packer/common/template"
|
||||||
"github.com/hashicorp/packer/common/uuid"
|
"github.com/hashicorp/packer/common/uuid"
|
||||||
"github.com/hashicorp/packer/helper/common"
|
"github.com/hashicorp/packer/helper/common"
|
||||||
awssmapi "github.com/hashicorp/packer/template/interpolate/aws/secretsmanager"
|
|
||||||
"github.com/hashicorp/packer/version"
|
"github.com/hashicorp/packer/version"
|
||||||
strftime "github.com/jehiah/go-strftime"
|
strftime "github.com/jehiah/go-strftime"
|
||||||
)
|
)
|
||||||
|
@ -280,37 +279,16 @@ func funcGenAwsSecrets(ctx *Context) interface{} {
|
||||||
// semantic checks should catch this.
|
// semantic checks should catch this.
|
||||||
return "", errors.New("AWS Secrets Manager is only allowed in the variables section")
|
return "", errors.New("AWS Secrets Manager is only allowed in the variables section")
|
||||||
}
|
}
|
||||||
|
switch len(secret) {
|
||||||
// Check if at least 1 parameter has been used
|
case 0:
|
||||||
if len(secret) == 0 {
|
return "", errors.New("secret name must be provided")
|
||||||
return "", errors.New("At least one secret name must be provided")
|
case 1:
|
||||||
|
return commontpl.GetAWSSecret(secret[0], "")
|
||||||
|
case 2:
|
||||||
|
return commontpl.GetAWSSecret(secret[0], secret[1])
|
||||||
|
default:
|
||||||
|
return "", errors.New("only secret name and optional secret key can be provided.")
|
||||||
}
|
}
|
||||||
// client uses AWS SDK CredentialChain method. So,credentials can
|
|
||||||
// be loaded from credential file, environment variables, or IAM
|
|
||||||
// roles.
|
|
||||||
client := awssmapi.New(
|
|
||||||
&awssmapi.AWSConfig{},
|
|
||||||
)
|
|
||||||
|
|
||||||
var name, key string
|
|
||||||
name = secret[0]
|
|
||||||
// key is optional if not used we fetch the first
|
|
||||||
// value stored in given secret. If more than two parameters
|
|
||||||
// are passed we take second param and ignore the others
|
|
||||||
if len(secret) > 1 {
|
|
||||||
key = secret[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
spec := &awssmapi.SecretSpec{
|
|
||||||
Name: name,
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := client.GetSecret(spec)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@ export default [
|
||||||
{
|
{
|
||||||
category: 'contextual',
|
category: 'contextual',
|
||||||
content: [
|
content: [
|
||||||
'vault',
|
'aws_secretsmanager',
|
||||||
'consul',
|
'consul',
|
||||||
|
'vault',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
---
|
||||||
|
layout: docs
|
||||||
|
page_title: aws_secretsmanager - Functions - Configuration Language
|
||||||
|
sidebar_title: aws_secretsmanager
|
||||||
|
description: The aws_secretsmanager function retrieves secrets from Amazon secretsmanager stores.
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
# `aws_secretsmanager_key` Function
|
||||||
|
|
||||||
|
Secrets can be read from the [AWS Secrets
|
||||||
|
Manager](https://aws.amazon.com/secrets-manager/) and used within your template
|
||||||
|
as locals.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
aws_secretsmanager(name, key)
|
||||||
|
```
|
||||||
|
|
||||||
|
When key is not set (`null` or empty: `""`) then `aws_secretsmanager` returns
|
||||||
|
the first secret key stored in secret `name` using the `AWSCURRENT`.
|
||||||
|
|
||||||
|
You can either use this function in a `locals` block or directly inline where
|
||||||
|
you want to use the value.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
locals {
|
||||||
|
// null is equivalent to "AWSCURRENT"
|
||||||
|
current_version = aws_secretsmanager("my_secret", null)
|
||||||
|
}
|
||||||
|
|
||||||
|
source "null" "first-example" {
|
||||||
|
communicator = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
build {
|
||||||
|
name = "my-build-name"
|
||||||
|
sources = ["null.first-example"]
|
||||||
|
|
||||||
|
provisioner "shell-local" {
|
||||||
|
environment_vars = ["TESTVAR=${build.PackerRunUUID}"]
|
||||||
|
inline = ["echo current version is '${local.current_version}'",
|
||||||
|
"echo previous version is '${aws_secretsmanager("my_secret", "AWSPREVIOUS")}'."]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will load the key stored at behind `my_secret` from aws secrets manager.
|
||||||
|
|
||||||
|
|
||||||
|
In order to use this function you have to configure valid AWS credentials using
|
||||||
|
one of the following methods:
|
||||||
|
|
||||||
|
- [Environment Variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)
|
||||||
|
- [CLI Configuration Files](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)
|
||||||
|
- [Container Credentials](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)
|
||||||
|
- [Instance Profile Credentials](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
|
Loading…
Reference in New Issue