]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/transform_provider.go
Merge branch 'fix_read_test' of github.com:alexandreFre/terraform-provider-statuscake
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_provider.go
index b9695d5242b45962682c111eb61a788e1c9f520f..6a4fb47c49c86e877b842811f80eaeda24522af1 100644 (file)
@@ -3,60 +3,201 @@ package terraform
 import (
        "fmt"
        "log"
-       "strings"
 
-       "github.com/hashicorp/go-multierror"
+       "github.com/hashicorp/hcl2/hcl"
+       "github.com/hashicorp/terraform/addrs"
+       "github.com/hashicorp/terraform/configs"
        "github.com/hashicorp/terraform/dag"
+       "github.com/hashicorp/terraform/tfdiags"
 )
 
+func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, config *configs.Config) GraphTransformer {
+       return GraphTransformMulti(
+               // Add providers from the config
+               &ProviderConfigTransformer{
+                       Config:    config,
+                       Providers: providers,
+                       Concrete:  concrete,
+               },
+               // Add any remaining missing providers
+               &MissingProviderTransformer{
+                       Providers: providers,
+                       Concrete:  concrete,
+               },
+               // Connect the providers
+               &ProviderTransformer{
+                       Config: config,
+               },
+               // Remove unused providers and proxies
+               &PruneProviderTransformer{},
+               // Connect provider to their parent provider nodes
+               &ParentProviderTransformer{},
+       )
+}
+
 // GraphNodeProvider is an interface that nodes that can be a provider
-// must implement. The ProviderName returned is the name of the provider
-// they satisfy.
+// must implement.
+//
+// ProviderAddr returns the address of the provider configuration this
+// satisfies, which is relative to the path returned by method Path().
+//
+// Name returns the full name of the provider in the config.
 type GraphNodeProvider interface {
-       ProviderName() string
+       GraphNodeSubPath
+       ProviderAddr() addrs.AbsProviderConfig
+       Name() string
 }
 
 // GraphNodeCloseProvider is an interface that nodes that can be a close
 // provider must implement. The CloseProviderName returned is the name of
 // the provider they satisfy.
 type GraphNodeCloseProvider interface {
-       CloseProviderName() string
+       GraphNodeSubPath
+       CloseProviderAddr() addrs.AbsProviderConfig
 }
 
 // GraphNodeProviderConsumer is an interface that nodes that require
-// a provider must implement. ProvidedBy must return the name of the provider
-// to use.
+// a provider must implement. ProvidedBy must return the address of the provider
+// to use, which will be resolved to a configuration either in the same module
+// or in an ancestor module, with the resulting absolute address passed to
+// SetProvider.
 type GraphNodeProviderConsumer interface {
-       ProvidedBy() []string
+       // ProvidedBy returns the address of the provider configuration the node
+       // refers to. If the returned "exact" value is true, this address will
+       // be taken exactly. If "exact" is false, a provider configuration from
+       // an ancestor module may be selected instead.
+       ProvidedBy() (addr addrs.AbsProviderConfig, exact bool)
+       // Set the resolved provider address for this resource.
+       SetProvider(addrs.AbsProviderConfig)
 }
 
 // ProviderTransformer is a GraphTransformer that maps resources to
 // providers within the graph. This will error if there are any resources
 // that don't map to proper resources.
-type ProviderTransformer struct{}
+type ProviderTransformer struct {
+       Config *configs.Config
+}
 
 func (t *ProviderTransformer) Transform(g *Graph) error {
-       // Go through the other nodes and match them to providers they need
-       var err error
-       m := providerVertexMap(g)
+       // We need to find a provider configuration address for each resource
+       // either directly represented by a node or referenced by a node in
+       // the graph, and then create graph edges from provider to provider user
+       // so that the providers will get initialized first.
+
+       var diags tfdiags.Diagnostics
+
+       // To start, we'll collect the _requested_ provider addresses for each
+       // node, which we'll then resolve (handling provider inheritence, etc) in
+       // the next step.
+       // Our "requested" map is from graph vertices to string representations of
+       // provider config addresses (for deduping) to requests.
+       type ProviderRequest struct {
+               Addr  addrs.AbsProviderConfig
+               Exact bool // If true, inheritence from parent modules is not attempted
+       }
+       requested := map[dag.Vertex]map[string]ProviderRequest{}
+       needConfigured := map[string]addrs.AbsProviderConfig{}
        for _, v := range g.Vertices() {
+
+               // Does the vertex _directly_ use a provider?
                if pv, ok := v.(GraphNodeProviderConsumer); ok {
-                       for _, p := range pv.ProvidedBy() {
-                               target := m[providerMapKey(p, pv)]
-                               if target == nil {
-                                       println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv)))
-                                       err = multierror.Append(err, fmt.Errorf(
-                                               "%s: provider %s couldn't be found",
-                                               dag.VertexName(v), p))
-                                       continue
+                       requested[v] = make(map[string]ProviderRequest)
+
+                       p, exact := pv.ProvidedBy()
+                       if exact {
+                               log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), p)
+                       } else {
+                               log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), p)
+                       }
+
+                       requested[v][p.String()] = ProviderRequest{
+                               Addr:  p,
+                               Exact: exact,
+                       }
+
+                       // Direct references need the provider configured as well as initialized
+                       needConfigured[p.String()] = p
+               }
+       }
+
+       // Now we'll go through all the requested addresses we just collected and
+       // figure out which _actual_ config address each belongs to, after resolving
+       // for provider inheritance and passing.
+       m := providerVertexMap(g)
+       for v, reqs := range requested {
+               for key, req := range reqs {
+                       p := req.Addr
+                       target := m[key]
+
+                       _, ok := v.(GraphNodeSubPath)
+                       if !ok && target == nil {
+                               // No target and no path to traverse up from
+                               diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p))
+                               continue
+                       }
+
+                       if target != nil {
+                               log.Printf("[TRACE] ProviderTransformer: exact match for %s serving %s", p, dag.VertexName(v))
+                       }
+
+                       // if we don't have a provider at this level, walk up the path looking for one,
+                       // unless we were told to be exact.
+                       if target == nil && !req.Exact {
+                               for pp, ok := p.Inherited(); ok; pp, ok = pp.Inherited() {
+                                       key := pp.String()
+                                       target = m[key]
+                                       if target != nil {
+                                               log.Printf("[TRACE] ProviderTransformer: %s uses inherited configuration %s", dag.VertexName(v), pp)
+                                               break
+                                       }
+                                       log.Printf("[TRACE] ProviderTransformer: looking for %s to serve %s", pp, dag.VertexName(v))
                                }
+                       }
+
+                       // If this provider doesn't need to be configured then we can just
+                       // stub it out with an init-only provider node, which will just
+                       // start up the provider and fetch its schema.
+                       if _, exists := needConfigured[key]; target == nil && !exists {
+                               stubAddr := p.ProviderConfig.Absolute(addrs.RootModuleInstance)
+                               stub := &NodeEvalableProvider{
+                                       &NodeAbstractProvider{
+                                               Addr: stubAddr,
+                                       },
+                               }
+                               m[stubAddr.String()] = stub
+                               log.Printf("[TRACE] ProviderTransformer: creating init-only node for %s", stubAddr)
+                               target = stub
+                               g.Add(target)
+                       }
+
+                       if target == nil {
+                               diags = diags.Append(tfdiags.Sourceless(
+                                       tfdiags.Error,
+                                       "Provider configuration not present",
+                                       fmt.Sprintf(
+                                               "To work with %s its original provider configuration at %s is required, but it has been removed. This occurs when a provider configuration is removed while objects created by that provider still exist in the state. Re-add the provider configuration to destroy %s, after which you can remove the provider configuration again.",
+                                               dag.VertexName(v), p, dag.VertexName(v),
+                                       ),
+                               ))
+                               break
+                       }
+
+                       // see if this in  an inherited provider
+                       if p, ok := target.(*graphNodeProxyProvider); ok {
+                               g.Remove(p)
+                               target = p.Target()
+                               key = target.(GraphNodeProvider).ProviderAddr().String()
+                       }
 
-                               g.Connect(dag.BasicEdge(v, target))
+                       log.Printf("[DEBUG] ProviderTransformer: %q (%T) needs %s", dag.VertexName(v), v, dag.VertexName(target))
+                       if pv, ok := v.(GraphNodeProviderConsumer); ok {
+                               pv.SetProvider(target.ProviderAddr())
                        }
+                       g.Connect(dag.BasicEdge(v, target))
                }
        }
 
-       return err
+       return diags.Err()
 }
 
 // CloseProviderTransformer is a GraphTransformer that adds nodes to the
@@ -67,36 +208,33 @@ type CloseProviderTransformer struct{}
 
 func (t *CloseProviderTransformer) Transform(g *Graph) error {
        pm := providerVertexMap(g)
-       cpm := closeProviderVertexMap(g)
+       cpm := make(map[string]*graphNodeCloseProvider)
        var err error
-       for _, v := range g.Vertices() {
-               if pv, ok := v.(GraphNodeProviderConsumer); ok {
-                       for _, p := range pv.ProvidedBy() {
-                               key := p
-                               source := cpm[key]
-
-                               if source == nil {
-                                       // Create a new graphNodeCloseProvider and add it to the graph
-                                       source = &graphNodeCloseProvider{ProviderNameValue: p}
-                                       g.Add(source)
-
-                                       // Close node needs to depend on provider
-                                       provider, ok := pm[key]
-                                       if !ok {
-                                               err = multierror.Append(err, fmt.Errorf(
-                                                       "%s: provider %s couldn't be found for closing",
-                                                       dag.VertexName(v), p))
-                                               continue
-                                       }
-                                       g.Connect(dag.BasicEdge(source, provider))
 
-                                       // Make sure we also add the new graphNodeCloseProvider to the map
-                                       // so we don't create and add any duplicate graphNodeCloseProviders.
-                                       cpm[key] = source
-                               }
+       for _, v := range pm {
+               p := v.(GraphNodeProvider)
+               key := p.ProviderAddr().String()
 
-                               // Close node depends on all nodes provided by the provider
-                               g.Connect(dag.BasicEdge(source, v))
+               // get the close provider of this type if we alread created it
+               closer := cpm[key]
+
+               if closer == nil {
+                       // create a closer for this provider type
+                       closer = &graphNodeCloseProvider{Addr: p.ProviderAddr()}
+                       g.Add(closer)
+                       cpm[key] = closer
+               }
+
+               // Close node depends on the provider itself
+               // this is added unconditionally, so it will connect to all instances
+               // of the provider. Extra edges will be removed by transitive
+               // reduction.
+               g.Connect(dag.BasicEdge(closer, p))
+
+               // connect all the provider's resources to the close node
+               for _, s := range g.UpEdges(p).List() {
+                       if _, ok := s.(GraphNodeProviderConsumer); ok {
+                               g.Connect(dag.BasicEdge(closer, s))
                        }
                }
        }
@@ -104,18 +242,24 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error {
        return err
 }
 
-// MissingProviderTransformer is a GraphTransformer that adds nodes
-// for missing providers into the graph. Specifically, it creates provider
-// configuration nodes for all the providers that we support. These are
-// pruned later during an optimization pass.
+// MissingProviderTransformer is a GraphTransformer that adds to the graph
+// a node for each default provider configuration that is referenced by another
+// node but not already present in the graph.
+//
+// These "default" nodes are always added to the root module, regardless of
+// where they are requested. This is important because our inheritance
+// resolution behavior in ProviderTransformer will then treat these as a
+// last-ditch fallback after walking up the tree, rather than preferring them
+// as it would if they were placed in the same module as the requester.
+//
+// This transformer may create extra nodes that are not needed in practice,
+// due to overriding provider configurations in child modules.
+// PruneProviderTransformer can then remove these once ProviderTransformer
+// has resolved all of the inheritence, etc.
 type MissingProviderTransformer struct {
        // Providers is the list of providers we support.
        Providers []string
 
-       // AllowAny will not check that a provider is supported before adding
-       // it to the graph.
-       AllowAny bool
-
        // Concrete, if set, overrides how the providers are made.
        Concrete ConcreteProviderNodeFunc
 }
@@ -128,191 +272,137 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error {
                }
        }
 
-       // Create a set of our supported providers
-       supported := make(map[string]struct{}, len(t.Providers))
-       for _, v := range t.Providers {
-               supported[v] = struct{}{}
-       }
-
-       // Get the map of providers we already have in our graph
+       var err error
        m := providerVertexMap(g)
-
-       // Go through all the provider consumers and make sure we add
-       // that provider if it is missing. We use a for loop here instead
-       // of "range" since we'll modify check as we go to add more to check.
-       check := g.Vertices()
-       for i := 0; i < len(check); i++ {
-               v := check[i]
-
+       for _, v := range g.Vertices() {
                pv, ok := v.(GraphNodeProviderConsumer)
                if !ok {
                        continue
                }
 
-               // If this node has a subpath, then we use that as a prefix
-               // into our map to check for an existing provider.
-               var path []string
-               if sp, ok := pv.(GraphNodeSubPath); ok {
-                       raw := normalizeModulePath(sp.Path())
-                       if len(raw) > len(rootModulePath) {
-                               path = raw
-                       }
+               // For our work here we actually care only about the provider type and
+               // we plan to place all default providers in the root module, and so
+               // it's safe for us to rely on ProvidedBy here rather than waiting for
+               // the later proper resolution of provider inheritance done by
+               // ProviderTransformer.
+               p, _ := pv.ProvidedBy()
+               if p.ProviderConfig.Alias != "" {
+                       // We do not create default aliased configurations.
+                       log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p)
+                       continue
                }
 
-               for _, p := range pv.ProvidedBy() {
-                       key := providerMapKey(p, pv)
-                       if _, ok := m[key]; ok {
-                               // This provider already exists as a configure node
-                               continue
-                       }
+               // We're going to create an implicit _default_ configuration for the
+               // referenced provider type in the _root_ module, ignoring all other
+               // aspects of the resource's declared provider address.
+               defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(p.ProviderConfig.Type)
+               key := defaultAddr.String()
+               provider := m[key]
 
-                       // If the provider has an alias in it, we just want the type
-                       ptype := p
-                       if idx := strings.IndexRune(p, '.'); idx != -1 {
-                               ptype = p[:idx]
-                       }
+               if provider != nil {
+                       // There's already an explicit default configuration for this
+                       // provider type in the root module, so we have nothing to do.
+                       continue
+               }
 
-                       if !t.AllowAny {
-                               if _, ok := supported[ptype]; !ok {
-                                       // If we don't support the provider type, skip it.
-                                       // Validation later will catch this as an error.
-                                       continue
-                               }
-                       }
+               log.Printf("[DEBUG] adding implicit provider configuration %s, implied first by %s", defaultAddr, dag.VertexName(v))
 
-                       // Add the missing provider node to the graph
-                       v := t.Concrete(&NodeAbstractProvider{
-                               NameValue: p,
-                               PathValue: path,
-                       }).(dag.Vertex)
-                       if len(path) > 0 {
-                               // We'll need the parent provider as well, so let's
-                               // add a dummy node to check to make sure that we add
-                               // that parent provider.
-                               check = append(check, &graphNodeProviderConsumerDummy{
-                                       ProviderValue: p,
-                                       PathValue:     path[:len(path)-1],
-                               })
-                       }
+               // create the missing top-level provider
+               provider = t.Concrete(&NodeAbstractProvider{
+                       Addr: defaultAddr,
+               }).(GraphNodeProvider)
 
-                       m[key] = g.Add(v)
-               }
+               g.Add(provider)
+               m[key] = provider
        }
 
-       return nil
+       return err
 }
 
 // ParentProviderTransformer connects provider nodes to their parents.
 //
 // This works by finding nodes that are both GraphNodeProviders and
 // GraphNodeSubPath. It then connects the providers to their parent
-// path.
+// path. The parent provider is always at the root level.
 type ParentProviderTransformer struct{}
 
 func (t *ParentProviderTransformer) Transform(g *Graph) error {
-       // Make a mapping of path to dag.Vertex, where path is: "path.name"
-       m := make(map[string]dag.Vertex)
-
-       // Also create a map that maps a provider to its parent
-       parentMap := make(map[dag.Vertex]string)
-       for _, raw := range g.Vertices() {
-               // If it is the flat version, then make it the non-flat version.
-               // We eventually want to get rid of the flat version entirely so
-               // this is a stop-gap while it still exists.
-               var v dag.Vertex = raw
-
+       pm := providerVertexMap(g)
+       for _, v := range g.Vertices() {
                // Only care about providers
                pn, ok := v.(GraphNodeProvider)
-               if !ok || pn.ProviderName() == "" {
+               if !ok {
                        continue
                }
 
-               // Also require a subpath, if there is no subpath then we
-               // just totally ignore it. The expectation of this transform is
-               // that it is used with a graph builder that is already flattened.
-               var path []string
-               if pn, ok := raw.(GraphNodeSubPath); ok {
-                       path = pn.Path()
-               }
-               path = normalizeModulePath(path)
-
-               // Build the key with path.name i.e. "child.subchild.aws"
-               key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
-               m[key] = raw
-
-               // Determine the parent if we're non-root. This is length 1 since
-               // the 0 index should be "root" since we normalize above.
-               if len(path) > 1 {
-                       path = path[:len(path)-1]
-                       key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
-                       parentMap[raw] = key
+               // Also require non-empty path, since otherwise we're in the root
+               // module and so cannot have a parent.
+               if len(pn.Path()) <= 1 {
+                       continue
                }
-       }
 
-       // Connect!
-       for v, key := range parentMap {
-               if parent, ok := m[key]; ok {
-                       g.Connect(dag.BasicEdge(v, parent))
+               // this provider may be disabled, but we can only get it's name from
+               // the ProviderName string
+               addr := pn.ProviderAddr()
+               parentAddr, ok := addr.Inherited()
+               if ok {
+                       parent := pm[parentAddr.String()]
+                       if parent != nil {
+                               g.Connect(dag.BasicEdge(v, parent))
+                       }
                }
        }
-
        return nil
 }
 
-// PruneProviderTransformer is a GraphTransformer that prunes all the
-// providers that aren't needed from the graph. A provider is unneeded if
-// no resource or module is using that provider.
+// PruneProviderTransformer removes any providers that are not actually used by
+// anything, and provider proxies. This avoids the provider being initialized
+// and configured.  This both saves resources but also avoids errors since
+// configuration may imply initialization which may require auth.
 type PruneProviderTransformer struct{}
 
 func (t *PruneProviderTransformer) Transform(g *Graph) error {
        for _, v := range g.Vertices() {
-               // We only care about the providers
-               if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" {
+               // We only care about providers
+               _, ok := v.(GraphNodeProvider)
+               if !ok {
                        continue
                }
-               // Does anything depend on this? If not, then prune it.
-               if s := g.UpEdges(v); s.Len() == 0 {
-                       if nv, ok := v.(dag.NamedVertex); ok {
-                               log.Printf("[DEBUG] Pruning provider with no dependencies: %s", nv.Name())
-                       }
+
+               // ProxyProviders will have up edges, but we're now done with them in the graph
+               if _, ok := v.(*graphNodeProxyProvider); ok {
+                       log.Printf("[DEBUG] pruning proxy %s", dag.VertexName(v))
                        g.Remove(v)
                }
-       }
-
-       return nil
-}
 
-// providerMapKey is a helper that gives us the key to use for the
-// maps returned by things such as providerVertexMap.
-func providerMapKey(k string, v dag.Vertex) string {
-       pathPrefix := ""
-       if sp, ok := v.(GraphNodeSubPath); ok {
-               raw := normalizeModulePath(sp.Path())
-               if len(raw) > len(rootModulePath) {
-                       pathPrefix = modulePrefixStr(raw) + "."
+               // Remove providers with no dependencies.
+               if g.UpEdges(v).Len() == 0 {
+                       log.Printf("[DEBUG] pruning unused %s", dag.VertexName(v))
+                       g.Remove(v)
                }
        }
 
-       return pathPrefix + k
+       return nil
 }
 
-func providerVertexMap(g *Graph) map[string]dag.Vertex {
-       m := make(map[string]dag.Vertex)
+func providerVertexMap(g *Graph) map[string]GraphNodeProvider {
+       m := make(map[string]GraphNodeProvider)
        for _, v := range g.Vertices() {
                if pv, ok := v.(GraphNodeProvider); ok {
-                       key := providerMapKey(pv.ProviderName(), v)
-                       m[key] = v
+                       addr := pv.ProviderAddr()
+                       m[addr.String()] = pv
                }
        }
 
        return m
 }
 
-func closeProviderVertexMap(g *Graph) map[string]dag.Vertex {
-       m := make(map[string]dag.Vertex)
+func closeProviderVertexMap(g *Graph) map[string]GraphNodeCloseProvider {
+       m := make(map[string]GraphNodeCloseProvider)
        for _, v := range g.Vertices() {
                if pv, ok := v.(GraphNodeCloseProvider); ok {
-                       m[pv.CloseProviderName()] = v
+                       addr := pv.CloseProviderAddr()
+                       m[addr.String()] = pv
                }
        }
 
@@ -320,16 +410,25 @@ func closeProviderVertexMap(g *Graph) map[string]dag.Vertex {
 }
 
 type graphNodeCloseProvider struct {
-       ProviderNameValue string
+       Addr addrs.AbsProviderConfig
 }
 
+var (
+       _ GraphNodeCloseProvider = (*graphNodeCloseProvider)(nil)
+)
+
 func (n *graphNodeCloseProvider) Name() string {
-       return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue)
+       return n.Addr.String() + " (close)"
+}
+
+// GraphNodeSubPath impl.
+func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance {
+       return n.Addr.Module
 }
 
 // GraphNodeEvalable impl.
 func (n *graphNodeCloseProvider) EvalTree() EvalNode {
-       return CloseProviderEvalTree(n.ProviderNameValue)
+       return CloseProviderEvalTree(n.Addr)
 }
 
 // GraphNodeDependable impl.
@@ -337,8 +436,8 @@ func (n *graphNodeCloseProvider) DependableName() []string {
        return []string{n.Name()}
 }
 
-func (n *graphNodeCloseProvider) CloseProviderName() string {
-       return n.ProviderNameValue
+func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig {
+       return n.Addr
 }
 
 // GraphNodeDotter impl.
@@ -362,19 +461,257 @@ func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool {
        return true
 }
 
-// graphNodeProviderConsumerDummy is a struct that never enters the real
-// graph (though it could to no ill effect). It implements
-// GraphNodeProviderConsumer and GraphNodeSubpath as a way to force
-// certain transformations.
-type graphNodeProviderConsumerDummy struct {
-       ProviderValue string
-       PathValue     []string
+// graphNodeProxyProvider is a GraphNodeProvider implementation that is used to
+// store the name and value of a provider node for inheritance between modules.
+// These nodes are only used to store the data while loading the provider
+// configurations, and are removed after all the resources have been connected
+// to their providers.
+type graphNodeProxyProvider struct {
+       addr   addrs.AbsProviderConfig
+       target GraphNodeProvider
 }
 
-func (n *graphNodeProviderConsumerDummy) Path() []string {
-       return n.PathValue
+var (
+       _ GraphNodeProvider = (*graphNodeProxyProvider)(nil)
+)
+
+func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig {
+       return n.addr
+}
+
+func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance {
+       return n.addr.Module
+}
+
+func (n *graphNodeProxyProvider) Name() string {
+       return n.addr.String() + " (proxy)"
 }
 
-func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string {
-       return []string{n.ProviderValue}
+// find the concrete provider instance
+func (n *graphNodeProxyProvider) Target() GraphNodeProvider {
+       switch t := n.target.(type) {
+       case *graphNodeProxyProvider:
+               return t.Target()
+       default:
+               return n.target
+       }
+}
+
+// ProviderConfigTransformer adds all provider nodes from the configuration and
+// attaches the configs.
+type ProviderConfigTransformer struct {
+       Providers []string
+       Concrete  ConcreteProviderNodeFunc
+
+       // each provider node is stored here so that the proxy nodes can look up
+       // their targets by name.
+       providers map[string]GraphNodeProvider
+       // record providers that can be overriden with a proxy
+       proxiable map[string]bool
+
+       // Config is the root node of the configuration tree to add providers from.
+       Config *configs.Config
+}
+
+func (t *ProviderConfigTransformer) Transform(g *Graph) error {
+       // If no configuration is given, we don't do anything
+       if t.Config == nil {
+               return nil
+       }
+
+       t.providers = make(map[string]GraphNodeProvider)
+       t.proxiable = make(map[string]bool)
+
+       // Start the transformation process
+       if err := t.transform(g, t.Config); err != nil {
+               return err
+       }
+
+       // finally attach the configs to the new nodes
+       return t.attachProviderConfigs(g)
+}
+
+func (t *ProviderConfigTransformer) transform(g *Graph, c *configs.Config) error {
+       // If no config, do nothing
+       if c == nil {
+               return nil
+       }
+
+       // Add our resources
+       if err := t.transformSingle(g, c); err != nil {
+               return err
+       }
+
+       // Transform all the children.
+       for _, cc := range c.Children {
+               if err := t.transform(g, cc); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) error {
+       // Get the module associated with this configuration tree node
+       mod := c.Module
+       staticPath := c.Path
+
+       // We actually need a dynamic module path here, but we've not yet updated
+       // our graph builders enough to support expansion of module calls with
+       // "count" and "for_each" set, so for now we'll shim this by converting to
+       // a dynamic path with no keys. At the time of writing this is the only
+       // possible kind of dynamic path anyway.
+       path := make(addrs.ModuleInstance, len(staticPath))
+       for i, name := range staticPath {
+               path[i] = addrs.ModuleInstanceStep{
+                       Name: name,
+               }
+       }
+
+       // add all providers from the configuration
+       for _, p := range mod.ProviderConfigs {
+               relAddr := p.Addr()
+               addr := relAddr.Absolute(path)
+
+               abstract := &NodeAbstractProvider{
+                       Addr: addr,
+               }
+               var v dag.Vertex
+               if t.Concrete != nil {
+                       v = t.Concrete(abstract)
+               } else {
+                       v = abstract
+               }
+
+               // Add it to the graph
+               g.Add(v)
+               key := addr.String()
+               t.providers[key] = v.(GraphNodeProvider)
+
+               // A provider configuration is "proxyable" if its configuration is
+               // entirely empty. This means it's standing in for a provider
+               // configuration that must be passed in from the parent module.
+               // We decide this by evaluating the config with an empty schema;
+               // if this succeeds, then we know there's nothing in the body.
+               _, diags := p.Config.Content(&hcl.BodySchema{})
+               t.proxiable[key] = !diags.HasErrors()
+       }
+
+       // Now replace the provider nodes with proxy nodes if a provider was being
+       // passed in, and create implicit proxies if there was no config. Any extra
+       // proxies will be removed in the prune step.
+       return t.addProxyProviders(g, c)
+}
+
+func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Config) error {
+       path := c.Path
+
+       // can't add proxies at the root
+       if len(path) == 0 {
+               return nil
+       }
+
+       parentPath, callAddr := path.Call()
+       parent := c.Parent
+       if parent == nil {
+               return nil
+       }
+
+       callName := callAddr.Name
+       var parentCfg *configs.ModuleCall
+       for name, mod := range parent.Module.ModuleCalls {
+               if name == callName {
+                       parentCfg = mod
+                       break
+               }
+       }
+
+       // We currently don't support count/for_each for modules and so we must
+       // shim our path and parentPath into module instances here so that the
+       // rest of Terraform can behave as if we do. This shimming should be
+       // removed later as part of implementing count/for_each for modules.
+       instPath := make(addrs.ModuleInstance, len(path))
+       for i, name := range path {
+               instPath[i] = addrs.ModuleInstanceStep{Name: name}
+       }
+       parentInstPath := make(addrs.ModuleInstance, len(parentPath))
+       for i, name := range parentPath {
+               parentInstPath[i] = addrs.ModuleInstanceStep{Name: name}
+       }
+
+       if parentCfg == nil {
+               // this can't really happen during normal execution.
+               return fmt.Errorf("parent module config not found for %s", c.Path.String())
+       }
+
+       // Go through all the providers the parent is passing in, and add proxies to
+       // the parent provider nodes.
+       for _, pair := range parentCfg.Providers {
+               fullAddr := pair.InChild.Addr().Absolute(instPath)
+               fullParentAddr := pair.InParent.Addr().Absolute(parentInstPath)
+               fullName := fullAddr.String()
+               fullParentName := fullParentAddr.String()
+
+               parentProvider := t.providers[fullParentName]
+
+               if parentProvider == nil {
+                       return fmt.Errorf("missing provider %s", fullParentName)
+               }
+
+               proxy := &graphNodeProxyProvider{
+                       addr:   fullAddr,
+                       target: parentProvider,
+               }
+
+               concreteProvider := t.providers[fullName]
+
+               // replace the concrete node with the provider passed in
+               if concreteProvider != nil && t.proxiable[fullName] {
+                       g.Replace(concreteProvider, proxy)
+                       t.providers[fullName] = proxy
+                       continue
+               }
+
+               // aliased configurations can't be implicitly passed in
+               if fullAddr.ProviderConfig.Alias != "" {
+                       continue
+               }
+
+               // There was no concrete provider, so add this as an implicit provider.
+               // The extra proxy will be pruned later if it's unused.
+               g.Add(proxy)
+               t.providers[fullName] = proxy
+       }
+       return nil
+}
+
+func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error {
+       for _, v := range g.Vertices() {
+               // Only care about GraphNodeAttachProvider implementations
+               apn, ok := v.(GraphNodeAttachProvider)
+               if !ok {
+                       continue
+               }
+
+               // Determine what we're looking for
+               addr := apn.ProviderAddr()
+
+               // Get the configuration.
+               mc := t.Config.DescendentForInstance(addr.Module)
+               if mc == nil {
+                       log.Printf("[TRACE] ProviderConfigTransformer: no configuration available for %s", addr.String())
+                       continue
+               }
+
+               // Go through the provider configs to find the matching config
+               for _, p := range mc.Module.ProviderConfigs {
+                       if p.Name == addr.ProviderConfig.Type && p.Alias == addr.ProviderConfig.Alias {
+                               log.Printf("[TRACE] ProviderConfigTransformer: attaching to %q provider configuration from %s", dag.VertexName(v), p.DeclRange)
+                               apn.AttachProvider(p)
+                               break
+                       }
+               }
+       }
+
+       return nil
 }