github.com/pgray/terraform@v0.5.4-0.20170822184730-b6a464c5214d/terraform/interpolate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/hashicorp/hil"
    12  	"github.com/hashicorp/hil/ast"
    13  	"github.com/hashicorp/terraform/config"
    14  	"github.com/hashicorp/terraform/config/module"
    15  	"github.com/hashicorp/terraform/flatmap"
    16  )
    17  
    18  const (
    19  	// VarEnvPrefix is the prefix of variables that are read from
    20  	// the environment to set variables here.
    21  	VarEnvPrefix = "TF_VAR_"
    22  )
    23  
    24  // Interpolater is the structure responsible for determining the values
    25  // for interpolations such as `aws_instance.foo.bar`.
    26  type Interpolater struct {
    27  	Operation          walkOperation
    28  	Meta               *ContextMeta
    29  	Module             *module.Tree
    30  	State              *State
    31  	StateLock          *sync.RWMutex
    32  	VariableValues     map[string]interface{}
    33  	VariableValuesLock *sync.Mutex
    34  }
    35  
    36  // InterpolationScope is the current scope of execution. This is required
    37  // since some variables which are interpolated are dependent on what we're
    38  // operating on and where we are.
    39  type InterpolationScope struct {
    40  	Path     []string
    41  	Resource *Resource
    42  }
    43  
    44  // Values returns the values for all the variables in the given map.
    45  func (i *Interpolater) Values(
    46  	scope *InterpolationScope,
    47  	vars map[string]config.InterpolatedVariable) (map[string]ast.Variable, error) {
    48  	if scope == nil {
    49  		scope = &InterpolationScope{}
    50  	}
    51  
    52  	result := make(map[string]ast.Variable, len(vars))
    53  
    54  	// Copy the default variables
    55  	if i.Module != nil && scope != nil {
    56  		mod := i.Module
    57  		if len(scope.Path) > 1 {
    58  			mod = i.Module.Child(scope.Path[1:])
    59  		}
    60  		for _, v := range mod.Config().Variables {
    61  			// Set default variables
    62  			if v.Default == nil {
    63  				continue
    64  			}
    65  
    66  			n := fmt.Sprintf("var.%s", v.Name)
    67  			variable, err := hil.InterfaceToVariable(v.Default)
    68  			if err != nil {
    69  				return nil, fmt.Errorf("invalid default map value for %s: %v", v.Name, v.Default)
    70  			}
    71  
    72  			result[n] = variable
    73  		}
    74  	}
    75  
    76  	for n, rawV := range vars {
    77  		var err error
    78  		switch v := rawV.(type) {
    79  		case *config.CountVariable:
    80  			err = i.valueCountVar(scope, n, v, result)
    81  		case *config.ModuleVariable:
    82  			err = i.valueModuleVar(scope, n, v, result)
    83  		case *config.PathVariable:
    84  			err = i.valuePathVar(scope, n, v, result)
    85  		case *config.ResourceVariable:
    86  			err = i.valueResourceVar(scope, n, v, result)
    87  		case *config.SelfVariable:
    88  			err = i.valueSelfVar(scope, n, v, result)
    89  		case *config.SimpleVariable:
    90  			err = i.valueSimpleVar(scope, n, v, result)
    91  		case *config.TerraformVariable:
    92  			err = i.valueTerraformVar(scope, n, v, result)
    93  		case *config.LocalVariable:
    94  			err = i.valueLocalVar(scope, n, v, result)
    95  		case *config.UserVariable:
    96  			err = i.valueUserVar(scope, n, v, result)
    97  		default:
    98  			err = fmt.Errorf("%s: unknown variable type: %T", n, rawV)
    99  		}
   100  
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  
   106  	return result, nil
   107  }
   108  
   109  func (i *Interpolater) valueCountVar(
   110  	scope *InterpolationScope,
   111  	n string,
   112  	v *config.CountVariable,
   113  	result map[string]ast.Variable) error {
   114  	switch v.Type {
   115  	case config.CountValueIndex:
   116  		if scope.Resource == nil {
   117  			return fmt.Errorf("%s: count.index is only valid within resources", n)
   118  		}
   119  		result[n] = ast.Variable{
   120  			Value: scope.Resource.CountIndex,
   121  			Type:  ast.TypeInt,
   122  		}
   123  		return nil
   124  	default:
   125  		return fmt.Errorf("%s: unknown count type: %#v", n, v.Type)
   126  	}
   127  }
   128  
   129  func unknownVariable() ast.Variable {
   130  	return ast.Variable{
   131  		Type:  ast.TypeUnknown,
   132  		Value: config.UnknownVariableValue,
   133  	}
   134  }
   135  
   136  func unknownValue() string {
   137  	return hil.UnknownValue
   138  }
   139  
   140  func (i *Interpolater) valueModuleVar(
   141  	scope *InterpolationScope,
   142  	n string,
   143  	v *config.ModuleVariable,
   144  	result map[string]ast.Variable) error {
   145  
   146  	// Build the path to the child module we want
   147  	path := make([]string, len(scope.Path), len(scope.Path)+1)
   148  	copy(path, scope.Path)
   149  	path = append(path, v.Name)
   150  
   151  	// Grab the lock so that if other interpolations are running or
   152  	// state is being modified, we'll be safe.
   153  	i.StateLock.RLock()
   154  	defer i.StateLock.RUnlock()
   155  
   156  	// Get the module where we're looking for the value
   157  	mod := i.State.ModuleByPath(path)
   158  	if mod == nil {
   159  		// If the module doesn't exist, then we can return an empty string.
   160  		// This happens usually only in Refresh() when we haven't populated
   161  		// a state. During validation, we semantically verify that all
   162  		// modules reference other modules, and graph ordering should
   163  		// ensure that the module is in the state, so if we reach this
   164  		// point otherwise it really is a panic.
   165  		result[n] = unknownVariable()
   166  
   167  		// During apply this is always an error
   168  		if i.Operation == walkApply {
   169  			return fmt.Errorf(
   170  				"Couldn't find module %q for var: %s",
   171  				v.Name, v.FullKey())
   172  		}
   173  	} else {
   174  		// Get the value from the outputs
   175  		if outputState, ok := mod.Outputs[v.Field]; ok {
   176  			output, err := hil.InterfaceToVariable(outputState.Value)
   177  			if err != nil {
   178  				return err
   179  			}
   180  			result[n] = output
   181  		} else {
   182  			// Same reasons as the comment above.
   183  			result[n] = unknownVariable()
   184  
   185  			// During apply this is always an error
   186  			if i.Operation == walkApply {
   187  				return fmt.Errorf(
   188  					"Couldn't find output %q for module var: %s",
   189  					v.Field, v.FullKey())
   190  			}
   191  		}
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  func (i *Interpolater) valuePathVar(
   198  	scope *InterpolationScope,
   199  	n string,
   200  	v *config.PathVariable,
   201  	result map[string]ast.Variable) error {
   202  	switch v.Type {
   203  	case config.PathValueCwd:
   204  		wd, err := os.Getwd()
   205  		if err != nil {
   206  			return fmt.Errorf(
   207  				"Couldn't get cwd for var %s: %s",
   208  				v.FullKey(), err)
   209  		}
   210  
   211  		result[n] = ast.Variable{
   212  			Value: wd,
   213  			Type:  ast.TypeString,
   214  		}
   215  	case config.PathValueModule:
   216  		if t := i.Module.Child(scope.Path[1:]); t != nil {
   217  			result[n] = ast.Variable{
   218  				Value: t.Config().Dir,
   219  				Type:  ast.TypeString,
   220  			}
   221  		}
   222  	case config.PathValueRoot:
   223  		result[n] = ast.Variable{
   224  			Value: i.Module.Config().Dir,
   225  			Type:  ast.TypeString,
   226  		}
   227  	default:
   228  		return fmt.Errorf("%s: unknown path type: %#v", n, v.Type)
   229  	}
   230  
   231  	return nil
   232  
   233  }
   234  
   235  func (i *Interpolater) valueResourceVar(
   236  	scope *InterpolationScope,
   237  	n string,
   238  	v *config.ResourceVariable,
   239  	result map[string]ast.Variable) error {
   240  	// If we're computing all dynamic fields, then module vars count
   241  	// and we mark it as computed.
   242  	if i.Operation == walkValidate {
   243  		result[n] = unknownVariable()
   244  		return nil
   245  	}
   246  
   247  	var variable *ast.Variable
   248  	var err error
   249  
   250  	if v.Multi && v.Index == -1 {
   251  		variable, err = i.computeResourceMultiVariable(scope, v)
   252  	} else {
   253  		variable, err = i.computeResourceVariable(scope, v)
   254  	}
   255  
   256  	if err != nil {
   257  		return err
   258  	}
   259  
   260  	if variable == nil {
   261  		// During the input walk we tolerate missing variables because
   262  		// we haven't yet had a chance to refresh state, so dynamic data may
   263  		// not yet be complete.
   264  		// If it truly is missing, we'll catch it on a later walk.
   265  		// This applies only to graph nodes that interpolate during the
   266  		// config walk, e.g. providers.
   267  		if i.Operation == walkInput || i.Operation == walkRefresh {
   268  			result[n] = unknownVariable()
   269  			return nil
   270  		}
   271  
   272  		return fmt.Errorf("variable %q is nil, but no error was reported", v.Name)
   273  	}
   274  
   275  	result[n] = *variable
   276  	return nil
   277  }
   278  
   279  func (i *Interpolater) valueSelfVar(
   280  	scope *InterpolationScope,
   281  	n string,
   282  	v *config.SelfVariable,
   283  	result map[string]ast.Variable) error {
   284  	if scope == nil || scope.Resource == nil {
   285  		return fmt.Errorf(
   286  			"%s: invalid scope, self variables are only valid on resources", n)
   287  	}
   288  
   289  	rv, err := config.NewResourceVariable(fmt.Sprintf(
   290  		"%s.%s.%d.%s",
   291  		scope.Resource.Type,
   292  		scope.Resource.Name,
   293  		scope.Resource.CountIndex,
   294  		v.Field))
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	return i.valueResourceVar(scope, n, rv, result)
   300  }
   301  
   302  func (i *Interpolater) valueSimpleVar(
   303  	scope *InterpolationScope,
   304  	n string,
   305  	v *config.SimpleVariable,
   306  	result map[string]ast.Variable) error {
   307  	// This error message includes some information for people who
   308  	// relied on this for their template_file data sources. We should
   309  	// remove this at some point but there isn't any rush.
   310  	return fmt.Errorf(
   311  		"invalid variable syntax: %q. Did you mean 'var.%s'? If this is part of inline `template` parameter\n"+
   312  			"then you must escape the interpolation with two dollar signs. For\n"+
   313  			"example: ${a} becomes $${a}.",
   314  		n, n)
   315  }
   316  
   317  func (i *Interpolater) valueTerraformVar(
   318  	scope *InterpolationScope,
   319  	n string,
   320  	v *config.TerraformVariable,
   321  	result map[string]ast.Variable) error {
   322  
   323  	// "env" is supported for backward compatibility, but it's deprecated and
   324  	// so we won't advertise it as being allowed in the error message. It will
   325  	// be removed in a future version of Terraform.
   326  	if v.Field != "workspace" && v.Field != "env" {
   327  		return fmt.Errorf(
   328  			"%s: only supported key for 'terraform.X' interpolations is 'workspace'", n)
   329  	}
   330  
   331  	if i.Meta == nil {
   332  		return fmt.Errorf(
   333  			"%s: internal error: nil Meta. Please report a bug.", n)
   334  	}
   335  
   336  	result[n] = ast.Variable{Type: ast.TypeString, Value: i.Meta.Env}
   337  	return nil
   338  }
   339  
   340  func (i *Interpolater) valueLocalVar(
   341  	scope *InterpolationScope,
   342  	n string,
   343  	v *config.LocalVariable,
   344  	result map[string]ast.Variable,
   345  ) error {
   346  	i.StateLock.RLock()
   347  	defer i.StateLock.RUnlock()
   348  
   349  	modTree := i.Module
   350  	if len(scope.Path) > 1 {
   351  		modTree = i.Module.Child(scope.Path[1:])
   352  	}
   353  
   354  	// Get the resource from the configuration so we can verify
   355  	// that the resource is in the configuration and so we can access
   356  	// the configuration if we need to.
   357  	var cl *config.Local
   358  	for _, l := range modTree.Config().Locals {
   359  		if l.Name == v.Name {
   360  			cl = l
   361  			break
   362  		}
   363  	}
   364  
   365  	if cl == nil {
   366  		return fmt.Errorf("%s: no local value of this name has been declared", n)
   367  	}
   368  
   369  	// Get the relevant module
   370  	module := i.State.ModuleByPath(scope.Path)
   371  	if module == nil {
   372  		result[n] = unknownVariable()
   373  		return nil
   374  	}
   375  
   376  	rawV, exists := module.Locals[v.Name]
   377  	if !exists {
   378  		result[n] = unknownVariable()
   379  		return nil
   380  	}
   381  
   382  	varV, err := hil.InterfaceToVariable(rawV)
   383  	if err != nil {
   384  		// Should never happen, since interpolation should always produce
   385  		// something we can feed back in to interpolation.
   386  		return fmt.Errorf("%s: %s", n, err)
   387  	}
   388  
   389  	result[n] = varV
   390  	return nil
   391  }
   392  
   393  func (i *Interpolater) valueUserVar(
   394  	scope *InterpolationScope,
   395  	n string,
   396  	v *config.UserVariable,
   397  	result map[string]ast.Variable) error {
   398  	i.VariableValuesLock.Lock()
   399  	defer i.VariableValuesLock.Unlock()
   400  	val, ok := i.VariableValues[v.Name]
   401  	if ok {
   402  		varValue, err := hil.InterfaceToVariable(val)
   403  		if err != nil {
   404  			return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s",
   405  				v.Name, val, err)
   406  		}
   407  		result[n] = varValue
   408  		return nil
   409  	}
   410  
   411  	if _, ok := result[n]; !ok && i.Operation == walkValidate {
   412  		result[n] = unknownVariable()
   413  		return nil
   414  	}
   415  
   416  	// Look up if we have any variables with this prefix because
   417  	// those are map overrides. Include those.
   418  	for k, val := range i.VariableValues {
   419  		if strings.HasPrefix(k, v.Name+".") {
   420  			keyComponents := strings.Split(k, ".")
   421  			overrideKey := keyComponents[len(keyComponents)-1]
   422  
   423  			mapInterface, ok := result["var."+v.Name]
   424  			if !ok {
   425  				return fmt.Errorf("override for non-existent variable: %s", v.Name)
   426  			}
   427  
   428  			mapVariable := mapInterface.Value.(map[string]ast.Variable)
   429  
   430  			varValue, err := hil.InterfaceToVariable(val)
   431  			if err != nil {
   432  				return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s",
   433  					v.Name, val, err)
   434  			}
   435  			mapVariable[overrideKey] = varValue
   436  		}
   437  	}
   438  
   439  	return nil
   440  }
   441  
   442  func (i *Interpolater) computeResourceVariable(
   443  	scope *InterpolationScope,
   444  	v *config.ResourceVariable) (*ast.Variable, error) {
   445  	id := v.ResourceId()
   446  	if v.Multi {
   447  		id = fmt.Sprintf("%s.%d", id, v.Index)
   448  	}
   449  
   450  	i.StateLock.RLock()
   451  	defer i.StateLock.RUnlock()
   452  
   453  	unknownVariable := unknownVariable()
   454  
   455  	// These variables must be declared early because of the use of GOTO
   456  	var isList bool
   457  	var isMap bool
   458  
   459  	// Get the information about this resource variable, and verify
   460  	// that it exists and such.
   461  	module, cr, err := i.resourceVariableInfo(scope, v)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  
   466  	// If we're requesting "count" its a special variable that we grab
   467  	// directly from the config itself.
   468  	if v.Field == "count" {
   469  		var count int
   470  		if cr != nil {
   471  			count, err = cr.Count()
   472  		} else {
   473  			count, err = i.resourceCountMax(module, cr, v)
   474  		}
   475  		if err != nil {
   476  			return nil, fmt.Errorf(
   477  				"Error reading %s count: %s",
   478  				v.ResourceId(),
   479  				err)
   480  		}
   481  
   482  		return &ast.Variable{Type: ast.TypeInt, Value: count}, nil
   483  	}
   484  
   485  	// Get the resource out from the state. We know the state exists
   486  	// at this point and if there is a state, we expect there to be a
   487  	// resource with the given name.
   488  	var r *ResourceState
   489  	if module != nil && len(module.Resources) > 0 {
   490  		var ok bool
   491  		r, ok = module.Resources[id]
   492  		if !ok && v.Multi && v.Index == 0 {
   493  			r, ok = module.Resources[v.ResourceId()]
   494  		}
   495  		if !ok {
   496  			r = nil
   497  		}
   498  	}
   499  	if r == nil || r.Primary == nil {
   500  		if i.Operation == walkApply || i.Operation == walkPlan {
   501  			return nil, fmt.Errorf(
   502  				"Resource '%s' not found for variable '%s'",
   503  				v.ResourceId(),
   504  				v.FullKey())
   505  		}
   506  
   507  		// If we have no module in the state yet or count, return empty.
   508  		// NOTE(@mitchellh): I actually don't know why this is here. During
   509  		// a refactor I kept this here to maintain the same behavior, but
   510  		// I'm not sure why its here.
   511  		if module == nil || len(module.Resources) == 0 {
   512  			return nil, nil
   513  		}
   514  
   515  		goto MISSING
   516  	}
   517  
   518  	if attr, ok := r.Primary.Attributes[v.Field]; ok {
   519  		v, err := hil.InterfaceToVariable(attr)
   520  		return &v, err
   521  	}
   522  
   523  	// computed list or map attribute
   524  	_, isList = r.Primary.Attributes[v.Field+".#"]
   525  	_, isMap = r.Primary.Attributes[v.Field+".%"]
   526  	if isList || isMap {
   527  		variable, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes)
   528  		return &variable, err
   529  	}
   530  
   531  	// At apply time, we can't do the "maybe has it" check below
   532  	// that we need for plans since parent elements might be computed.
   533  	// Therefore, it is an error and we're missing the key.
   534  	//
   535  	// TODO: test by creating a state and configuration that is referencing
   536  	// a non-existent variable "foo.bar" where the state only has "foo"
   537  	// and verify plan works, but apply doesn't.
   538  	if i.Operation == walkApply || i.Operation == walkDestroy {
   539  		goto MISSING
   540  	}
   541  
   542  	// We didn't find the exact field, so lets separate the dots
   543  	// and see if anything along the way is a computed set. i.e. if
   544  	// we have "foo.0.bar" as the field, check to see if "foo" is
   545  	// a computed list. If so, then the whole thing is computed.
   546  	if parts := strings.Split(v.Field, "."); len(parts) > 1 {
   547  		for i := 1; i < len(parts); i++ {
   548  			// Lists and sets make this
   549  			key := fmt.Sprintf("%s.#", strings.Join(parts[:i], "."))
   550  			if attr, ok := r.Primary.Attributes[key]; ok {
   551  				v, err := hil.InterfaceToVariable(attr)
   552  				return &v, err
   553  			}
   554  
   555  			// Maps make this
   556  			key = fmt.Sprintf("%s", strings.Join(parts[:i], "."))
   557  			if attr, ok := r.Primary.Attributes[key]; ok {
   558  				v, err := hil.InterfaceToVariable(attr)
   559  				return &v, err
   560  			}
   561  		}
   562  	}
   563  
   564  MISSING:
   565  	// Validation for missing interpolations should happen at a higher
   566  	// semantic level. If we reached this point and don't have variables,
   567  	// just return the computed value.
   568  	if scope == nil && scope.Resource == nil {
   569  		return &unknownVariable, nil
   570  	}
   571  
   572  	// If the operation is refresh, it isn't an error for a value to
   573  	// be unknown. Instead, we return that the value is computed so
   574  	// that the graph can continue to refresh other nodes. It doesn't
   575  	// matter because the config isn't interpolated anyways.
   576  	//
   577  	// For a Destroy, we're also fine with computed values, since our goal is
   578  	// only to get destroy nodes for existing resources.
   579  	//
   580  	// For an input walk, computed values are okay to return because we're only
   581  	// looking for missing variables to prompt the user for.
   582  	if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkInput {
   583  		return &unknownVariable, nil
   584  	}
   585  
   586  	return nil, fmt.Errorf(
   587  		"Resource '%s' does not have attribute '%s' "+
   588  			"for variable '%s'",
   589  		id,
   590  		v.Field,
   591  		v.FullKey())
   592  }
   593  
   594  func (i *Interpolater) computeResourceMultiVariable(
   595  	scope *InterpolationScope,
   596  	v *config.ResourceVariable) (*ast.Variable, error) {
   597  	i.StateLock.RLock()
   598  	defer i.StateLock.RUnlock()
   599  
   600  	unknownVariable := unknownVariable()
   601  
   602  	// If we're only looking for input, we don't need to expand a
   603  	// multi-variable. This prevents us from encountering things that should be
   604  	// known but aren't because the state has yet to be refreshed.
   605  	if i.Operation == walkInput {
   606  		return &unknownVariable, nil
   607  	}
   608  
   609  	// Get the information about this resource variable, and verify
   610  	// that it exists and such.
   611  	module, cr, err := i.resourceVariableInfo(scope, v)
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  
   616  	// Get the keys for all the resources that are created for this resource
   617  	countMax, err := i.resourceCountMax(module, cr, v)
   618  	if err != nil {
   619  		return nil, err
   620  	}
   621  
   622  	// If count is zero, we return an empty list
   623  	if countMax == 0 {
   624  		return &ast.Variable{Type: ast.TypeList, Value: []ast.Variable{}}, nil
   625  	}
   626  
   627  	// If we have no module in the state yet or count, return unknown
   628  	if module == nil || len(module.Resources) == 0 {
   629  		return &unknownVariable, nil
   630  	}
   631  
   632  	var values []interface{}
   633  	for idx := 0; idx < countMax; idx++ {
   634  		id := fmt.Sprintf("%s.%d", v.ResourceId(), idx)
   635  
   636  		// ID doesn't have a trailing index. We try both here, but if a value
   637  		// without a trailing index is found we prefer that. This choice
   638  		// is for legacy reasons: older versions of TF preferred it.
   639  		if id == v.ResourceId()+".0" {
   640  			potential := v.ResourceId()
   641  			if _, ok := module.Resources[potential]; ok {
   642  				id = potential
   643  			}
   644  		}
   645  
   646  		r, ok := module.Resources[id]
   647  		if !ok {
   648  			continue
   649  		}
   650  
   651  		if r.Primary == nil {
   652  			continue
   653  		}
   654  
   655  		if singleAttr, ok := r.Primary.Attributes[v.Field]; ok {
   656  			values = append(values, singleAttr)
   657  			continue
   658  		}
   659  
   660  		// computed list or map attribute
   661  		_, isList := r.Primary.Attributes[v.Field+".#"]
   662  		_, isMap := r.Primary.Attributes[v.Field+".%"]
   663  		if !(isList || isMap) {
   664  			continue
   665  		}
   666  		multiAttr, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes)
   667  		if err != nil {
   668  			return nil, err
   669  		}
   670  
   671  		values = append(values, multiAttr)
   672  	}
   673  
   674  	if len(values) == 0 {
   675  		// If the operation is refresh, it isn't an error for a value to
   676  		// be unknown. Instead, we return that the value is computed so
   677  		// that the graph can continue to refresh other nodes. It doesn't
   678  		// matter because the config isn't interpolated anyways.
   679  		//
   680  		// For a Destroy, we're also fine with computed values, since our goal is
   681  		// only to get destroy nodes for existing resources.
   682  		//
   683  		// For an input walk, computed values are okay to return because we're only
   684  		// looking for missing variables to prompt the user for.
   685  		if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput {
   686  			return &unknownVariable, nil
   687  		}
   688  
   689  		return nil, fmt.Errorf(
   690  			"Resource '%s' does not have attribute '%s' "+
   691  				"for variable '%s'",
   692  			v.ResourceId(),
   693  			v.Field,
   694  			v.FullKey())
   695  	}
   696  
   697  	variable, err := hil.InterfaceToVariable(values)
   698  	return &variable, err
   699  }
   700  
   701  func (i *Interpolater) interpolateComplexTypeAttribute(
   702  	resourceID string,
   703  	attributes map[string]string) (ast.Variable, error) {
   704  
   705  	// We can now distinguish between lists and maps in state by the count field:
   706  	//    - lists (and by extension, sets) use the traditional .# notation
   707  	//    - maps use the newer .% notation
   708  	// Consequently here we can decide how to deal with the keys appropriately
   709  	// based on whether the type is a map of list.
   710  	if lengthAttr, isList := attributes[resourceID+".#"]; isList {
   711  		log.Printf("[DEBUG] Interpolating computed list element attribute %s (%s)",
   712  			resourceID, lengthAttr)
   713  
   714  		// In Terraform's internal dotted representation of list-like attributes, the
   715  		// ".#" count field is marked as unknown to indicate "this whole list is
   716  		// unknown". We must honor that meaning here so computed references can be
   717  		// treated properly during the plan phase.
   718  		if lengthAttr == config.UnknownVariableValue {
   719  			return unknownVariable(), nil
   720  		}
   721  
   722  		expanded := flatmap.Expand(attributes, resourceID)
   723  		return hil.InterfaceToVariable(expanded)
   724  	}
   725  
   726  	if lengthAttr, isMap := attributes[resourceID+".%"]; isMap {
   727  		log.Printf("[DEBUG] Interpolating computed map element attribute %s (%s)",
   728  			resourceID, lengthAttr)
   729  
   730  		// In Terraform's internal dotted representation of map attributes, the
   731  		// ".%" count field is marked as unknown to indicate "this whole list is
   732  		// unknown". We must honor that meaning here so computed references can be
   733  		// treated properly during the plan phase.
   734  		if lengthAttr == config.UnknownVariableValue {
   735  			return unknownVariable(), nil
   736  		}
   737  
   738  		expanded := flatmap.Expand(attributes, resourceID)
   739  		return hil.InterfaceToVariable(expanded)
   740  	}
   741  
   742  	return ast.Variable{}, fmt.Errorf("No complex type %s found", resourceID)
   743  }
   744  
   745  func (i *Interpolater) resourceVariableInfo(
   746  	scope *InterpolationScope,
   747  	v *config.ResourceVariable) (*ModuleState, *config.Resource, error) {
   748  	// Get the module tree that contains our current path. This is
   749  	// either the current module (path is empty) or a child.
   750  	modTree := i.Module
   751  	if len(scope.Path) > 1 {
   752  		modTree = i.Module.Child(scope.Path[1:])
   753  	}
   754  
   755  	// Get the resource from the configuration so we can verify
   756  	// that the resource is in the configuration and so we can access
   757  	// the configuration if we need to.
   758  	var cr *config.Resource
   759  	for _, r := range modTree.Config().Resources {
   760  		if r.Id() == v.ResourceId() {
   761  			cr = r
   762  			break
   763  		}
   764  	}
   765  
   766  	// Get the relevant module
   767  	module := i.State.ModuleByPath(scope.Path)
   768  	return module, cr, nil
   769  }
   770  
   771  func (i *Interpolater) resourceCountMax(
   772  	ms *ModuleState,
   773  	cr *config.Resource,
   774  	v *config.ResourceVariable) (int, error) {
   775  	id := v.ResourceId()
   776  
   777  	// If we're NOT applying, then we assume we can read the count
   778  	// from the state. Plan and so on may not have any state yet so
   779  	// we do a full interpolation.
   780  	if i.Operation != walkApply {
   781  		if cr == nil {
   782  			return 0, nil
   783  		}
   784  
   785  		count, err := cr.Count()
   786  		if err != nil {
   787  			return 0, err
   788  		}
   789  
   790  		return count, nil
   791  	}
   792  
   793  	// If we have no module state in the apply walk, that suggests we've hit
   794  	// a rather awkward edge-case: the resource this variable refers to
   795  	// has count = 0 and is the only resource processed so far on this walk,
   796  	// and so we've ended up not creating any resource states yet. We don't
   797  	// create a module state until the first resource is written into it,
   798  	// so the module state doesn't exist when we get here.
   799  	//
   800  	// In this case we act as we would if we had been passed a module
   801  	// with an empty resource state map.
   802  	if ms == nil {
   803  		return 0, nil
   804  	}
   805  
   806  	// We need to determine the list of resource keys to get values from.
   807  	// This needs to be sorted so the order is deterministic. We used to
   808  	// use "cr.Count()" but that doesn't work if the count is interpolated
   809  	// and we can't guarantee that so we instead depend on the state.
   810  	max := -1
   811  	for k, _ := range ms.Resources {
   812  		// Get the index number for this resource
   813  		index := ""
   814  		if k == id {
   815  			// If the key is the id, then its just 0 (no explicit index)
   816  			index = "0"
   817  		} else if strings.HasPrefix(k, id+".") {
   818  			// Grab the index number out of the state
   819  			index = k[len(id+"."):]
   820  			if idx := strings.IndexRune(index, '.'); idx >= 0 {
   821  				index = index[:idx]
   822  			}
   823  		}
   824  
   825  		// If there was no index then this resource didn't match
   826  		// the one we're looking for, exit.
   827  		if index == "" {
   828  			continue
   829  		}
   830  
   831  		// Turn the index into an int
   832  		raw, err := strconv.ParseInt(index, 0, 0)
   833  		if err != nil {
   834  			return 0, fmt.Errorf(
   835  				"%s: error parsing index %q as int: %s",
   836  				id, index, err)
   837  		}
   838  
   839  		// Keep track of this index if its the max
   840  		if new := int(raw); new > max {
   841  			max = new
   842  		}
   843  	}
   844  
   845  	// If we never found any matching resources in the state, we
   846  	// have zero.
   847  	if max == -1 {
   848  		return 0, nil
   849  	}
   850  
   851  	// The result value is "max+1" because we're returning the
   852  	// max COUNT, not the max INDEX, and we zero-index.
   853  	return max + 1, nil
   854  }