github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/config/loader_hcl.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	"github.com/hashicorp/go-multierror"
     8  	"github.com/hashicorp/hcl"
     9  	"github.com/hashicorp/hcl/hcl/ast"
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // hclConfigurable is an implementation of configurable that knows
    14  // how to turn HCL configuration into a *Config object.
    15  type hclConfigurable struct {
    16  	File string
    17  	Root *ast.File
    18  }
    19  
    20  func (t *hclConfigurable) Config() (*Config, error) {
    21  	validKeys := map[string]struct{}{
    22  		"atlas":     struct{}{},
    23  		"data":      struct{}{},
    24  		"module":    struct{}{},
    25  		"output":    struct{}{},
    26  		"provider":  struct{}{},
    27  		"resource":  struct{}{},
    28  		"terraform": struct{}{},
    29  		"variable":  struct{}{},
    30  	}
    31  
    32  	// Top-level item should be the object list
    33  	list, ok := t.Root.Node.(*ast.ObjectList)
    34  	if !ok {
    35  		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
    36  	}
    37  
    38  	// Start building up the actual configuration.
    39  	config := new(Config)
    40  
    41  	// Terraform config
    42  	if o := list.Filter("terraform"); len(o.Items) > 0 {
    43  		var err error
    44  		config.Terraform, err = loadTerraformHcl(o)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  	}
    49  
    50  	// Build the variables
    51  	if vars := list.Filter("variable"); len(vars.Items) > 0 {
    52  		var err error
    53  		config.Variables, err = loadVariablesHcl(vars)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  	}
    58  
    59  	// Get Atlas configuration
    60  	if atlas := list.Filter("atlas"); len(atlas.Items) > 0 {
    61  		var err error
    62  		config.Atlas, err = loadAtlasHcl(atlas)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  	}
    67  
    68  	// Build the modules
    69  	if modules := list.Filter("module"); len(modules.Items) > 0 {
    70  		var err error
    71  		config.Modules, err = loadModulesHcl(modules)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  	}
    76  
    77  	// Build the provider configs
    78  	if providers := list.Filter("provider"); len(providers.Items) > 0 {
    79  		var err error
    80  		config.ProviderConfigs, err = loadProvidersHcl(providers)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  	}
    85  
    86  	// Build the resources
    87  	{
    88  		var err error
    89  		managedResourceConfigs := list.Filter("resource")
    90  		dataResourceConfigs := list.Filter("data")
    91  
    92  		config.Resources = make(
    93  			[]*Resource, 0,
    94  			len(managedResourceConfigs.Items)+len(dataResourceConfigs.Items),
    95  		)
    96  
    97  		managedResources, err := loadManagedResourcesHcl(managedResourceConfigs)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		dataResources, err := loadDataResourcesHcl(dataResourceConfigs)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  
   106  		config.Resources = append(config.Resources, dataResources...)
   107  		config.Resources = append(config.Resources, managedResources...)
   108  	}
   109  
   110  	// Build the outputs
   111  	if outputs := list.Filter("output"); len(outputs.Items) > 0 {
   112  		var err error
   113  		config.Outputs, err = loadOutputsHcl(outputs)
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  	}
   118  
   119  	// Check for invalid keys
   120  	for _, item := range list.Items {
   121  		if len(item.Keys) == 0 {
   122  			// Not sure how this would happen, but let's avoid a panic
   123  			continue
   124  		}
   125  
   126  		k := item.Keys[0].Token.Value().(string)
   127  		if _, ok := validKeys[k]; ok {
   128  			continue
   129  		}
   130  
   131  		config.unknownKeys = append(config.unknownKeys, k)
   132  	}
   133  
   134  	return config, nil
   135  }
   136  
   137  // loadFileHcl is a fileLoaderFunc that knows how to read HCL
   138  // files and turn them into hclConfigurables.
   139  func loadFileHcl(root string) (configurable, []string, error) {
   140  	// Read the HCL file and prepare for parsing
   141  	d, err := ioutil.ReadFile(root)
   142  	if err != nil {
   143  		return nil, nil, fmt.Errorf(
   144  			"Error reading %s: %s", root, err)
   145  	}
   146  
   147  	// Parse it
   148  	hclRoot, err := hcl.Parse(string(d))
   149  	if err != nil {
   150  		return nil, nil, fmt.Errorf(
   151  			"Error parsing %s: %s", root, err)
   152  	}
   153  
   154  	// Start building the result
   155  	result := &hclConfigurable{
   156  		File: root,
   157  		Root: hclRoot,
   158  	}
   159  
   160  	// Dive in, find the imports. This is disabled for now since
   161  	// imports were removed prior to Terraform 0.1. The code is
   162  	// remaining here commented for historical purposes.
   163  	/*
   164  		imports := obj.Get("import")
   165  		if imports == nil {
   166  			result.Object.Ref()
   167  			return result, nil, nil
   168  		}
   169  
   170  		if imports.Type() != libucl.ObjectTypeString {
   171  			imports.Close()
   172  
   173  			return nil, nil, fmt.Errorf(
   174  				"Error in %s: all 'import' declarations should be in the format\n"+
   175  					"`import \"foo\"` (Got type %s)",
   176  				root,
   177  				imports.Type())
   178  		}
   179  
   180  		// Gather all the import paths
   181  		importPaths := make([]string, 0, imports.Len())
   182  		iter := imports.Iterate(false)
   183  		for imp := iter.Next(); imp != nil; imp = iter.Next() {
   184  			path := imp.ToString()
   185  			if !filepath.IsAbs(path) {
   186  				// Relative paths are relative to the Terraform file itself
   187  				dir := filepath.Dir(root)
   188  				path = filepath.Join(dir, path)
   189  			}
   190  
   191  			importPaths = append(importPaths, path)
   192  			imp.Close()
   193  		}
   194  		iter.Close()
   195  		imports.Close()
   196  
   197  		result.Object.Ref()
   198  	*/
   199  
   200  	return result, nil, nil
   201  }
   202  
   203  // Given a handle to a HCL object, this transforms it into the Terraform config
   204  func loadTerraformHcl(list *ast.ObjectList) (*Terraform, error) {
   205  	if len(list.Items) > 1 {
   206  		return nil, fmt.Errorf("only one 'terraform' block allowed per module")
   207  	}
   208  
   209  	// Get our one item
   210  	item := list.Items[0]
   211  
   212  	// We need the item value as an ObjectList
   213  	var listVal *ast.ObjectList
   214  	if ot, ok := item.Val.(*ast.ObjectType); ok {
   215  		listVal = ot.List
   216  	} else {
   217  		return nil, fmt.Errorf("terraform block: should be an object")
   218  	}
   219  
   220  	// NOTE: We purposely don't validate unknown HCL keys here so that
   221  	// we can potentially read _future_ Terraform version config (to
   222  	// still be able to validate the required version).
   223  	//
   224  	// We should still keep track of unknown keys to validate later, but
   225  	// HCL doesn't currently support that.
   226  
   227  	var config Terraform
   228  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   229  		return nil, fmt.Errorf(
   230  			"Error reading terraform config: %s",
   231  			err)
   232  	}
   233  
   234  	// If we have provisioners, then parse those out
   235  	if os := listVal.Filter("backend"); len(os.Items) > 0 {
   236  		var err error
   237  		config.Backend, err = loadTerraformBackendHcl(os)
   238  		if err != nil {
   239  			return nil, fmt.Errorf(
   240  				"Error reading backend config for terraform block: %s",
   241  				err)
   242  		}
   243  	}
   244  
   245  	return &config, nil
   246  }
   247  
   248  // Loads the Backend configuration from an object list.
   249  func loadTerraformBackendHcl(list *ast.ObjectList) (*Backend, error) {
   250  	if len(list.Items) > 1 {
   251  		return nil, fmt.Errorf("only one 'backend' block allowed")
   252  	}
   253  
   254  	// Get our one item
   255  	item := list.Items[0]
   256  
   257  	// Verify the keys
   258  	if len(item.Keys) != 1 {
   259  		return nil, fmt.Errorf(
   260  			"position %s: 'backend' must be followed by exactly one string: a type",
   261  			item.Pos())
   262  	}
   263  
   264  	typ := item.Keys[0].Token.Value().(string)
   265  
   266  	// Decode the raw config
   267  	var config map[string]interface{}
   268  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   269  		return nil, fmt.Errorf(
   270  			"Error reading backend config: %s",
   271  			err)
   272  	}
   273  
   274  	rawConfig, err := NewRawConfig(config)
   275  	if err != nil {
   276  		return nil, fmt.Errorf(
   277  			"Error reading backend config: %s",
   278  			err)
   279  	}
   280  
   281  	b := &Backend{
   282  		Type:      typ,
   283  		RawConfig: rawConfig,
   284  	}
   285  	b.Hash = b.Rehash()
   286  
   287  	return b, nil
   288  }
   289  
   290  // Given a handle to a HCL object, this transforms it into the Atlas
   291  // configuration.
   292  func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) {
   293  	if len(list.Items) > 1 {
   294  		return nil, fmt.Errorf("only one 'atlas' block allowed")
   295  	}
   296  
   297  	// Get our one item
   298  	item := list.Items[0]
   299  
   300  	var config AtlasConfig
   301  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   302  		return nil, fmt.Errorf(
   303  			"Error reading atlas config: %s",
   304  			err)
   305  	}
   306  
   307  	return &config, nil
   308  }
   309  
   310  // Given a handle to a HCL object, this recurses into the structure
   311  // and pulls out a list of modules.
   312  //
   313  // The resulting modules may not be unique, but each module
   314  // represents exactly one module definition in the HCL configuration.
   315  // We leave it up to another pass to merge them together.
   316  func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
   317  	list = list.Children()
   318  	if len(list.Items) == 0 {
   319  		return nil, nil
   320  	}
   321  
   322  	// Where all the results will go
   323  	var result []*Module
   324  
   325  	// Now go over all the types and their children in order to get
   326  	// all of the actual resources.
   327  	for _, item := range list.Items {
   328  		k := item.Keys[0].Token.Value().(string)
   329  
   330  		var listVal *ast.ObjectList
   331  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   332  			listVal = ot.List
   333  		} else {
   334  			return nil, fmt.Errorf("module '%s': should be an object", k)
   335  		}
   336  
   337  		var config map[string]interface{}
   338  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   339  			return nil, fmt.Errorf(
   340  				"Error reading config for %s: %s",
   341  				k,
   342  				err)
   343  		}
   344  
   345  		// Remove the fields we handle specially
   346  		delete(config, "source")
   347  
   348  		rawConfig, err := NewRawConfig(config)
   349  		if err != nil {
   350  			return nil, fmt.Errorf(
   351  				"Error reading config for %s: %s",
   352  				k,
   353  				err)
   354  		}
   355  
   356  		// If we have a count, then figure it out
   357  		var source string
   358  		if o := listVal.Filter("source"); len(o.Items) > 0 {
   359  			err = hcl.DecodeObject(&source, o.Items[0].Val)
   360  			if err != nil {
   361  				return nil, fmt.Errorf(
   362  					"Error parsing source for %s: %s",
   363  					k,
   364  					err)
   365  			}
   366  		}
   367  
   368  		result = append(result, &Module{
   369  			Name:      k,
   370  			Source:    source,
   371  			RawConfig: rawConfig,
   372  		})
   373  	}
   374  
   375  	return result, nil
   376  }
   377  
   378  // LoadOutputsHcl recurses into the given HCL object and turns
   379  // it into a mapping of outputs.
   380  func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
   381  	list = list.Children()
   382  	if len(list.Items) == 0 {
   383  		return nil, fmt.Errorf(
   384  			"'output' must be followed by exactly one string: a name")
   385  	}
   386  
   387  	// Go through each object and turn it into an actual result.
   388  	result := make([]*Output, 0, len(list.Items))
   389  	for _, item := range list.Items {
   390  		n := item.Keys[0].Token.Value().(string)
   391  
   392  		var listVal *ast.ObjectList
   393  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   394  			listVal = ot.List
   395  		} else {
   396  			return nil, fmt.Errorf("output '%s': should be an object", n)
   397  		}
   398  
   399  		var config map[string]interface{}
   400  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   401  			return nil, err
   402  		}
   403  
   404  		// Delete special keys
   405  		delete(config, "depends_on")
   406  
   407  		rawConfig, err := NewRawConfig(config)
   408  		if err != nil {
   409  			return nil, fmt.Errorf(
   410  				"Error reading config for output %s: %s",
   411  				n,
   412  				err)
   413  		}
   414  
   415  		// If we have depends fields, then add those in
   416  		var dependsOn []string
   417  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   418  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   419  			if err != nil {
   420  				return nil, fmt.Errorf(
   421  					"Error reading depends_on for output %q: %s",
   422  					n,
   423  					err)
   424  			}
   425  		}
   426  
   427  		result = append(result, &Output{
   428  			Name:      n,
   429  			RawConfig: rawConfig,
   430  			DependsOn: dependsOn,
   431  		})
   432  	}
   433  
   434  	return result, nil
   435  }
   436  
   437  // LoadVariablesHcl recurses into the given HCL object and turns
   438  // it into a list of variables.
   439  func loadVariablesHcl(list *ast.ObjectList) ([]*Variable, error) {
   440  	list = list.Children()
   441  	if len(list.Items) == 0 {
   442  		return nil, fmt.Errorf(
   443  			"'variable' must be followed by exactly one strings: a name")
   444  	}
   445  
   446  	// hclVariable is the structure each variable is decoded into
   447  	type hclVariable struct {
   448  		DeclaredType string `hcl:"type"`
   449  		Default      interface{}
   450  		Description  string
   451  		Fields       []string `hcl:",decodedFields"`
   452  	}
   453  
   454  	// Go through each object and turn it into an actual result.
   455  	result := make([]*Variable, 0, len(list.Items))
   456  	for _, item := range list.Items {
   457  		// Clean up items from JSON
   458  		unwrapHCLObjectKeysFromJSON(item, 1)
   459  
   460  		// Verify the keys
   461  		if len(item.Keys) != 1 {
   462  			return nil, fmt.Errorf(
   463  				"position %s: 'variable' must be followed by exactly one strings: a name",
   464  				item.Pos())
   465  		}
   466  
   467  		n := item.Keys[0].Token.Value().(string)
   468  		if !NameRegexp.MatchString(n) {
   469  			return nil, fmt.Errorf(
   470  				"position %s: 'variable' name must match regular expression: %s",
   471  				item.Pos(), NameRegexp)
   472  		}
   473  
   474  		// Check for invalid keys
   475  		valid := []string{"type", "default", "description"}
   476  		if err := checkHCLKeys(item.Val, valid); err != nil {
   477  			return nil, multierror.Prefix(err, fmt.Sprintf(
   478  				"variable[%s]:", n))
   479  		}
   480  
   481  		// Decode into hclVariable to get typed values
   482  		var hclVar hclVariable
   483  		if err := hcl.DecodeObject(&hclVar, item.Val); err != nil {
   484  			return nil, err
   485  		}
   486  
   487  		// Defaults turn into a slice of map[string]interface{} and
   488  		// we need to make sure to convert that down into the
   489  		// proper type for Config.
   490  		if ms, ok := hclVar.Default.([]map[string]interface{}); ok {
   491  			def := make(map[string]interface{})
   492  			for _, m := range ms {
   493  				for k, v := range m {
   494  					def[k] = v
   495  				}
   496  			}
   497  
   498  			hclVar.Default = def
   499  		}
   500  
   501  		// Build the new variable and do some basic validation
   502  		newVar := &Variable{
   503  			Name:         n,
   504  			DeclaredType: hclVar.DeclaredType,
   505  			Default:      hclVar.Default,
   506  			Description:  hclVar.Description,
   507  		}
   508  		if err := newVar.ValidateTypeAndDefault(); err != nil {
   509  			return nil, err
   510  		}
   511  
   512  		result = append(result, newVar)
   513  	}
   514  
   515  	return result, nil
   516  }
   517  
   518  // LoadProvidersHcl recurses into the given HCL object and turns
   519  // it into a mapping of provider configs.
   520  func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
   521  	list = list.Children()
   522  	if len(list.Items) == 0 {
   523  		return nil, nil
   524  	}
   525  
   526  	// Go through each object and turn it into an actual result.
   527  	result := make([]*ProviderConfig, 0, len(list.Items))
   528  	for _, item := range list.Items {
   529  		n := item.Keys[0].Token.Value().(string)
   530  
   531  		var listVal *ast.ObjectList
   532  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   533  			listVal = ot.List
   534  		} else {
   535  			return nil, fmt.Errorf("module '%s': should be an object", n)
   536  		}
   537  
   538  		var config map[string]interface{}
   539  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   540  			return nil, err
   541  		}
   542  
   543  		delete(config, "alias")
   544  
   545  		rawConfig, err := NewRawConfig(config)
   546  		if err != nil {
   547  			return nil, fmt.Errorf(
   548  				"Error reading config for provider config %s: %s",
   549  				n,
   550  				err)
   551  		}
   552  
   553  		// If we have an alias field, then add those in
   554  		var alias string
   555  		if a := listVal.Filter("alias"); len(a.Items) > 0 {
   556  			err := hcl.DecodeObject(&alias, a.Items[0].Val)
   557  			if err != nil {
   558  				return nil, fmt.Errorf(
   559  					"Error reading alias for provider[%s]: %s",
   560  					n,
   561  					err)
   562  			}
   563  		}
   564  
   565  		result = append(result, &ProviderConfig{
   566  			Name:      n,
   567  			Alias:     alias,
   568  			RawConfig: rawConfig,
   569  		})
   570  	}
   571  
   572  	return result, nil
   573  }
   574  
   575  // Given a handle to a HCL object, this recurses into the structure
   576  // and pulls out a list of data sources.
   577  //
   578  // The resulting data sources may not be unique, but each one
   579  // represents exactly one data definition in the HCL configuration.
   580  // We leave it up to another pass to merge them together.
   581  func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   582  	list = list.Children()
   583  	if len(list.Items) == 0 {
   584  		return nil, nil
   585  	}
   586  
   587  	// Where all the results will go
   588  	var result []*Resource
   589  
   590  	// Now go over all the types and their children in order to get
   591  	// all of the actual resources.
   592  	for _, item := range list.Items {
   593  		if len(item.Keys) != 2 {
   594  			return nil, fmt.Errorf(
   595  				"position %s: 'data' must be followed by exactly two strings: a type and a name",
   596  				item.Pos())
   597  		}
   598  
   599  		t := item.Keys[0].Token.Value().(string)
   600  		k := item.Keys[1].Token.Value().(string)
   601  
   602  		var listVal *ast.ObjectList
   603  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   604  			listVal = ot.List
   605  		} else {
   606  			return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
   607  		}
   608  
   609  		var config map[string]interface{}
   610  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   611  			return nil, fmt.Errorf(
   612  				"Error reading config for %s[%s]: %s",
   613  				t,
   614  				k,
   615  				err)
   616  		}
   617  
   618  		// Remove the fields we handle specially
   619  		delete(config, "depends_on")
   620  		delete(config, "provider")
   621  		delete(config, "count")
   622  
   623  		rawConfig, err := NewRawConfig(config)
   624  		if err != nil {
   625  			return nil, fmt.Errorf(
   626  				"Error reading config for %s[%s]: %s",
   627  				t,
   628  				k,
   629  				err)
   630  		}
   631  
   632  		// If we have a count, then figure it out
   633  		var count string = "1"
   634  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   635  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   636  			if err != nil {
   637  				return nil, fmt.Errorf(
   638  					"Error parsing count for %s[%s]: %s",
   639  					t,
   640  					k,
   641  					err)
   642  			}
   643  		}
   644  		countConfig, err := NewRawConfig(map[string]interface{}{
   645  			"count": count,
   646  		})
   647  		if err != nil {
   648  			return nil, err
   649  		}
   650  		countConfig.Key = "count"
   651  
   652  		// If we have depends fields, then add those in
   653  		var dependsOn []string
   654  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   655  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   656  			if err != nil {
   657  				return nil, fmt.Errorf(
   658  					"Error reading depends_on for %s[%s]: %s",
   659  					t,
   660  					k,
   661  					err)
   662  			}
   663  		}
   664  
   665  		// If we have a provider, then parse it out
   666  		var provider string
   667  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   668  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   669  			if err != nil {
   670  				return nil, fmt.Errorf(
   671  					"Error reading provider for %s[%s]: %s",
   672  					t,
   673  					k,
   674  					err)
   675  			}
   676  		}
   677  
   678  		result = append(result, &Resource{
   679  			Mode:         DataResourceMode,
   680  			Name:         k,
   681  			Type:         t,
   682  			RawCount:     countConfig,
   683  			RawConfig:    rawConfig,
   684  			Provider:     provider,
   685  			Provisioners: []*Provisioner{},
   686  			DependsOn:    dependsOn,
   687  			Lifecycle:    ResourceLifecycle{},
   688  		})
   689  	}
   690  
   691  	return result, nil
   692  }
   693  
   694  // Given a handle to a HCL object, this recurses into the structure
   695  // and pulls out a list of managed resources.
   696  //
   697  // The resulting resources may not be unique, but each resource
   698  // represents exactly one "resource" block in the HCL configuration.
   699  // We leave it up to another pass to merge them together.
   700  func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   701  	list = list.Children()
   702  	if len(list.Items) == 0 {
   703  		return nil, nil
   704  	}
   705  
   706  	// Where all the results will go
   707  	var result []*Resource
   708  
   709  	// Now go over all the types and their children in order to get
   710  	// all of the actual resources.
   711  	for _, item := range list.Items {
   712  		// GH-4385: We detect a pure provisioner resource and give the user
   713  		// an error about how to do it cleanly.
   714  		if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
   715  			return nil, fmt.Errorf(
   716  				"position %s: provisioners in a resource should be wrapped in a list\n\n"+
   717  					"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
   718  				item.Pos())
   719  		}
   720  
   721  		// Fix up JSON input
   722  		unwrapHCLObjectKeysFromJSON(item, 2)
   723  
   724  		if len(item.Keys) != 2 {
   725  			return nil, fmt.Errorf(
   726  				"position %s: resource must be followed by exactly two strings, a type and a name",
   727  				item.Pos())
   728  		}
   729  
   730  		t := item.Keys[0].Token.Value().(string)
   731  		k := item.Keys[1].Token.Value().(string)
   732  
   733  		var listVal *ast.ObjectList
   734  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   735  			listVal = ot.List
   736  		} else {
   737  			return nil, fmt.Errorf("resources %s[%s]: should be an object", t, k)
   738  		}
   739  
   740  		var config map[string]interface{}
   741  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   742  			return nil, fmt.Errorf(
   743  				"Error reading config for %s[%s]: %s",
   744  				t,
   745  				k,
   746  				err)
   747  		}
   748  
   749  		// Remove the fields we handle specially
   750  		delete(config, "connection")
   751  		delete(config, "count")
   752  		delete(config, "depends_on")
   753  		delete(config, "provisioner")
   754  		delete(config, "provider")
   755  		delete(config, "lifecycle")
   756  
   757  		rawConfig, err := NewRawConfig(config)
   758  		if err != nil {
   759  			return nil, fmt.Errorf(
   760  				"Error reading config for %s[%s]: %s",
   761  				t,
   762  				k,
   763  				err)
   764  		}
   765  
   766  		// If we have a count, then figure it out
   767  		var count string = "1"
   768  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   769  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   770  			if err != nil {
   771  				return nil, fmt.Errorf(
   772  					"Error parsing count for %s[%s]: %s",
   773  					t,
   774  					k,
   775  					err)
   776  			}
   777  		}
   778  		countConfig, err := NewRawConfig(map[string]interface{}{
   779  			"count": count,
   780  		})
   781  		if err != nil {
   782  			return nil, err
   783  		}
   784  		countConfig.Key = "count"
   785  
   786  		// If we have depends fields, then add those in
   787  		var dependsOn []string
   788  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   789  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   790  			if err != nil {
   791  				return nil, fmt.Errorf(
   792  					"Error reading depends_on for %s[%s]: %s",
   793  					t,
   794  					k,
   795  					err)
   796  			}
   797  		}
   798  
   799  		// If we have connection info, then parse those out
   800  		var connInfo map[string]interface{}
   801  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   802  			err := hcl.DecodeObject(&connInfo, o.Items[0].Val)
   803  			if err != nil {
   804  				return nil, fmt.Errorf(
   805  					"Error reading connection info for %s[%s]: %s",
   806  					t,
   807  					k,
   808  					err)
   809  			}
   810  		}
   811  
   812  		// If we have provisioners, then parse those out
   813  		var provisioners []*Provisioner
   814  		if os := listVal.Filter("provisioner"); len(os.Items) > 0 {
   815  			var err error
   816  			provisioners, err = loadProvisionersHcl(os, connInfo)
   817  			if err != nil {
   818  				return nil, fmt.Errorf(
   819  					"Error reading provisioners for %s[%s]: %s",
   820  					t,
   821  					k,
   822  					err)
   823  			}
   824  		}
   825  
   826  		// If we have a provider, then parse it out
   827  		var provider string
   828  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   829  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   830  			if err != nil {
   831  				return nil, fmt.Errorf(
   832  					"Error reading provider for %s[%s]: %s",
   833  					t,
   834  					k,
   835  					err)
   836  			}
   837  		}
   838  
   839  		// Check if the resource should be re-created before
   840  		// destroying the existing instance
   841  		var lifecycle ResourceLifecycle
   842  		if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
   843  			if len(o.Items) > 1 {
   844  				return nil, fmt.Errorf(
   845  					"%s[%s]: Multiple lifecycle blocks found, expected one",
   846  					t, k)
   847  			}
   848  
   849  			// Check for invalid keys
   850  			valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
   851  			if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
   852  				return nil, multierror.Prefix(err, fmt.Sprintf(
   853  					"%s[%s]:", t, k))
   854  			}
   855  
   856  			var raw map[string]interface{}
   857  			if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
   858  				return nil, fmt.Errorf(
   859  					"Error parsing lifecycle for %s[%s]: %s",
   860  					t,
   861  					k,
   862  					err)
   863  			}
   864  
   865  			if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil {
   866  				return nil, fmt.Errorf(
   867  					"Error parsing lifecycle for %s[%s]: %s",
   868  					t,
   869  					k,
   870  					err)
   871  			}
   872  		}
   873  
   874  		result = append(result, &Resource{
   875  			Mode:         ManagedResourceMode,
   876  			Name:         k,
   877  			Type:         t,
   878  			RawCount:     countConfig,
   879  			RawConfig:    rawConfig,
   880  			Provisioners: provisioners,
   881  			Provider:     provider,
   882  			DependsOn:    dependsOn,
   883  			Lifecycle:    lifecycle,
   884  		})
   885  	}
   886  
   887  	return result, nil
   888  }
   889  
   890  func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) {
   891  	list = list.Children()
   892  	if len(list.Items) == 0 {
   893  		return nil, nil
   894  	}
   895  
   896  	// Go through each object and turn it into an actual result.
   897  	result := make([]*Provisioner, 0, len(list.Items))
   898  	for _, item := range list.Items {
   899  		n := item.Keys[0].Token.Value().(string)
   900  
   901  		var listVal *ast.ObjectList
   902  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   903  			listVal = ot.List
   904  		} else {
   905  			return nil, fmt.Errorf("provisioner '%s': should be an object", n)
   906  		}
   907  
   908  		var config map[string]interface{}
   909  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   910  			return nil, err
   911  		}
   912  
   913  		// Parse the "when" value
   914  		when := ProvisionerWhenCreate
   915  		if v, ok := config["when"]; ok {
   916  			switch v {
   917  			case "create":
   918  				when = ProvisionerWhenCreate
   919  			case "destroy":
   920  				when = ProvisionerWhenDestroy
   921  			default:
   922  				return nil, fmt.Errorf(
   923  					"position %s: 'provisioner' when must be 'create' or 'destroy'",
   924  					item.Pos())
   925  			}
   926  		}
   927  
   928  		// Parse the "on_failure" value
   929  		onFailure := ProvisionerOnFailureFail
   930  		if v, ok := config["on_failure"]; ok {
   931  			switch v {
   932  			case "continue":
   933  				onFailure = ProvisionerOnFailureContinue
   934  			case "fail":
   935  				onFailure = ProvisionerOnFailureFail
   936  			default:
   937  				return nil, fmt.Errorf(
   938  					"position %s: 'provisioner' on_failure must be 'continue' or 'fail'",
   939  					item.Pos())
   940  			}
   941  		}
   942  
   943  		// Delete fields we special case
   944  		delete(config, "connection")
   945  		delete(config, "when")
   946  		delete(config, "on_failure")
   947  
   948  		rawConfig, err := NewRawConfig(config)
   949  		if err != nil {
   950  			return nil, err
   951  		}
   952  
   953  		// Check if we have a provisioner-level connection
   954  		// block that overrides the resource-level
   955  		var subConnInfo map[string]interface{}
   956  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   957  			err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val)
   958  			if err != nil {
   959  				return nil, err
   960  			}
   961  		}
   962  
   963  		// Inherit from the resource connInfo any keys
   964  		// that are not explicitly overriden.
   965  		if connInfo != nil && subConnInfo != nil {
   966  			for k, v := range connInfo {
   967  				if _, ok := subConnInfo[k]; !ok {
   968  					subConnInfo[k] = v
   969  				}
   970  			}
   971  		} else if subConnInfo == nil {
   972  			subConnInfo = connInfo
   973  		}
   974  
   975  		// Parse the connInfo
   976  		connRaw, err := NewRawConfig(subConnInfo)
   977  		if err != nil {
   978  			return nil, err
   979  		}
   980  
   981  		result = append(result, &Provisioner{
   982  			Type:      n,
   983  			RawConfig: rawConfig,
   984  			ConnInfo:  connRaw,
   985  			When:      when,
   986  			OnFailure: onFailure,
   987  		})
   988  	}
   989  
   990  	return result, nil
   991  }
   992  
   993  /*
   994  func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
   995  	objects := make(map[string][]*hclobj.Object)
   996  
   997  	for _, o := range os.Elem(false) {
   998  		for _, elem := range o.Elem(true) {
   999  			val, ok := objects[elem.Key]
  1000  			if !ok {
  1001  				val = make([]*hclobj.Object, 0, 1)
  1002  			}
  1003  
  1004  			val = append(val, elem)
  1005  			objects[elem.Key] = val
  1006  		}
  1007  	}
  1008  
  1009  	return objects
  1010  }
  1011  */
  1012  
  1013  func checkHCLKeys(node ast.Node, valid []string) error {
  1014  	var list *ast.ObjectList
  1015  	switch n := node.(type) {
  1016  	case *ast.ObjectList:
  1017  		list = n
  1018  	case *ast.ObjectType:
  1019  		list = n.List
  1020  	default:
  1021  		return fmt.Errorf("cannot check HCL keys of type %T", n)
  1022  	}
  1023  
  1024  	validMap := make(map[string]struct{}, len(valid))
  1025  	for _, v := range valid {
  1026  		validMap[v] = struct{}{}
  1027  	}
  1028  
  1029  	var result error
  1030  	for _, item := range list.Items {
  1031  		key := item.Keys[0].Token.Value().(string)
  1032  		if _, ok := validMap[key]; !ok {
  1033  			result = multierror.Append(result, fmt.Errorf(
  1034  				"invalid key: %s", key))
  1035  		}
  1036  	}
  1037  
  1038  	return result
  1039  }
  1040  
  1041  // unwrapHCLObjectKeysFromJSON cleans up an edge case that can occur when
  1042  // parsing JSON as input: if we're parsing JSON then directly nested
  1043  // items will show up as additional "keys".
  1044  //
  1045  // For objects that expect a fixed number of keys, this breaks the
  1046  // decoding process. This function unwraps the object into what it would've
  1047  // looked like if it came directly from HCL by specifying the number of keys
  1048  // you expect.
  1049  //
  1050  // Example:
  1051  //
  1052  // { "foo": { "baz": {} } }
  1053  //
  1054  // Will show up with Keys being: []string{"foo", "baz"}
  1055  // when we really just want the first two. This function will fix this.
  1056  func unwrapHCLObjectKeysFromJSON(item *ast.ObjectItem, depth int) {
  1057  	if len(item.Keys) > depth && item.Keys[0].Token.JSON {
  1058  		for len(item.Keys) > depth {
  1059  			// Pop off the last key
  1060  			n := len(item.Keys)
  1061  			key := item.Keys[n-1]
  1062  			item.Keys[n-1] = nil
  1063  			item.Keys = item.Keys[:n-1]
  1064  
  1065  			// Wrap our value in a list
  1066  			item.Val = &ast.ObjectType{
  1067  				List: &ast.ObjectList{
  1068  					Items: []*ast.ObjectItem{
  1069  						&ast.ObjectItem{
  1070  							Keys: []*ast.ObjectKey{key},
  1071  							Val:  item.Val,
  1072  						},
  1073  					},
  1074  				},
  1075  			}
  1076  		}
  1077  	}
  1078  }