github.com/brandonstevens/terraform@v0.9.6-0.20170512224929-5367f2607e16/command/output.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "flag" 7 "fmt" 8 "sort" 9 "strings" 10 ) 11 12 // OutputCommand is a Command implementation that reads an output 13 // from a Terraform state and prints it. 14 type OutputCommand struct { 15 Meta 16 } 17 18 func (c *OutputCommand) Run(args []string) int { 19 args = c.Meta.process(args, false) 20 21 var module string 22 var jsonOutput bool 23 cmdFlags := flag.NewFlagSet("output", flag.ContinueOnError) 24 cmdFlags.BoolVar(&jsonOutput, "json", false, "json") 25 cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 26 cmdFlags.StringVar(&module, "module", "", "module") 27 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 28 if err := cmdFlags.Parse(args); err != nil { 29 return 1 30 } 31 32 args = cmdFlags.Args() 33 if len(args) > 1 { 34 c.Ui.Error( 35 "The output command expects exactly one argument with the name\n" + 36 "of an output variable or no arguments to show all outputs.\n") 37 cmdFlags.Usage() 38 return 1 39 } 40 41 name := "" 42 if len(args) > 0 { 43 name = args[0] 44 } 45 46 // Load the backend 47 b, err := c.Backend(nil) 48 if err != nil { 49 c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) 50 return 1 51 } 52 53 env := c.Env() 54 55 // Get the state 56 stateStore, err := b.State(env) 57 if err != nil { 58 c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) 59 return 1 60 } 61 62 if err := stateStore.RefreshState(); err != nil { 63 c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) 64 return 1 65 } 66 67 if module == "" { 68 module = "root" 69 } else { 70 module = "root." + module 71 } 72 73 // Get the proper module we want to get outputs for 74 modPath := strings.Split(module, ".") 75 76 state := stateStore.State() 77 mod := state.ModuleByPath(modPath) 78 if mod == nil { 79 c.Ui.Error(fmt.Sprintf( 80 "The module %s could not be found. There is nothing to output.", 81 module)) 82 return 1 83 } 84 85 if state.Empty() || len(mod.Outputs) == 0 { 86 c.Ui.Error( 87 "The state file either has no outputs defined, or all the defined\n" + 88 "outputs are empty. Please define an output in your configuration\n" + 89 "with the `output` keyword and run `terraform refresh` for it to\n" + 90 "become available. If you are using interpolation, please verify\n" + 91 "the interpolated value is not empty. You can use the \n" + 92 "`terraform console` command to assist.") 93 return 1 94 } 95 96 if name == "" { 97 if jsonOutput { 98 jsonOutputs, err := json.MarshalIndent(mod.Outputs, "", " ") 99 if err != nil { 100 return 1 101 } 102 103 c.Ui.Output(string(jsonOutputs)) 104 return 0 105 } else { 106 c.Ui.Output(outputsAsString(state, modPath, nil, false)) 107 return 0 108 } 109 } 110 111 v, ok := mod.Outputs[name] 112 if !ok { 113 c.Ui.Error(fmt.Sprintf( 114 "The output variable requested could not be found in the state\n" + 115 "file. If you recently added this to your configuration, be\n" + 116 "sure to run `terraform apply`, since the state won't be updated\n" + 117 "with new output variables until that command is run.")) 118 return 1 119 } 120 121 if jsonOutput { 122 jsonOutputs, err := json.MarshalIndent(v, "", " ") 123 if err != nil { 124 return 1 125 } 126 127 c.Ui.Output(string(jsonOutputs)) 128 } else { 129 switch output := v.Value.(type) { 130 case string: 131 c.Ui.Output(output) 132 return 0 133 case []interface{}: 134 c.Ui.Output(formatListOutput("", "", output)) 135 return 0 136 case map[string]interface{}: 137 c.Ui.Output(formatMapOutput("", "", output)) 138 return 0 139 default: 140 c.Ui.Error(fmt.Sprintf("Unknown output type: %T", v.Type)) 141 return 1 142 } 143 } 144 145 return 0 146 } 147 148 func formatNestedList(indent string, outputList []interface{}) string { 149 outputBuf := new(bytes.Buffer) 150 outputBuf.WriteString(fmt.Sprintf("%s[", indent)) 151 152 lastIdx := len(outputList) - 1 153 154 for i, value := range outputList { 155 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, " ", value)) 156 if i != lastIdx { 157 outputBuf.WriteString(",") 158 } 159 } 160 161 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 162 return strings.TrimPrefix(outputBuf.String(), "\n") 163 } 164 165 func formatListOutput(indent, outputName string, outputList []interface{}) string { 166 keyIndent := "" 167 168 outputBuf := new(bytes.Buffer) 169 170 if outputName != "" { 171 outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) 172 keyIndent = " " 173 } 174 175 lastIdx := len(outputList) - 1 176 177 for i, value := range outputList { 178 switch typedValue := value.(type) { 179 case string: 180 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) 181 case []interface{}: 182 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 183 formatNestedList(indent+keyIndent, typedValue))) 184 case map[string]interface{}: 185 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 186 formatNestedMap(indent+keyIndent, typedValue))) 187 } 188 189 if lastIdx != i { 190 outputBuf.WriteString(",") 191 } 192 } 193 194 if outputName != "" { 195 if len(outputList) > 0 { 196 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 197 } else { 198 outputBuf.WriteString("]") 199 } 200 } 201 202 return strings.TrimPrefix(outputBuf.String(), "\n") 203 } 204 205 func formatNestedMap(indent string, outputMap map[string]interface{}) string { 206 ks := make([]string, 0, len(outputMap)) 207 for k, _ := range outputMap { 208 ks = append(ks, k) 209 } 210 sort.Strings(ks) 211 212 outputBuf := new(bytes.Buffer) 213 outputBuf.WriteString(fmt.Sprintf("%s{", indent)) 214 215 lastIdx := len(outputMap) - 1 216 for i, k := range ks { 217 v := outputMap[k] 218 outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+" ", k, v)) 219 220 if lastIdx != i { 221 outputBuf.WriteString(",") 222 } 223 } 224 225 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 226 227 return strings.TrimPrefix(outputBuf.String(), "\n") 228 } 229 230 func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { 231 ks := make([]string, 0, len(outputMap)) 232 for k, _ := range outputMap { 233 ks = append(ks, k) 234 } 235 sort.Strings(ks) 236 237 keyIndent := "" 238 239 outputBuf := new(bytes.Buffer) 240 if outputName != "" { 241 outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) 242 keyIndent = " " 243 } 244 245 for _, k := range ks { 246 v := outputMap[k] 247 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) 248 } 249 250 if outputName != "" { 251 if len(outputMap) > 0 { 252 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 253 } else { 254 outputBuf.WriteString("}") 255 } 256 } 257 258 return strings.TrimPrefix(outputBuf.String(), "\n") 259 } 260 261 func (c *OutputCommand) Help() string { 262 helpText := ` 263 Usage: terraform output [options] [NAME] 264 265 Reads an output variable from a Terraform state file and prints 266 the value. With no additional arguments, output will display all 267 the outputs for the root module. If NAME is not specified, all 268 outputs are printed. 269 270 Options: 271 272 -state=path Path to the state file to read. Defaults to 273 "terraform.tfstate". 274 275 -no-color If specified, output won't contain any color. 276 277 -module=name If specified, returns the outputs for a 278 specific module 279 280 -json If specified, machine readable output will be 281 printed in JSON format 282 283 ` 284 return strings.TrimSpace(helpText) 285 } 286 287 func (c *OutputCommand) Synopsis() string { 288 return "Read an output from a state file" 289 }