Skip to content

Commit 86c85c7

Browse files
authored
Merge pull request #623 from cloudflare/support-tf-1.5-import-blocks
import: add support for `import` blocks in Terraform 1.5+
2 parents b9c3de5 + 8b288a6 commit 86c85c7

File tree

3 files changed

+58
-15
lines changed

3 files changed

+58
-15
lines changed

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,30 @@ If you use another OS, you will need to download the release directly from
135135

136136
## Importing with Terraform state
137137

138-
`cf-terraforming` will output the `terraform import` compatible commands for you
139-
when you invoke the `import` command. This command assumes you have already ran
140-
`cf-terraforming generate ...` to output your resources.
138+
`cf-terraforming` has the ability to generate the configuration for you to import
139+
existing resources.
141140

142-
In the future we aim to automate this however for now, it is a manual step to
143-
allow flexibility in directory structure.
141+
Depending on your version of Terraform, you can generate the `import` block
142+
(Terraform 1.5+) using the `--modern-import-block` flag or the `terraform import`
143+
compatible CLI output (all versions).
144+
145+
This command assumes you have already ran `cf-terraforming generate ...` to
146+
output your resources.
147+
148+
```
149+
# All versions of Terraform
150+
$ cf-terraforming import \
151+
--resource-type "cloudflare_record" \
152+
--email $CLOUDFLARE_EMAIL \
153+
--key $CLOUDFLARE_API_KEY \
154+
--zone $CLOUDFLARE_ZONE_ID
155+
```
144156

145157
```
158+
# Terraform 1.5+ only
146159
$ cf-terraforming import \
147160
--resource-type "cloudflare_record" \
161+
--modern-import-block \
148162
--email $CLOUDFLARE_EMAIL \
149163
--key $CLOUDFLARE_API_KEY \
150164
--zone $CLOUDFLARE_ZONE_ID
@@ -157,7 +171,7 @@ Any resources not listed are currently not supported.
157171
| Resource | Resource Scope | Generate Supported | Import Supported |
158172
| ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | ------------------ | ---------------- |
159173
| [cloudflare_access_application](https://www.terraform.io/docs/providers/cloudflare/r/access_application) | Account |||
160-
| [cloudflare_access_group](https://www.terraform.io/docs/providers/cloudflare/r/access_group) | Account || |
174+
| [cloudflare_access_group](https://www.terraform.io/docs/providers/cloudflare/r/access_group) | Account |||
161175
| [cloudflare_access_identity_provider](https://www.terraform.io/docs/providers/cloudflare/r/access_identity_provider) | Account |||
162176
| [cloudflare_access_mutual_tls_certificate](https://www.terraform.io/docs/providers/cloudflare/r/access_mutual_tls_certificate) | Account |||
163177
| [cloudflare_access_policy](https://www.terraform.io/docs/providers/cloudflare/r/access_policy) | Account |||

internal/app/cf-terraforming/cmd/import.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"time"
1010

1111
"github.com/cloudflare/cloudflare-go"
12+
"github.com/hashicorp/hcl/v2/hclwrite"
1213
"github.com/spf13/cobra"
14+
"github.com/zclconf/go-cty/cty"
1315
)
1416

1517
// resourceImportStringFormats contains a mapping of the resource type to the
@@ -483,16 +485,43 @@ func runImport() func(cmd *cobra.Command, args []string) {
483485
return
484486
}
485487

488+
importFile := hclwrite.NewEmptyFile()
489+
importBody := importFile.Body()
490+
486491
for _, data := range jsonStructData {
487-
fmt.Fprint(cmd.OutOrStdout(), buildCompositeID(resourceType, data.(map[string]interface{})["id"].(string)))
492+
id := data.(map[string]interface{})["id"].(string)
493+
494+
if useModernImportBlock {
495+
idvalue := buildRawImportAddress(resourceType, id)
496+
imp := importBody.AppendNewBlock("import", []string{}).Body()
497+
imp.SetAttributeRaw("to", hclwrite.TokensForIdentifier(fmt.Sprintf("%s.%s", resourceType, fmt.Sprintf("%s_%s", terraformResourceNamePrefix, id))))
498+
imp.SetAttributeValue("id", cty.StringVal(idvalue))
499+
importFile.Body().AppendNewline()
500+
} else {
501+
fmt.Fprint(cmd.OutOrStdout(), buildTerraformImportCommand(resourceType, id))
502+
}
503+
}
504+
505+
if useModernImportBlock {
506+
// don't format the output; there is a bug in hclwrite.Format that
507+
// splits incorrectly on certain characters. instead, manually
508+
// insert new lines on the block.
509+
fmt.Fprint(cmd.OutOrStdout(), string(importFile.Bytes()))
488510
}
489511
}
490512
}
491513

492-
// buildCompositeID takes the resourceType and resourceID in order to lookup the
493-
// resource type import string and then return a suitable composite value that
494-
// is compatible with `terraform import`.
495-
func buildCompositeID(resourceType, resourceID string) string {
514+
// buildTerraformImportCommand takes the resourceType and resourceID in order to
515+
// lookup the resource type import string and then return a suitable composite
516+
// value that is compatible with `terraform import`.
517+
func buildTerraformImportCommand(resourceType, resourceID string) string {
518+
resourceImportAddress := buildRawImportAddress(resourceType, resourceID)
519+
return fmt.Sprintf("%s %s.%s_%s %s\n", terraformImportCmdPrefix, resourceType, terraformResourceNamePrefix, resourceID, resourceImportAddress)
520+
}
521+
522+
// buildRawImportAddress takes the resourceType and resourceID in order to lookup
523+
// the resource type import string and then return a suitable address.
524+
func buildRawImportAddress(resourceType, resourceID string) string {
496525
if _, ok := resourceImportStringFormats[resourceType]; !ok {
497526
log.Fatalf("%s does not have an import format defined", resourceType)
498527
}
@@ -508,16 +537,14 @@ func buildCompositeID(resourceType, resourceID string) string {
508537
identiferValue = zoneID
509538
}
510539

511-
s := ""
512-
s += fmt.Sprintf("%s %s.%s_%s %s", terraformImportCmdPrefix, resourceType, terraformResourceNamePrefix, resourceID, resourceImportStringFormats[resourceType])
540+
s := resourceImportStringFormats[resourceType]
513541
replacer := strings.NewReplacer(
514542
":identifier_type", identiferType,
515543
":identifier_value", identiferValue,
516544
":zone_id", zoneID,
517545
":account_id", accountID,
518546
":id", resourceID,
519547
)
520-
s += "\n"
521548

522549
return replacer.Replace(s)
523550
}

internal/app/cf-terraforming/cmd/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
var log = logrus.New()
1212
var cfgFile, zoneID, hostname, apiEmail, apiKey, apiToken, accountID, terraformInstallPath, terraformBinaryPath string
13-
var verbose bool
13+
var verbose, useModernImportBlock bool
1414
var api *cloudflare.API
1515
var terraformImportCmdPrefix = "terraform import"
1616
var terraformResourceNamePrefix = "terraform_managed_resource"
@@ -118,6 +118,8 @@ func init() {
118118
if err = viper.BindEnv("terraform-install-path", "CLOUDFLARE_TERRAFORM_INSTALL_PATH"); err != nil {
119119
log.Fatal(err)
120120
}
121+
122+
rootCmd.PersistentFlags().BoolVarP(&useModernImportBlock, "modern-import-block", "", false, "Whether to generate HCL import blocks for generated resources instead of `terraform import` compatible CLI commands. This is only compatible with Terraform 1.5+")
121123
}
122124

123125
// initConfig reads in config file and ENV variables if set.

0 commit comments

Comments
 (0)