func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
	if err := assertAllBlocksHaveNames("provider", list); err != nil {
		return nil, err
	}
	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")
		delete(config, "version")
		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)
			}
		}
		var version string
		if a := listVal.Filter("version"); len(a.Items) > 0 {
			err := hcl.DecodeObject(&version, a.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"Error reading version for provider[%s]: %s",
					n,
					err)
			}
		}
		result = append(result, &ProviderConfig{
			Name:      n,
			Alias:     alias,
			Version:   version,
			RawConfig: rawConfig,
		})
	}
	return result, nil
}
LoadProvidersHcl recurses into the given HCL object and turns
it into a mapping of provider configs.
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 ReservedResourceFields = []string{
	"connection",
	"count",
	"depends_on",
	"id",
	"lifecycle",
	"provider",
	"provisioner",
}
var ReservedProviderFields = []string{
	"alias",
	"id",
	"version",
}
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]
	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)
		}
		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) {
	if err := assertAllBlocksHaveNames("output", list); err != nil {
		return nil, err
	}
	list = list.Children()
	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) {
	if err := assertAllBlocksHaveNames("variable", list); err != nil {
		return nil, err
	}
	list = list.Children()
	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
}