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