github.com/rvichery/terraform@v0.11.10/builtin/providers/terraform/data_source_state.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 multierror "github.com/hashicorp/go-multierror" 9 "github.com/hashicorp/terraform/backend" 10 backendInit "github.com/hashicorp/terraform/backend/init" 11 "github.com/hashicorp/terraform/config" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/hashicorp/terraform/terraform" 14 ) 15 16 func dataSourceRemoteState() *schema.Resource { 17 return &schema.Resource{ 18 Read: dataSourceRemoteStateRead, 19 20 Schema: map[string]*schema.Schema{ 21 "backend": { 22 Type: schema.TypeString, 23 Required: true, 24 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 25 if vStr, ok := v.(string); ok && vStr == "_local" { 26 ws = append(ws, "Use of the %q backend is now officially "+ 27 "supported as %q. Please update your configuration to ensure "+ 28 "compatibility with future versions of Terraform.", 29 "_local", "local") 30 } 31 32 return 33 }, 34 }, 35 36 // This field now contains all possible attributes that are supported 37 // by any of the existing backends. When merging this into 0.12 this 38 // should be reverted and instead the new 'cty.DynamicPseudoType' type 39 // should be used to make this work with any future backends as well. 40 "config": { 41 Type: schema.TypeSet, 42 Optional: true, 43 MaxItems: 1, 44 Elem: &schema.Resource{ 45 Schema: map[string]*schema.Schema{ 46 "path": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 }, 50 "hostname": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 }, 54 "organization": &schema.Schema{ 55 Type: schema.TypeString, 56 Optional: true, 57 }, 58 "token": &schema.Schema{ 59 Type: schema.TypeString, 60 Optional: true, 61 }, 62 "workspaces": &schema.Schema{ 63 Type: schema.TypeList, 64 Optional: true, 65 MaxItems: 1, 66 Elem: &schema.Schema{Type: schema.TypeMap}, 67 }, 68 "username": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 }, 72 "password": &schema.Schema{ 73 Type: schema.TypeString, 74 Optional: true, 75 }, 76 "url": &schema.Schema{ 77 Type: schema.TypeString, 78 Optional: true, 79 }, 80 "repo": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 }, 84 "subpath": &schema.Schema{ 85 Type: schema.TypeString, 86 Optional: true, 87 }, 88 "storage_account_name": &schema.Schema{ 89 Type: schema.TypeString, 90 Optional: true, 91 }, 92 "container_name": &schema.Schema{ 93 Type: schema.TypeString, 94 Optional: true, 95 }, 96 "key": &schema.Schema{ 97 Type: schema.TypeString, 98 Optional: true, 99 }, 100 "access_key": &schema.Schema{ 101 Type: schema.TypeString, 102 Optional: true, 103 }, 104 "environment": &schema.Schema{ 105 Type: schema.TypeString, 106 Optional: true, 107 }, 108 "resource_group_name": &schema.Schema{ 109 Type: schema.TypeString, 110 Optional: true, 111 }, 112 "arm_subscription_id": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 }, 116 "arm_client_id": &schema.Schema{ 117 Type: schema.TypeString, 118 Optional: true, 119 }, 120 "arm_client_secret": &schema.Schema{ 121 Type: schema.TypeString, 122 Optional: true, 123 }, 124 "arm_tenant_id": &schema.Schema{ 125 Type: schema.TypeString, 126 Optional: true, 127 }, 128 "access_token": &schema.Schema{ 129 Type: schema.TypeString, 130 Optional: true, 131 }, 132 "address": &schema.Schema{ 133 Type: schema.TypeString, 134 Optional: true, 135 }, 136 "scheme": &schema.Schema{ 137 Type: schema.TypeString, 138 Optional: true, 139 }, 140 "datacenter": &schema.Schema{ 141 Type: schema.TypeString, 142 Optional: true, 143 }, 144 "http_auth": &schema.Schema{ 145 Type: schema.TypeString, 146 Optional: true, 147 }, 148 "gzip": &schema.Schema{ 149 Type: schema.TypeString, 150 Optional: true, 151 }, 152 "lock": &schema.Schema{ 153 Type: schema.TypeString, 154 Optional: true, 155 }, 156 "ca_file": &schema.Schema{ 157 Type: schema.TypeString, 158 Optional: true, 159 }, 160 "cert_file": &schema.Schema{ 161 Type: schema.TypeString, 162 Optional: true, 163 }, 164 "key_file": &schema.Schema{ 165 Type: schema.TypeString, 166 Optional: true, 167 }, 168 "endpoints": &schema.Schema{ 169 Type: schema.TypeString, 170 Optional: true, 171 }, 172 "prefix": &schema.Schema{ 173 Type: schema.TypeString, 174 Optional: true, 175 }, 176 "cacert_path": &schema.Schema{ 177 Type: schema.TypeString, 178 Optional: true, 179 }, 180 "cert_path": &schema.Schema{ 181 Type: schema.TypeString, 182 Optional: true, 183 }, 184 "key_path": &schema.Schema{ 185 Type: schema.TypeString, 186 Optional: true, 187 }, 188 "bucket": &schema.Schema{ 189 Type: schema.TypeString, 190 Optional: true, 191 }, 192 "credentials": &schema.Schema{ 193 Type: schema.TypeString, 194 Optional: true, 195 }, 196 "project": &schema.Schema{ 197 Type: schema.TypeString, 198 Optional: true, 199 }, 200 "region": &schema.Schema{ 201 Type: schema.TypeString, 202 Optional: true, 203 }, 204 "encryption_key": &schema.Schema{ 205 Type: schema.TypeString, 206 Optional: true, 207 }, 208 "update_method": &schema.Schema{ 209 Type: schema.TypeString, 210 Optional: true, 211 }, 212 "lock_address": &schema.Schema{ 213 Type: schema.TypeString, 214 Optional: true, 215 }, 216 "lock_method": &schema.Schema{ 217 Type: schema.TypeString, 218 Optional: true, 219 }, 220 "unlock_address": &schema.Schema{ 221 Type: schema.TypeString, 222 Optional: true, 223 }, 224 "unlock_method": &schema.Schema{ 225 Type: schema.TypeString, 226 Optional: true, 227 }, 228 "skip_cert_verification": &schema.Schema{ 229 Type: schema.TypeString, 230 Optional: true, 231 }, 232 "account": &schema.Schema{ 233 Type: schema.TypeString, 234 Optional: true, 235 }, 236 "user": &schema.Schema{ 237 Type: schema.TypeString, 238 Optional: true, 239 }, 240 "key_material": &schema.Schema{ 241 Type: schema.TypeString, 242 Optional: true, 243 }, 244 "key_id": &schema.Schema{ 245 Type: schema.TypeString, 246 Optional: true, 247 }, 248 "insecure_skip_tls_verify": &schema.Schema{ 249 Type: schema.TypeString, 250 Optional: true, 251 }, 252 "object_name": &schema.Schema{ 253 Type: schema.TypeString, 254 Optional: true, 255 }, 256 "endpoint": &schema.Schema{ 257 Type: schema.TypeString, 258 Optional: true, 259 }, 260 "encrypt": &schema.Schema{ 261 Type: schema.TypeString, 262 Optional: true, 263 }, 264 "acl": &schema.Schema{ 265 Type: schema.TypeString, 266 Optional: true, 267 }, 268 "secret_key": &schema.Schema{ 269 Type: schema.TypeString, 270 Optional: true, 271 }, 272 "kms_key_id": &schema.Schema{ 273 Type: schema.TypeString, 274 Optional: true, 275 }, 276 "lock_table": &schema.Schema{ 277 Type: schema.TypeString, 278 Optional: true, 279 }, 280 "dynamodb_table": &schema.Schema{ 281 Type: schema.TypeString, 282 Optional: true, 283 }, 284 "profile": &schema.Schema{ 285 Type: schema.TypeString, 286 Optional: true, 287 }, 288 "shared_credentials_file": &schema.Schema{ 289 Type: schema.TypeString, 290 Optional: true, 291 }, 292 "role_arn": &schema.Schema{ 293 Type: schema.TypeString, 294 Optional: true, 295 }, 296 "assume_role_policy": &schema.Schema{ 297 Type: schema.TypeString, 298 Optional: true, 299 }, 300 "external_id": &schema.Schema{ 301 Type: schema.TypeString, 302 Optional: true, 303 }, 304 "session_name": &schema.Schema{ 305 Type: schema.TypeString, 306 Optional: true, 307 }, 308 "workspace_key_prefix": &schema.Schema{ 309 Type: schema.TypeString, 310 Optional: true, 311 }, 312 "skip_credentials_validation": &schema.Schema{ 313 Type: schema.TypeString, 314 Optional: true, 315 }, 316 "skip_get_ec2_platforms": &schema.Schema{ 317 Type: schema.TypeString, 318 Optional: true, 319 }, 320 "skip_region_validation": &schema.Schema{ 321 Type: schema.TypeString, 322 Optional: true, 323 }, 324 "skip_requesting_account_id": &schema.Schema{ 325 Type: schema.TypeString, 326 Optional: true, 327 }, 328 "skip_metadata_api_check": &schema.Schema{ 329 Type: schema.TypeString, 330 Optional: true, 331 }, 332 "auth_url": &schema.Schema{ 333 Type: schema.TypeString, 334 Optional: true, 335 }, 336 "container": &schema.Schema{ 337 Type: schema.TypeString, 338 Optional: true, 339 }, 340 "user_name": &schema.Schema{ 341 Type: schema.TypeString, 342 Optional: true, 343 }, 344 "user_id": &schema.Schema{ 345 Type: schema.TypeString, 346 Optional: true, 347 }, 348 "region_name": &schema.Schema{ 349 Type: schema.TypeString, 350 Optional: true, 351 }, 352 "tenant_id": &schema.Schema{ 353 Type: schema.TypeString, 354 Optional: true, 355 }, 356 "tenant_name": &schema.Schema{ 357 Type: schema.TypeString, 358 Optional: true, 359 }, 360 "domain_id": &schema.Schema{ 361 Type: schema.TypeString, 362 Optional: true, 363 }, 364 "domain_name": &schema.Schema{ 365 Type: schema.TypeString, 366 Optional: true, 367 }, 368 "insecure": &schema.Schema{ 369 Type: schema.TypeString, 370 Optional: true, 371 }, 372 "cacert_file": &schema.Schema{ 373 Type: schema.TypeString, 374 Optional: true, 375 }, 376 "cert": &schema.Schema{ 377 Type: schema.TypeString, 378 Optional: true, 379 }, 380 "archive_container": &schema.Schema{ 381 Type: schema.TypeString, 382 Optional: true, 383 }, 384 "archive_path": &schema.Schema{ 385 Type: schema.TypeString, 386 Optional: true, 387 }, 388 "expire_after": &schema.Schema{ 389 Type: schema.TypeString, 390 Optional: true, 391 }, 392 "name": &schema.Schema{ 393 Type: schema.TypeString, 394 Optional: true, 395 }, 396 }, 397 }, 398 }, 399 400 "defaults": { 401 Type: schema.TypeMap, 402 Optional: true, 403 }, 404 405 "environment": { 406 Type: schema.TypeString, 407 Optional: true, 408 Default: backend.DefaultStateName, 409 Deprecated: "Terraform environments are now called workspaces. Please use the workspace key instead.", 410 }, 411 412 "workspace": { 413 Type: schema.TypeString, 414 Optional: true, 415 Default: backend.DefaultStateName, 416 }, 417 418 "__has_dynamic_attributes": { 419 Type: schema.TypeString, 420 Optional: true, 421 }, 422 }, 423 } 424 } 425 426 func dataSourceRemoteStateRead(d *schema.ResourceData, meta interface{}) error { 427 backendType := d.Get("backend").(string) 428 429 // Get the configuration in a type we want. This is a bit of a hack but makes 430 // things work for the 'remote' backend as well. This can simply be deleted or 431 // reverted when merging this 0.12. 432 raw := make(map[string]interface{}) 433 if cfg, ok := d.GetOk("config"); ok { 434 if raw, ok = cfg.(*schema.Set).List()[0].(map[string]interface{}); ok { 435 for k, v := range raw { 436 switch v := v.(type) { 437 case string: 438 if v == "" { 439 delete(raw, k) 440 } 441 case []interface{}: 442 if len(v) == 0 { 443 delete(raw, k) 444 } 445 } 446 } 447 } 448 } 449 450 rawConfig, err := config.NewRawConfig(raw) 451 if err != nil { 452 return fmt.Errorf("error initializing backend: %s", err) 453 } 454 455 // Don't break people using the old _local syntax - but note warning above 456 if backendType == "_local" { 457 log.Println(`[INFO] Switching old (unsupported) backend "_local" to "local"`) 458 backendType = "local" 459 } 460 461 // Create the client to access our remote state 462 log.Printf("[DEBUG] Initializing remote state backend: %s", backendType) 463 f := backendInit.Backend(backendType) 464 if f == nil { 465 return fmt.Errorf("Unknown backend type: %s", backendType) 466 } 467 b := f() 468 469 warns, errs := b.Validate(terraform.NewResourceConfig(rawConfig)) 470 for _, warning := range warns { 471 log.Printf("[DEBUG] Warning validating backend config: %s", warning) 472 } 473 if len(errs) > 0 { 474 return fmt.Errorf("error validating backend config: %s", multierror.Append(nil, errs...)) 475 } 476 477 // Configure the backend 478 if err := b.Configure(terraform.NewResourceConfig(rawConfig)); err != nil { 479 return fmt.Errorf("error initializing backend: %s", err) 480 } 481 482 // environment is deprecated in favour of workspace. 483 // If both keys are set workspace should win. 484 name := d.Get("environment").(string) 485 if ws, ok := d.GetOk("workspace"); ok && ws != backend.DefaultStateName { 486 name = ws.(string) 487 } 488 489 state, err := b.State(name) 490 if err != nil { 491 return fmt.Errorf("error loading the remote state: %s", err) 492 } 493 if err := state.RefreshState(); err != nil { 494 return err 495 } 496 d.SetId(time.Now().UTC().String()) 497 498 outputMap := make(map[string]interface{}) 499 500 defaults := d.Get("defaults").(map[string]interface{}) 501 for key, val := range defaults { 502 outputMap[key] = val 503 } 504 505 remoteState := state.State() 506 if remoteState.Empty() { 507 log.Println("[DEBUG] empty remote state") 508 } else { 509 for key, val := range remoteState.RootModule().Outputs { 510 if val.Value != nil { 511 outputMap[key] = val.Value 512 } 513 } 514 } 515 516 mappedOutputs := remoteStateFlatten(outputMap) 517 518 for key, val := range mappedOutputs { 519 d.UnsafeSetFieldRaw(key, val) 520 } 521 522 return nil 523 }