func loadLocalsHcl(list *ast.ObjectList) ([]*Local, error) {
	result := make([]*Local, 0, len(list.Items))
	for _, block := range list.Items {
		if len(block.Keys) > 0 {
			return nil, fmt.Errorf(
				"locals block at %s should not have label %q",
				block.Pos(), block.Keys[0].Token.Value(),
			)
		}
		blockObj, ok := block.Val.(*ast.ObjectType)
		if !ok {
			return nil, fmt.Errorf("locals value at %s should be a block", block.Val.Pos())
		}
		for _, item := range blockObj.List.Items {
			if len(item.Keys) != 1 {
				return nil, fmt.Errorf("local declaration at %s may not be a block", item.Val.Pos())
			}
			kv := map[string]interface{}{}
			hcl.DecodeObject(&kv, item)
			for k, v := range kv {
				rawConfig, err := NewRawConfig(map[string]interface{}{
					"value": v,
				})
				if err != nil {
					return nil, fmt.Errorf(
						"error parsing local value %q at %s: %s",
						k, item.Val.Pos(), err,
					)
				}
				result = append(result, &Local{
					Name:      k,
					RawConfig: rawConfig,
				})
			}
		}
	}
	return result, nil
}
loadLocalsHcl recurses into the given HCL object turns it into
a list of locals.
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
}
var ReservedDataSourceFields = []string{
	"connection",
	"count",
	"depends_on",
	"lifecycle",
	"provider",
	"provisioner",
}
var ReservedResourceFields = []string{
	"connection",
	"count",
	"depends_on",
	"id",
	"lifecycle",
	"provider",
	"provisioner",
}
var ReservedProviderFields = []string{
	"alias",
	"version",
}
func (t *hclConfigurable) Config() (*Config, error) {
	validKeys := map[string]struct{}{
		"atlas":     struct{}{},
		"data":      struct{}{},
		"locals":    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 locals := list.Filter("locals"); len(locals.Items) > 0 {
		var err error
		config.Locals, err = loadLocalsHcl(locals)
		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]
	if len(item.Keys) > 0 {
		item = &ast.ObjectItem{
			Val: &ast.ObjectType{
				List: &ast.ObjectList{
					Items: []*ast.ObjectItem{item},
				},
			},
		}
	}
	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) {
	if err := assertAllBlocksHaveNames("module", list); err != nil {
		return nil, err
	}
	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)
		}
		rawConfig, err := NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"Error reading config for %s: %s",
				k,
				err)
		}
		delete(config, "source")
		delete(config, "version")
		delete(config, "providers")
		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)
			}
		}
		var version string
		if o := listVal.Filter("version"); len(o.Items) > 0 {
			err = hcl.DecodeObject(&version, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error parsing version for %s: %s",
					k,
					err)
			}
		}
		var providers map[string]string
		if o := listVal.Filter("providers"); len(o.Items) > 0 {
			err = hcl.DecodeObject(&providers, o.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error parsing providers for %s: %s",
					k,
					err)
			}
		}
		result = append(result, &Module{
			Name:      k,
			Source:    source,
			Version:   version,
			Providers: providers,
			RawConfig: rawConfig,
		})
	}
	return result, nil
}