github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/vault/resource_generic_secret.go (about) 1 package vault 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 8 "github.com/hashicorp/terraform/helper/schema" 9 10 "github.com/hashicorp/vault/api" 11 ) 12 13 func genericSecretResource() *schema.Resource { 14 return &schema.Resource{ 15 Create: genericSecretResourceWrite, 16 Update: genericSecretResourceWrite, 17 Delete: genericSecretResourceDelete, 18 Read: genericSecretResourceRead, 19 20 Schema: map[string]*schema.Schema{ 21 "path": &schema.Schema{ 22 Type: schema.TypeString, 23 Required: true, 24 ForceNew: true, 25 Description: "Full path where the generic secret will be written.", 26 }, 27 28 // Data is passed as JSON so that an arbitrary structure is 29 // possible, rather than forcing e.g. all values to be strings. 30 "data_json": &schema.Schema{ 31 Type: schema.TypeString, 32 Required: true, 33 Description: "JSON-encoded secret data to write.", 34 // We rebuild the attached JSON string to a simple singleline 35 // string. This makes terraform not want to change when an extra 36 // space is included in the JSON string. It is also necesarry 37 // when allow_read is true for comparing values. 38 StateFunc: NormalizeDataJSON, 39 ValidateFunc: ValidateDataJSON, 40 }, 41 42 "allow_read": &schema.Schema{ 43 Type: schema.TypeBool, 44 Optional: true, 45 Default: false, 46 Description: "True if the provided token is allowed to read the secret from vault", 47 }, 48 }, 49 } 50 } 51 52 func ValidateDataJSON(configI interface{}, k string) ([]string, []error) { 53 dataJSON := configI.(string) 54 dataMap := map[string]interface{}{} 55 err := json.Unmarshal([]byte(dataJSON), &dataMap) 56 if err != nil { 57 return nil, []error{err} 58 } 59 return nil, nil 60 } 61 62 func NormalizeDataJSON(configI interface{}) string { 63 dataJSON := configI.(string) 64 65 dataMap := map[string]interface{}{} 66 err := json.Unmarshal([]byte(dataJSON), &dataMap) 67 if err != nil { 68 // The validate function should've taken care of this. 69 return "" 70 } 71 72 ret, err := json.Marshal(dataMap) 73 if err != nil { 74 // Should never happen. 75 return dataJSON 76 } 77 78 return string(ret) 79 } 80 81 func genericSecretResourceWrite(d *schema.ResourceData, meta interface{}) error { 82 client := meta.(*api.Client) 83 84 path := d.Get("path").(string) 85 86 var data map[string]interface{} 87 err := json.Unmarshal([]byte(d.Get("data_json").(string)), &data) 88 if err != nil { 89 return fmt.Errorf("data_json %#v syntax error: %s", d.Get("data_json"), err) 90 } 91 92 log.Printf("[DEBUG] Writing generic Vault secret to %s", path) 93 _, err = client.Logical().Write(path, data) 94 if err != nil { 95 return fmt.Errorf("error writing to Vault: %s", err) 96 } 97 98 d.SetId(path) 99 100 return nil 101 } 102 103 func genericSecretResourceDelete(d *schema.ResourceData, meta interface{}) error { 104 client := meta.(*api.Client) 105 106 path := d.Id() 107 108 log.Printf("[DEBUG] Deleting generic Vault from %s", path) 109 _, err := client.Logical().Delete(path) 110 if err != nil { 111 return fmt.Errorf("error deleting from Vault: %s", err) 112 } 113 114 return nil 115 } 116 117 func genericSecretResourceRead(d *schema.ResourceData, meta interface{}) error { 118 allowed_to_read := d.Get("allow_read").(bool) 119 path := d.Get("path").(string) 120 121 if allowed_to_read { 122 client := meta.(*api.Client) 123 124 log.Printf("[DEBUG] Reading %s from Vault", path) 125 secret, err := client.Logical().Read(path) 126 if err != nil { 127 return fmt.Errorf("error reading from Vault: %s", err) 128 } 129 130 // Ignoring error because this value came from JSON in the 131 // first place so no reason why it should fail to re-encode. 132 jsonDataBytes, _ := json.Marshal(secret.Data) 133 d.Set("data_json", string(jsonDataBytes)) 134 } 135 136 d.SetId(path) 137 log.Printf("[WARN] vault_generic_secret does not automatically refresh if allow_read is set to false") 138 return nil 139 }