6 "github.com/hashicorp/terraform/addrs"
7 "github.com/hashicorp/terraform/providers"
8 "github.com/hashicorp/terraform/tfdiags"
11 // ImportStateTransformer is a GraphTransformer that adds nodes to the
12 // graph to represent the imports we want to do for resources.
13 type ImportStateTransformer struct {
14 Targets []*ImportTarget
17 func (t *ImportStateTransformer) Transform(g *Graph) error {
18 for _, target := range t.Targets {
19 // The ProviderAddr may not be supplied for non-aliased providers.
20 // This will be populated if the targets come from the cli, but tests
21 // may not specify implied provider addresses.
22 providerAddr := target.ProviderAddr
23 if providerAddr.ProviderConfig.Type == "" {
24 providerAddr = target.Addr.Resource.Resource.DefaultProviderConfig().Absolute(target.Addr.Module)
27 node := &graphNodeImportState{
30 ProviderAddr: providerAddr,
37 type graphNodeImportState struct {
38 Addr addrs.AbsResourceInstance // Addr is the resource address to import into
39 ID string // ID is the ID to import as
40 ProviderAddr addrs.AbsProviderConfig // Provider address given by the user, or implied by the resource type
41 ResolvedProvider addrs.AbsProviderConfig // provider node address after resolution
43 states []providers.ImportedResource
47 _ GraphNodeSubPath = (*graphNodeImportState)(nil)
48 _ GraphNodeEvalable = (*graphNodeImportState)(nil)
49 _ GraphNodeProviderConsumer = (*graphNodeImportState)(nil)
50 _ GraphNodeDynamicExpandable = (*graphNodeImportState)(nil)
53 func (n *graphNodeImportState) Name() string {
54 return fmt.Sprintf("%s (import id %q)", n.Addr, n.ID)
57 // GraphNodeProviderConsumer
58 func (n *graphNodeImportState) ProvidedBy() (addrs.AbsProviderConfig, bool) {
59 // We assume that n.ProviderAddr has been properly populated here.
60 // It's the responsibility of the code creating a graphNodeImportState
61 // to populate this, possibly by calling DefaultProviderConfig() on the
62 // resource address to infer an implied provider from the resource type
64 return n.ProviderAddr, false
67 // GraphNodeProviderConsumer
68 func (n *graphNodeImportState) SetProvider(addr addrs.AbsProviderConfig) {
69 n.ResolvedProvider = addr
73 func (n *graphNodeImportState) Path() addrs.ModuleInstance {
77 // GraphNodeEvalable impl.
78 func (n *graphNodeImportState) EvalTree() EvalNode {
79 var provider providers.Interface
84 // Return our sequence
88 Addr: n.ResolvedProvider,
92 Addr: n.Addr.Resource,
101 // GraphNodeDynamicExpandable impl.
103 // We use DynamicExpand as a way to generate the subgraph of refreshes
104 // and state inserts we need to do for our import state. Since they're new
105 // resources they don't depend on anything else and refreshes are isolated
106 // so this is nearly a perfect use case for dynamic expand.
107 func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
108 var diags tfdiags.Diagnostics
110 g := &Graph{Path: ctx.Path()}
112 // nameCounter is used to de-dup names in the state.
113 nameCounter := make(map[string]int)
115 // Compile the list of addresses that we'll be inserting into the state.
116 // We do this ahead of time so we can verify that we aren't importing
117 // something that already exists.
118 addrs := make([]addrs.AbsResourceInstance, len(n.states))
119 for i, state := range n.states {
121 if t := state.TypeName; t != "" {
122 addr.Resource.Resource.Type = t
125 // Determine if we need to suffix the name to de-dup
127 count, ok := nameCounter[key]
130 addr.Resource.Resource.Name += fmt.Sprintf("-%d", count)
132 nameCounter[key] = count
134 // Add it to our list
138 // Verify that all the addresses are clear
140 for _, addr := range addrs {
141 existing := state.ResourceInstance(addr)
143 diags = diags.Append(tfdiags.Sourceless(
145 "Resource already managed by Terraform",
146 fmt.Sprintf("Terraform is already managing a remote object for %s. To import to this address you must first remove the existing object from the state.", addr),
151 if diags.HasErrors() {
152 // Bail out early, then.
153 return nil, diags.Err()
156 // For each of the states, we add a node to handle the refresh/add to state.
157 // "n.states" is populated by our own EvalTree with the result of
158 // ImportState. Since DynamicExpand is always called after EvalTree, this
160 for i, state := range n.states {
161 g.Add(&graphNodeImportStateSub{
162 TargetAddr: addrs[i],
164 ResolvedProvider: n.ResolvedProvider,
168 // Root transform for a single root
169 t := &RootTransformer{}
170 if err := t.Transform(g); err != nil {
175 return g, diags.Err()
178 // graphNodeImportStateSub is the sub-node of graphNodeImportState
179 // and is part of the subgraph. This node is responsible for refreshing
180 // and adding a resource to the state once it is imported.
181 type graphNodeImportStateSub struct {
182 TargetAddr addrs.AbsResourceInstance
183 State providers.ImportedResource
184 ResolvedProvider addrs.AbsProviderConfig
188 _ GraphNodeSubPath = (*graphNodeImportStateSub)(nil)
189 _ GraphNodeEvalable = (*graphNodeImportStateSub)(nil)
192 func (n *graphNodeImportStateSub) Name() string {
193 return fmt.Sprintf("import %s result", n.TargetAddr)
196 func (n *graphNodeImportStateSub) Path() addrs.ModuleInstance {
197 return n.TargetAddr.Module
200 // GraphNodeEvalable impl.
201 func (n *graphNodeImportStateSub) EvalTree() EvalNode {
202 // If the Ephemeral type isn't set, then it is an error
203 if n.State.TypeName == "" {
204 err := fmt.Errorf("import of %s didn't set type", n.TargetAddr.String())
205 return &EvalReturnError{Error: &err}
208 state := n.State.AsInstanceObject()
210 var provider providers.Interface
211 var providerSchema *ProviderSchema
212 return &EvalSequence{
215 Addr: n.ResolvedProvider,
217 Schema: &providerSchema,
220 Addr: n.TargetAddr.Resource,
221 ProviderAddr: n.ResolvedProvider,
223 ProviderSchema: &providerSchema,
227 &EvalImportStateVerify{
228 Addr: n.TargetAddr.Resource,
232 Addr: n.TargetAddr.Resource,
233 ProviderAddr: n.ResolvedProvider,
234 ProviderSchema: &providerSchema,