func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
	list = list.Children()
	if len(list.Items) == 0 {
		return nil, nil
	}
	var result []*Resource
	for _, item := range list.Items {
		if len(item.Keys) != 2 {
			return nil, fmt.Errorf(
				"position %s: 'data' must be followed by exactly two strings: a type and a name",
				item.Pos())
		}
		t := item.Keys[0].Token.Value().(string)
		k := item.Keys[1].Token.Value().(string)
		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
		}
		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, fmt.Errorf(
				"Error reading config for %s[%s]: %s",
				t,
				k,
				err)
		}
		delete(config, "depends_on")
		delete(config, "provider")
		delete(config, "count")
		rawConfig, err := NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading config for %s[%s]: %s",
				t,
				k,
				err)
		}
		var count string = "1"
		if o := listVal.Filter("count"); len(o.Items) > 0 {
			err = hcl.DecodeObject(&count, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error parsing count for %s[%s]: %s",
					t,
					k,
					err)
			}
		}
		countConfig, err := NewRawConfig(map[string]interface{}{
			"count": count,
		})
		if err != nil {
			return nil, err
		}
		countConfig.Key = "count"
		var dependsOn []string
		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error reading depends_on for %s[%s]: %s",
					t,
					k,
					err)
			}
		}
		var provider string
		if o := listVal.Filter("provider"); len(o.Items) > 0 {
			err := hcl.DecodeObject(&provider, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error reading provider for %s[%s]: %s",
					t,
					k,
					err)
			}
		}
		result = append(result, &Resource{
			Mode:         DataResourceMode,
			Name:         k,
			Type:         t,
			RawCount:     countConfig,
			RawConfig:    rawConfig,
			Provider:     provider,
			Provisioners: []*Provisioner{},
			DependsOn:    dependsOn,
			Lifecycle:    ResourceLifecycle{},
		})
	}
	return result, nil
}
Given a handle to a HCL object, this recurses into the structure
and pulls out a list of data sources.

The resulting data sources may not be unique, but each one
represents exactly one data definition in the HCL configuration.
We leave it up to another pass to merge them together.
package config
import (
	"fmt"
	"io/ioutil"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/mitchellh/mapstructure"
)
type hclConfigurable struct {
	File string
	Root *ast.File
}
func (t *hclConfigurable) Config() (*Config, error) {
	validKeys := map[string]struct{}{
		"atlas":     struct{}{},
		"data":      struct{}{},
		"module":    struct{}{},
		"output":    struct{}{},
		"provider":  struct{}{},
		"resource":  struct{}{},
		"terraform": struct{}{},
		"variable":  struct{}{},
	}
	list, ok := t.Root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
	}
	config := new(Config)
	if o := list.Filter("terraform"); len(o.Items) > 0 {
		var err error
		config.Terraform, err = loadTerraformHcl(o)
		if err != nil {
			return nil, err
		}
	}
	if vars := list.Filter("variable"); len(vars.Items) > 0 {
		var err error
		config.Variables, err = loadVariablesHcl(vars)
		if err != nil {
			return nil, err
		}
	}
	if atlas := list.Filter("atlas"); len(atlas.Items) > 0 {
		var err error
		config.Atlas, err = loadAtlasHcl(atlas)
		if err != nil {
			return nil, err
		}
	}
	if modules := list.Filter("module"); len(modules.Items) > 0 {
		var err error
		config.Modules, err = loadModulesHcl(modules)
		if err != nil {
			return nil, err
		}
	}
	if providers := list.Filter("provider"); len(providers.Items) > 0 {
		var err error
		config.ProviderConfigs, err = loadProvidersHcl(providers)
		if err != nil {
			return nil, err
		}
	}
	{
		var err error
		managedResourceConfigs := list.Filter("resource")
		dataResourceConfigs := list.Filter("data")
		config.Resources = make(
			[]*Resource, 0,
			len(managedResourceConfigs.Items)+len(dataResourceConfigs.Items),
		)
		managedResources, err := loadManagedResourcesHcl(managedResourceConfigs)
		if err != nil {
			return nil, err
		}
		dataResources, err := loadDataResourcesHcl(dataResourceConfigs)
		if err != nil {
			return nil, err
		}
		config.Resources = append(config.Resources, dataResources...)
		config.Resources = append(config.Resources, managedResources...)
	}
	if outputs := list.Filter("output"); len(outputs.Items) > 0 {
		var err error
		config.Outputs, err = loadOutputsHcl(outputs)
		if err != nil {
			return nil, err
		}
	}
	for _, item := range list.Items {
		if len(item.Keys) == 0 {
			continue
		}
		k := item.Keys[0].Token.Value().(string)
		if _, ok := validKeys[k]; ok {
			continue
		}
		config.unknownKeys = append(config.unknownKeys, k)
	}
	return config, nil
}
func loadFileHcl(root string) (configurable, []string, error) {
	d, err := ioutil.ReadFile(root)
	if err != nil {
		return nil, nil, fmt.Errorf(
			"Error reading %s: %s", root, err)
	}
	hclRoot, err := hcl.Parse(string(d))
	if err != nil {
		return nil, nil, fmt.Errorf(
			"Error parsing %s: %s", root, err)
	}
	result := &hclConfigurable{
		File: root,
		Root: hclRoot,
	}
	return result, nil, nil
}
func loadTerraformHcl(list *ast.ObjectList) (*Terraform, error) {
	if len(list.Items) > 1 {
		return nil, fmt.Errorf("only one 'terraform' block allowed per module")
	}
	item := list.Items[0]
	var listVal *ast.ObjectList
	if ot, ok := item.Val.(*ast.ObjectType); ok {
		listVal = ot.List
	} else {
		return nil, fmt.Errorf("terraform block: should be an object")
	}
	var config Terraform
	if err := hcl.DecodeObject(&config, item.Val); err != nil {
		return nil, fmt.Errorf(
			"Error reading terraform config: %s",
			err)
	}
	if os := listVal.Filter("backend"); len(os.Items) > 0 {
		var err error
		config.Backend, err = loadTerraformBackendHcl(os)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading backend config for terraform block: %s",
				err)
		}
	}
	return &config, nil
}
func loadTerraformBackendHcl(list *ast.ObjectList) (*Backend, error) {
	if len(list.Items) > 1 {
		return nil, fmt.Errorf("only one 'backend' block allowed")
	}
	item := list.Items[0]
	if len(item.Keys) != 1 {
		return nil, fmt.Errorf(
			"position %s: 'backend' must be followed by exactly one string: a type",
			item.Pos())
	}
	typ := item.Keys[0].Token.Value().(string)
	var config map[string]interface{}
	if err := hcl.DecodeObject(&config, item.Val); err != nil {
		return nil, fmt.Errorf(
			"Error reading backend config: %s",
			err)
	}
	rawConfig, err := NewRawConfig(config)
	if err != nil {
		return nil, fmt.Errorf(
			"Error reading backend config: %s",
			err)
	}
	b := &Backend{
		Type:      typ,
		RawConfig: rawConfig,
	}
	b.Hash = b.Rehash()
	return b, nil
}
func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) {
	if len(list.Items) > 1 {
		return nil, fmt.Errorf("only one 'atlas' block allowed")
	}
	item := list.Items[0]
	var config AtlasConfig
	if err := hcl.DecodeObject(&config, item.Val); err != nil {
		return nil, fmt.Errorf(
			"Error reading atlas config: %s",
			err)
	}
	return &config, nil
}
func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
	list = list.Children()
	if len(list.Items) == 0 {
		return nil, nil
	}
	var result []*Module
	for _, item := range list.Items {
		k := item.Keys[0].Token.Value().(string)
		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("module '%s': should be an object", k)
		}
		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, fmt.Errorf(
				"Error reading config for %s: %s",
				k,
				err)
		}
		delete(config, "source")
		rawConfig, err := NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading config for %s: %s",
				k,
				err)
		}
		var source string
		if o := listVal.Filter("source"); len(o.Items) > 0 {
			err = hcl.DecodeObject(&source, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error parsing source for %s: %s",
					k,
					err)
			}
		}
		result = append(result, &Module{
			Name:      k,
			Source:    source,
			RawConfig: rawConfig,
		})
	}
	return result, nil
}
func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
	list = list.Children()
	if len(list.Items) == 0 {
		return nil, fmt.Errorf(
			"'output' must be followed by exactly one string: a name")
	}
	result := make([]*Output, 0, len(list.Items))
	for _, item := range list.Items {
		n := item.Keys[0].Token.Value().(string)
		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("output '%s': should be an object", n)
		}
		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, err
		}
		delete(config, "depends_on")
		rawConfig, err := NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading config for output %s: %s",
				n,
				err)
		}
		var dependsOn []string
		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error reading depends_on for output %q: %s",
					n,
					err)
			}
		}
		result = append(result, &Output{
			Name:      n,
			RawConfig: rawConfig,
			DependsOn: dependsOn,
		})
	}
	return result, nil
}
func loadVariablesHcl(list *ast.ObjectList) ([]*Variable, error) {
	list = list.Children()
	if len(list.Items) == 0 {
		return nil, fmt.Errorf(
			"'variable' must be followed by exactly one strings: a name")
	}
	type hclVariable struct {
		DeclaredType string `hcl:"type"`
		Default      interface{}
		Description  string
		Fields       []string `hcl:",decodedFields"`
	}
	result := make([]*Variable, 0, len(list.Items))
	for _, item := range list.Items {
		unwrapHCLObjectKeysFromJSON(item, 1)
		if len(item.Keys) != 1 {
			return nil, fmt.Errorf(
				"position %s: 'variable' must be followed by exactly one strings: a name",
				item.Pos())
		}
		n := item.Keys[0].Token.Value().(string)
		if !NameRegexp.MatchString(n) {
			return nil, fmt.Errorf(
				"position %s: 'variable' name must match regular expression: %s",
				item.Pos(), NameRegexp)
		}
		valid := []string{"type", "default", "description"}
		if err := checkHCLKeys(item.Val, valid); err != nil {
			return nil, multierror.Prefix(err, fmt.Sprintf(
				"variable[%s]:", n))
		}
		var hclVar hclVariable
		if err := hcl.DecodeObject(&hclVar, item.Val); err != nil {
			return nil, err
		}
		if ms, ok := hclVar.Default.([]map[string]interface{}); ok {
			def := make(map[string]interface{})
			for _, m := range ms {
				for k, v := range m {
					def[k] = v
				}
			}
			hclVar.Default = def
		}
		newVar := &Variable{
			Name:         n,
			DeclaredType: hclVar.DeclaredType,
			Default:      hclVar.Default,
			Description:  hclVar.Description,
		}
		if err := newVar.ValidateTypeAndDefault(); err != nil {
			return nil, err
		}
		result = append(result, newVar)
	}
	return result, nil
}
func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
	list = list.Children()
	if len(list.Items) == 0 {
		return nil, nil
	}
	result := make([]*ProviderConfig, 0, len(list.Items))
	for _, item := range list.Items {
		n := item.Keys[0].Token.Value().(string)
		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("module '%s': should be an object", n)
		}
		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, err
		}
		delete(config, "alias")
		rawConfig, err := NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading config for provider config %s: %s",
				n,
				err)
		}
		var alias string
		if a := listVal.Filter("alias"); len(a.Items) > 0 {
			err := hcl.DecodeObject(&alias, a.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error reading alias for provider[%s]: %s",
					n,
					err)
			}
		}
		result = append(result, &ProviderConfig{
			Name:      n,
			Alias:     alias,
			RawConfig: rawConfig,
		})
	}
	return result, nil
}