@@ -11,6 +11,7 @@ import (
11
11
"path/filepath"
12
12
"strings"
13
13
14
+ "github.com/fatih/color"
14
15
"github.com/magodo/aztfy/internal/armtemplate"
15
16
"github.com/magodo/aztfy/schema"
16
17
@@ -147,9 +148,19 @@ func (meta *Meta) ExportArmTemplate(ctx context.Context) error {
147
148
if err := json .Unmarshal (raw , & meta .armTemplate ); err != nil {
148
149
return fmt .Errorf ("unmarshalling the template: %w" , err )
149
150
}
151
+
150
152
return nil
151
153
}
152
154
155
+ func (meta * Meta ) ListAzureResourceIDs () []string {
156
+ var ids []string
157
+ for _ , res := range meta .armTemplate .Resources {
158
+ ids = append (ids , res .ID (meta .subscriptionId , meta .resourceGroup ))
159
+ }
160
+ ids = append (ids , armtemplate .ResourceGroupId .ID (meta .subscriptionId , meta .resourceGroup ))
161
+ return ids
162
+ }
163
+
153
164
type ImportList []ImportItem
154
165
155
166
func (l ImportList ) NonSkipped () ImportList {
@@ -163,10 +174,42 @@ func (l ImportList) NonSkipped() ImportList {
163
174
return out
164
175
}
165
176
177
+ func (l ImportList ) ImportErrored () ImportList {
178
+ var out ImportList
179
+ for _ , item := range l {
180
+ if item .ImportError == nil {
181
+ continue
182
+ }
183
+ out = append (out , item )
184
+ }
185
+ return out
186
+ }
187
+
188
+ func (l ImportList ) Imported () ImportList {
189
+ var out ImportList
190
+ for _ , item := range l .NonSkipped () {
191
+ if item .ImportError != nil {
192
+ continue
193
+ }
194
+ out = append (out , item )
195
+ }
196
+ return out
197
+ }
198
+
166
199
type ImportItem struct {
167
- ResourceID string
168
- Skip bool
200
+ // The azure resource id
201
+ ResourceID string
202
+
203
+ // Wether this azure resource should be skipped from importing
204
+ Skip bool
205
+
206
+ // Whether this azure resource failed to import into terraform (this might due to the TFResourceType doesn't match the resource)
207
+ ImportError error
208
+
209
+ // The terraform resource type
169
210
TFResourceType string
211
+
212
+ // The terraform resource name
170
213
TFResourceName string
171
214
}
172
215
@@ -177,12 +220,7 @@ func (item *ImportItem) TFAddr() string {
177
220
return item .TFResourceType + "." + item .TFResourceName
178
221
}
179
222
180
- func (meta * Meta ) ResolveImportList (ctx context.Context ) (ImportList , error ) {
181
- var ids []string
182
- for _ , res := range meta .armTemplate .Resources {
183
- ids = append (ids , res .ID (meta .subscriptionId , meta .resourceGroup ))
184
- }
185
- ids = append (ids , armtemplate .ResourceGroupId .ID (meta .subscriptionId , meta .resourceGroup ))
223
+ func (meta * Meta ) ResolveImportList (ids []string , ctx context.Context ) (ImportList , error ) {
186
224
187
225
// schema, err := meta.tf.ProvidersSchema(ctx)
188
226
// if err != nil {
@@ -196,7 +234,7 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
196
234
// userResourceMap is used to track the resource types and resource names that are specified by users.
197
235
userResourceMap := map [string ]map [string ]bool {}
198
236
reader := bufio .NewReader (os .Stdin )
199
- fmt . Println ( `Please input the Terraform resource type and name for each Azure resource in form of "<resource type>.<resource name>. Press enter with no input will skip importing that resource.` )
237
+ color . Cyan ( " \n Please input either the Terraform resource type and name in the form of \ " <resource type>.<resource name>\" , or simply enter to skip\n " )
200
238
for idx , id := range ids {
201
239
item := ImportItem {
202
240
ResourceID : id ,
@@ -244,7 +282,13 @@ func (meta *Meta) ResolveImportList(ctx context.Context) (ImportList, error) {
244
282
return list , nil
245
283
}
246
284
247
- func (meta * Meta ) Import (ctx context.Context , list ImportList ) error {
285
+ func (meta * Meta ) Import (ctx context.Context , list ImportList ) (ImportList , error ) {
286
+ if len (list .NonSkipped ()) == 0 {
287
+ return nil , nil
288
+ }
289
+
290
+ color .Cyan ("\n Import Azure Resources\n " )
291
+
248
292
// Generate a temp Terraform config to include the empty template for each resource.
249
293
// This is required for the following importing.
250
294
cfgFile := filepath .Join (meta .workspace , "main.tf" )
@@ -253,27 +297,39 @@ func (meta *Meta) Import(ctx context.Context, list ImportList) error {
253
297
for _ , item := range list .NonSkipped () {
254
298
tpl , err := meta .tf .Add (ctx , item .TFAddr ())
255
299
if err != nil {
256
- return fmt .Errorf ("generating resource template for %s: %w" , item .TFAddr (), err )
300
+ return nil , fmt .Errorf ("generating resource template for %s: %w" , item .TFAddr (), err )
257
301
}
258
302
tpls = append (tpls , tpl )
259
303
}
260
304
if err := os .WriteFile (cfgFile , []byte (strings .Join (tpls , "\n " )), 0644 ); err != nil {
261
- return fmt .Errorf ("generating resource template cfgFile file: %w" , err )
305
+ return nil , fmt .Errorf ("generating resource template cfgFile file: %w" , err )
262
306
}
263
307
// Remove the temp Terraform config once resources are imported.
264
308
// This is due to the fact that "terraform add" will complain the resource to be added already exist in the config, even we are outputting to stdout.
265
309
// This should be resolved once hashicorp/terraform#29220 is addressed.
266
310
defer os .Remove (cfgFile )
267
311
268
312
// Import resources
313
+ out := ImportList {}
269
314
for idx , item := range list .NonSkipped () {
270
- fmt .Printf ("[%d/%d] Importing %q as %s\n " , idx + 1 , len (list .NonSkipped ()), item .ResourceID , item .TFAddr ())
271
- if err := meta .tf .Import (ctx , item .TFAddr (), item .ResourceID ); err != nil {
272
- return err
315
+ fmt .Printf ("[%d/%d] Importing %q as %s...\n " , idx + 1 , len (list .NonSkipped ()), item .ResourceID , item .TFAddr ())
316
+ err := meta .tf .Import (ctx , item .TFAddr (), item .ResourceID )
317
+ if err != nil {
318
+ emsg := []string {}
319
+ for _ , el := range strings .Split (err .Error (), "\n " ) {
320
+ el := strings .TrimSpace (el )
321
+ if el == "" {
322
+ continue
323
+ }
324
+ emsg = append (emsg , "\t " + el )
325
+ }
326
+ color .Red (strings .Join (emsg , "\n " ))
273
327
}
328
+ item .ImportError = err
329
+ out = append (out , item )
274
330
}
275
331
276
- return nil
332
+ return out , nil
277
333
}
278
334
279
335
type ConfigInfos []ConfigInfo
@@ -290,10 +346,7 @@ func (cfg ConfigInfo) DumpHCL(w io.Writer) (int, error) {
290
346
func (meta * Meta ) StateToConfig (ctx context.Context , list ImportList ) (ConfigInfos , error ) {
291
347
out := ConfigInfos {}
292
348
293
- for _ , item := range list .NonSkipped () {
294
- if item .Skip {
295
- continue
296
- }
349
+ for _ , item := range list .Imported () {
297
350
tpl , err := meta .tf .Add (ctx , item .TFAddr (), tfexec .FromState (true ))
298
351
if err != nil {
299
352
return nil , fmt .Errorf ("converting terraform state to config for resource %s: %w" , item .TFAddr (), err )
@@ -335,7 +388,7 @@ func (meta *Meta) ResolveDependency(ctx context.Context, configs ConfigInfos) (C
335
388
out = append (out , cfg )
336
389
continue
337
390
}
338
- // This should never happen
391
+ // This should never happen as we always ensure there is at least one implicit dependency on the resource group for each resource.
339
392
if _ , ok := depInfo [armId ]; ! ok {
340
393
return nil , fmt .Errorf ("can't find resource %q in the arm template" , armId .ID (meta .subscriptionId , meta .resourceGroup ))
341
394
}
@@ -360,7 +413,6 @@ func (meta *Meta) GenerateConfig(cfgs ConfigInfos) error {
360
413
return fmt .Errorf ("generating main configuration file: %w" , err )
361
414
}
362
415
363
- fmt .Printf ("Please find the Terraform state and the config at: %s\n " , meta .workspace )
364
416
return nil
365
417
}
366
418
@@ -369,7 +421,7 @@ func (meta *Meta) hclBlockAppendDependency(body *hclwrite.Body, armIds []armtemp
369
421
for _ , armid := range armIds {
370
422
cfg , ok := cfgset [armid ]
371
423
if ! ok {
372
- dependencies = append (dependencies , fmt .Sprintf ("# Depending on %q, but it is not imported by Terraform. Please fix it manually ." , armid .ID (meta .subscriptionId , meta .resourceGroup )))
424
+ dependencies = append (dependencies , fmt .Sprintf ("# Depending on %q, which is not imported by Terraform." , armid .ID (meta .subscriptionId , meta .resourceGroup )))
373
425
continue
374
426
}
375
427
dependencies = append (dependencies , cfg .TFAddr ()+ "," )
0 commit comments