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