Skip to content

Commit

Permalink
feat!: replace env resource by path pattern resource
Browse files Browse the repository at this point in the history
  • Loading branch information
charlthibault committed Apr 15, 2024
1 parent 1f24c5b commit 95285aa
Show file tree
Hide file tree
Showing 11 changed files with 84 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `ansiblevault_env` Data Source

Use `ansiblevault_env` data source to read in `group_vars/tag_<env>/vault.yml` file the specified `key`.
Use `ansiblevault_path_pattern` data source to read from path_pattern (see provider config) file the specified `key`.

## Example Usage

Expand All @@ -10,7 +10,7 @@ See [examples](https://github.com/MeilleursAgents/terraform-provider-ansiblevaul

The following arguments are supported:

* `env` - (Required) environment targeted for reading.
* `params` - (Required) A map to render the path_pattern. Must contains all keys given in path_pattern

* `key` - (Required) key to find in yaml.

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ See [examples](https://github.com/MeilleursAgents/terraform-provider-ansiblevaul
| Key | Required | EnvVar | Description |
|:--:|:--:|:--:|:--:|
| vault_path | | `ANSIBLE_VAULT_PASSWORD_FILE` | Path to ansible vault password file |
| path_pattern | | `ANSIBLE_VAULT_PATH_PATTERN` | Vault file path pattern to be used by ansiblevault_path_pattern resources (example: /group_vars/{{.env}}/vault.yml) |
| vault_pass | | `ANSIBLE_VAULT_PASS` | Ansible vault pass value |
| root_folder || `ANSIBLE_ROOT_FOLDER` | Ansible root directory |

Expand Down
5 changes: 4 additions & 1 deletion examples/terraform/provider_pass.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ variable "vault_pass" {

# See https://github.com/MeilleursAgents/terraform-provider-ansiblevault/blob/master/README.md for installation and usage
provider "ansiblevault" {
path_pattern = "/group_vars/tag_{{.env}}/vault.yml"
vault_pass = var.vault_pass
root_folder = "../ansible"
}
Expand All @@ -15,7 +16,9 @@ data "ansiblevault_path" "path" {
}

data "ansiblevault_env" "env" {
env = "prod"
params = {
env = "prod"
}
key = "API_KEY"
}

Expand Down
18 changes: 9 additions & 9 deletions pkg/provider/in_env.go → pkg/provider/in_path_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func inEnvResource() *schema.Resource {
func inPathPatternResource() *schema.Resource {
return &schema.Resource{
Read: inEnvRead,
Read: inPathPatternRead,
Schema: map[string]*schema.Schema{
"env": {
Type: schema.TypeString,
Description: "Ansible environment searched",
"params": {
Type: schema.TypeMap,
Description: "Parameters for path pattern",
Required: true,
},
"key": {
Expand All @@ -31,18 +31,18 @@ func inEnvResource() *schema.Resource {
}
}

func inEnvRead(data *schema.ResourceData, m interface{}) error {
env := data.Get("env").(string)
func inPathPatternRead(data *schema.ResourceData, m interface{}) error {
params := data.Get("params").(map[string]interface{})
key := data.Get("key").(string)

data.SetId(time.Now().UTC().String())

value, err := m.(*vault.App).InEnv(env, key)
value, err := m.(*vault.App).InPathPattern(params, key)
if err != nil {
data.SetId("")

if err == vault.ErrKeyNotFound {
return fmt.Errorf("%s not found in %s vault", key, env)
return fmt.Errorf("not found in %s vault", key)
}

return err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@ import (
func TestInEnvRead(t *testing.T) {
var cases = []struct {
intention string
env string
params map[string]interface{}
key string
want string
wantErr error
}{
{
"simple",
"prod",
map[string]interface{}{"env": "prod"},
"API_KEY",
"PROD_KEEP_IT_SECRET",
nil,
},
{
"not found key",
"prod",
map[string]interface{}{"env": "prod"},
"SECRET_KEY",
"",
errors.New("SECRET_KEY not found in prod vault"),
errors.New("not found in SECRET_KEY vault"),
},
{
"not found env",
"dev",
map[string]interface{}{"env": "dev"},
"SECRET_KEY",
"",
fmt.Errorf("open %s: no such file or directory", path.Join(ansibleFolder, "group_vars/tag_dev/vault.yml")),
Expand All @@ -42,9 +42,9 @@ func TestInEnvRead(t *testing.T) {

for _, testCase := range cases {
t.Run(testCase.intention, func(t *testing.T) {
data := inEnvResource().Data(nil)
data := inPathPatternResource().Data(nil)

if err := data.Set("env", testCase.env); err != nil {
if err := data.Set("params", testCase.params); err != nil {
t.Errorf("unable to set env: %#v", err)
return
}
Expand All @@ -54,13 +54,13 @@ func TestInEnvRead(t *testing.T) {
return
}

vaultApp, err := vault.New("secret", ansibleFolder)
vaultApp, err := vault.New("secret", ansibleFolder, "/group_vars/tag_{{.env}}/vault.yml")
if err != nil {
t.Errorf("unable to create vault app: %#v", err)
return
}

err = inEnvRead(data, vaultApp)
err = inPathPatternRead(data, vaultApp)
result := data.Get("value").(string)

failed := false
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/in_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestInPathRead(t *testing.T) {
return
}

vaultApp, err := vault.New("secret", ansibleFolder)
vaultApp, err := vault.New("secret", ansibleFolder, "")
if err != nil {
t.Errorf("unable to create vault app: %#v", err)
return
Expand Down
4 changes: 2 additions & 2 deletions pkg/provider/in_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestInStringRead(t *testing.T) {
return
}

vaultApp, err := vault.New("secret", ansibleFolder)
vaultApp, err := vault.New("secret", ansibleFolder, "")
if err != nil {
t.Errorf("unable to create vault app: %#v", err)
return
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestInStringEncRead(t *testing.T) {
return
}

vaultApp, err := vault.New("secret", ansibleFolder)
vaultApp, err := vault.New("secret", ansibleFolder, "")
if err != nil {
t.Errorf("unable to create vault app: %#v", err)
return
Expand Down
18 changes: 12 additions & 6 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ func Provider() *schema.Provider {
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ANSIBLE_VAULT_PASSWORD_FILE", nil),
},
"path_pattern": {
Type: schema.TypeString,
Description: "Vault path pattern (example: '/group_vars/tag_{{ .env }}/vault.yml')",
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ANSIBLE_VAULT_PATH_PATTERN", nil),
},
"vault_pass": {
Type: schema.TypeString,
Description: "Ansible vault pass value",
Expand All @@ -39,24 +45,24 @@ func Provider() *schema.Provider {
},
},
DataSourcesMap: map[string]*schema.Resource{
"ansiblevault_env": inEnvResource(),
"ansiblevault_path": inPathResource(),
"ansiblevault_string": inStringResource(),
"ansiblevault_path_pattern": inPathPatternResource(),
"ansiblevault_path": inPathResource(),
"ansiblevault_string": inStringResource(),
},
ResourcesMap: map[string]*schema.Resource{
"ansiblevault_enc_string": inStringEncResource(),
},
ConfigureFunc: func(r *schema.ResourceData) (interface{}, error) {
return configure(r.Get("vault_path").(string), r.Get("vault_pass").(string), r.Get("root_folder").(string))
return configure(r.Get("vault_path").(string), r.Get("path_pattern").(string), r.Get("vault_pass").(string), r.Get("root_folder").(string))
},
}
}

func configure(path, pass, rootFolder string) (interface{}, error) {
func configure(path string, path_pattern string, pass string, rootFolder string) (interface{}, error) {
pass, err := vault.GetVaultPassword(path, pass)
if err != nil {
return nil, err
}

return vault.New(pass, rootFolder)
return vault.New(pass, rootFolder, path_pattern)
}
19 changes: 11 additions & 8 deletions pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,30 @@ func TestProvider(t *testing.T) {
}

func TestConfigure(t *testing.T) {
validVault, _ := vault.New("secret", "../../examples/ansible")
validVault, _ := vault.New("secret", "../../examples/ansible", "")

var cases = []struct {
intention string
path string
pass string
rootFolder string
want interface{}
wantErr error
intention string
path string
path_pattern string
pass string
rootFolder string
want interface{}
wantErr error
}{
{
"erroneous password",
"",
"",
"",
"",
nil,
vault.ErrNoVaultPass,
},
{
"erroneous password",
"",
"",
"secret",
"../../examples/ansible",
validVault,
Expand All @@ -62,7 +65,7 @@ func TestConfigure(t *testing.T) {

for _, testCase := range cases {
t.Run(testCase.intention, func(t *testing.T) {
result, err := configure(testCase.path, testCase.pass, testCase.rootFolder)
result, err := configure(testCase.path, testCase.path_pattern, testCase.pass, testCase.rootFolder)

failed := false

Expand Down
21 changes: 16 additions & 5 deletions pkg/vault/vault.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package vault

import (
"bytes"
"errors"
"fmt"
"html/template"
"io/ioutil"
"path"
"strconv"
Expand All @@ -27,17 +28,21 @@ var (
type App struct {
vaultPassword string
rootFolder string
path_template template.Template
}

// New creates new App from Config
func New(vaultPassword string, rootFolder string) (*App, error) {
func New(vaultPassword string, rootFolder string, path_pattern string) (*App, error) {
if rootFolder == "" {
return nil, ErrNoRootFolder
}

return &App{
vaultPassword: vaultPassword,
rootFolder: rootFolder,
path_template: *template.Must(
template.New("path_pattern").Parse(path_pattern),
),
}, nil
}

Expand Down Expand Up @@ -101,9 +106,15 @@ func (a App) getVaultKey(filename string, key string, getVaultContent func(strin
return "", ErrKeyNotFound
}

// InEnv retrieves given key in environment vault
func (a App) InEnv(env string, key string) (string, error) {
return a.getVaultKey(path.Join(a.rootFolder, fmt.Sprintf("group_vars/tag_%s/vault.yml", env)), key, ansible_vault.DecryptFile)
// InPathPattern retrieves given key in environment vault
func (a App) InPathPattern(params map[string]interface{}, key string) (string, error) {
var buffer bytes.Buffer
err := a.path_template.Execute(&buffer, params)
if err != nil {
return "", err
}

return a.getVaultKey(path.Join(a.rootFolder, buffer.String()), key, ansible_vault.DecryptFile)
}

// InPath retrieves given key in vault file
Expand Down
Loading

0 comments on commit 95285aa

Please sign in to comment.