diff --git a/README.md b/README.md index 7bbe9c893..0efddb585 100644 --- a/README.md +++ b/README.md @@ -32,20 +32,20 @@ Available Commands: version Print the version number of cf-terraforming Flags: - -a, --account string Use specific account ID for commands - -c, --config string Path to config file (default "/Users/jacob/.cf-terraforming.yaml") + -a, --account string Target the provided account ID for the command + -c, --config string Path to config file (default "~/.cf-terraforming.yaml") -e, --email string API Email address associated with your account -h, --help help for cf-terraforming --hostname string Hostname to use to query the API -k, --key string API Key generated on the 'My Profile' page. See: https://dash.cloudflare.com/profile --modern-import-block Whether to generate HCL import blocks for generated resources instead of terraform import compatible CLI commands. This is only compatible with Terraform 1.5+ --provider-registry-hostname string Hostname to use for provider registry lookups (default "registry.terraform.io") - --resource-type string Which resource you wish to generate + --resource-type string Comma delimitered string of which resource(s) you wish to generate --terraform-binary-path string Path to an existing Terraform binary (otherwise, one will be downloaded) --terraform-install-path string Path to an initialized Terraform working directory (default ".") -t, --token string API Token -v, --verbose Specify verbose output (same as setting log level to debug) - -z, --zone string Limit the export to a single zone ID + -z, --zone string Target the provided zone ID for the command Use "cf-terraforming [command] --help" for more information about a command. ``` diff --git a/internal/app/cf-terraforming/cmd/generate.go b/internal/app/cf-terraforming/cmd/generate.go index d9861a2d1..c759e7a8a 100644 --- a/internal/app/cf-terraforming/cmd/generate.go +++ b/internal/app/cf-terraforming/cmd/generate.go @@ -88,284 +88,228 @@ func generateResources() func(cmd *cobra.Command, args []string) { log.Fatal("failed to detect provider installation") } - r := s.ResourceSchemas[resourceType] - log.Debugf("beginning to read and build %s resources", resourceType) - - // Initialise `resourceCount` outside of the switch for supported resources - // to allow it to be referenced further down in the loop that outputs the - // newly generated resources. - resourceCount := 0 - - // Lazy approach to restrict support to known resources due to Go's type - // restrictions and the need to explicitly map out the structs. - var jsonStructData []interface{} - - var identifier *cloudflare.ResourceContainer - if accountID != "" { - identifier = cloudflare.AccountIdentifier(accountID) - } else { - identifier = cloudflare.ZoneIdentifier(zoneID) - } - - switch resourceType { - case "cloudflare_access_application": - jsonPayload, _, err := api.ListAccessApplications(context.Background(), identifier, cloudflare.ListAccessApplicationsParams{}) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_group": - jsonPayload, _, err := api.ListAccessGroups(context.Background(), identifier, cloudflare.ListAccessGroupsParams{}) - if err != nil { - log.Fatal(err) - } + resources := strings.Split(resourceType, ",") + for _, resourceType := range resources { + r := s.ResourceSchemas[resourceType] + log.Debugf("beginning to read and build %s resources", resourceType) - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_identity_provider": - jsonPayload, _, err := api.ListAccessIdentityProviders(context.Background(), identifier, cloudflare.ListAccessIdentityProvidersParams{}) - if err != nil { - log.Fatal(err) - } + // Initialise `resourceCount` outside of the switch for supported resources + // to allow it to be referenced further down in the loop that outputs the + // newly generated resources. + resourceCount := 0 - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_service_token": - jsonPayload, _, err := api.ListAccessServiceTokens(context.Background(), identifier, cloudflare.ListAccessServiceTokensParams{}) - if err != nil { - log.Fatal(err) - } + // Lazy approach to restrict support to known resources due to Go's type + // restrictions and the need to explicitly map out the structs. + var jsonStructData []interface{} - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_mutual_tls_certificate": - jsonPayload, _, err := api.ListAccessMutualTLSCertificates(context.Background(), identifier, cloudflare.ListAccessMutualTLSCertificatesParams{}) - if err != nil { - log.Fatal(err) + var identifier *cloudflare.ResourceContainer + if accountID != "" { + identifier = cloudflare.AccountIdentifier(accountID) + } else { + identifier = cloudflare.ZoneIdentifier(zoneID) } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_rule": - if accountID != "" { - jsonPayload, err := api.ListAccountAccessRules(context.Background(), accountID, cloudflare.AccessRule{}, 1) + switch resourceType { + case "cloudflare_access_application": + jsonPayload, _, err := api.ListAccessApplications(context.Background(), identifier, cloudflare.ListAccessApplicationsParams{}) if err != nil { log.Fatal(err) } - resourceCount = len(jsonPayload.Result) - m, _ := json.Marshal(jsonPayload.Result) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - } else { - jsonPayload, err := api.ListZoneAccessRules(context.Background(), zoneID, cloudflare.AccessRule{}, 1) + case "cloudflare_access_group": + jsonPayload, _, err := api.ListAccessGroups(context.Background(), identifier, cloudflare.ListAccessGroupsParams{}) if err != nil { log.Fatal(err) } - resourceCount = len(jsonPayload.Result) - m, _ := json.Marshal(jsonPayload.Result) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - } - case "cloudflare_account_member": - jsonPayload, _, err := api.AccountMembers(context.Background(), accountID, cloudflare.PaginationOptions{}) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + case "cloudflare_access_identity_provider": + jsonPayload, _, err := api.ListAccessIdentityProviders(context.Background(), identifier, cloudflare.ListAccessIdentityProvidersParams{}) + if err != nil { + log.Fatal(err) + } - // remap email and role_ids into the right structure. - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["email_address"] = jsonStructData[i].(map[string]interface{})["user"].(map[string]interface{})["email"] - roleIDs := []string{} - for _, role := range jsonStructData[i].(map[string]interface{})["roles"].([]interface{}) { - roleIDs = append(roleIDs, role.(map[string]interface{})["id"].(string)) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_access_service_token": + jsonPayload, _, err := api.ListAccessServiceTokens(context.Background(), identifier, cloudflare.ListAccessServiceTokensParams{}) + if err != nil { + log.Fatal(err) } - jsonStructData[i].(map[string]interface{})["role_ids"] = roleIDs - } - case "cloudflare_argo": - jsonPayload := []cloudflare.ArgoFeatureSetting{} - argoSmartRouting, err := api.ArgoSmartRouting(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - jsonPayload = append(jsonPayload, argoSmartRouting) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_access_mutual_tls_certificate": + jsonPayload, _, err := api.ListAccessMutualTLSCertificates(context.Background(), identifier, cloudflare.ListAccessMutualTLSCertificatesParams{}) + if err != nil { + log.Fatal(err) + } - argoTieredCaching, err := api.ArgoTieredCaching(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - jsonPayload = append(jsonPayload, argoTieredCaching) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_access_rule": + if accountID != "" { + jsonPayload, err := api.ListAccountAccessRules(context.Background(), accountID, cloudflare.AccessRule{}, 1) + if err != nil { + log.Fatal(err) + } - resourceCount = 1 + resourceCount = len(jsonPayload.Result) + m, _ := json.Marshal(jsonPayload.Result) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } else { + jsonPayload, err := api.ListZoneAccessRules(context.Background(), zoneID, cloudflare.AccessRule{}, 1) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload.Result) + m, _ := json.Marshal(jsonPayload.Result) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } + case "cloudflare_account_member": + jsonPayload, _, err := api.AccountMembers(context.Background(), accountID, cloudflare.PaginationOptions{}) + if err != nil { + log.Fatal(err) + } - for i, b := range jsonStructData { - key := b.(map[string]interface{})["id"].(string) - jsonStructData[0].(map[string]interface{})[key] = jsonStructData[i].(map[string]interface{})["value"] - } - case "cloudflare_api_shield": - jsonPayload := []cloudflare.APIShield{} - apiShieldConfig, _, err := api.GetAPIShieldConfiguration(context.Background(), identifier) - if err != nil { - log.Fatal(err) - } - // the response can contain an empty APIShield struct. Verify we have data before we attempt to do anything - jsonPayload = append(jsonPayload, apiShieldConfig) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // remap email and role_ids into the right structure. + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["email_address"] = jsonStructData[i].(map[string]interface{})["user"].(map[string]interface{})["email"] + roleIDs := []string{} + for _, role := range jsonStructData[i].(map[string]interface{})["roles"].([]interface{}) { + roleIDs = append(roleIDs, role.(map[string]interface{})["id"].(string)) + } + jsonStructData[i].(map[string]interface{})["role_ids"] = roleIDs + } + case "cloudflare_argo": + jsonPayload := []cloudflare.ArgoFeatureSetting{} - // this is only every a 1:1 so we can just verify if the 0th element has they key we expect - jsonStructData[0].(map[string]interface{})["id"] = zoneID + argoSmartRouting, err := api.ArgoSmartRouting(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } + jsonPayload = append(jsonPayload, argoSmartRouting) - if jsonStructData[0].(map[string]interface{})["auth_id_characteristics"] == nil { - // force a no resources return by setting resourceCount to 0 - resourceCount = 0 - } - case "cloudflare_user_agent_blocking_rule": - page := 1 - var jsonPayload []cloudflare.UserAgentRule - for { - res, err := api.ListUserAgentRules(context.Background(), zoneID, page) + argoTieredCaching, err := api.ArgoTieredCaching(context.Background(), zoneID) if err != nil { log.Fatal(err) } + jsonPayload = append(jsonPayload, argoTieredCaching) - jsonPayload = append(jsonPayload, res.Result...) - res.ResultInfo = res.ResultInfo.Next() + resourceCount = 1 - if res.ResultInfo.Done() { - break + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } - page = page + 1 - } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err := json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_bot_management": - botManagement, err := api.GetBotManagement(context.Background(), identifier) - if err != nil { - log.Fatal(err) - } - var jsonPayload []cloudflare.BotManagement - jsonPayload = append(jsonPayload, botManagement) + for i, b := range jsonStructData { + key := b.(map[string]interface{})["id"].(string) + jsonStructData[0].(map[string]interface{})[key] = jsonStructData[i].(map[string]interface{})["value"] + } + case "cloudflare_api_shield": + jsonPayload := []cloudflare.APIShield{} + apiShieldConfig, _, err := api.GetAPIShieldConfiguration(context.Background(), identifier) + if err != nil { + log.Fatal(err) + } + // the response can contain an empty APIShield struct. Verify we have data before we attempt to do anything + jsonPayload = append(jsonPayload, apiShieldConfig) - resourceCount = 1 - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - jsonStructData[0].(map[string]interface{})["id"] = zoneID - case "cloudflare_byo_ip_prefix": - jsonPayload, err := api.ListPrefixes(context.Background(), accountID) - if err != nil { - log.Fatal(err) - } + // this is only every a 1:1 so we can just verify if the 0th element has they key we expect + jsonStructData[0].(map[string]interface{})["id"] = zoneID - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + if jsonStructData[0].(map[string]interface{})["auth_id_characteristics"] == nil { + // force a no resources return by setting resourceCount to 0 + resourceCount = 0 + } + case "cloudflare_user_agent_blocking_rule": + page := 1 + var jsonPayload []cloudflare.UserAgentRule + for { + res, err := api.ListUserAgentRules(context.Background(), zoneID, page) + if err != nil { + log.Fatal(err) + } - // remap ID to prefix_id and advertised to advertisement on the JSON payloads. - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["prefix_id"] = jsonStructData[i].(map[string]interface{})["id"] + jsonPayload = append(jsonPayload, res.Result...) + res.ResultInfo = res.ResultInfo.Next() - if jsonStructData[i].(map[string]interface{})["advertised"].(bool) { - jsonStructData[i].(map[string]interface{})["advertisement"] = "on" - } else { - jsonStructData[i].(map[string]interface{})["advertisement"] = "off" + if res.ResultInfo.Done() { + break + } + page = page + 1 } - } - case "cloudflare_certificate_pack": - jsonPayload, err := api.ListCertificatePacks(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - var customerManagedCertificates []cloudflare.CertificatePack - for _, r := range jsonPayload { - if r.Type != "universal" { - customerManagedCertificates = append(customerManagedCertificates, r) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err := json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } - } - jsonPayload = customerManagedCertificates - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_custom_pages": - if accountID != "" { - acc := cloudflare.CustomPageOptions{AccountID: accountID} - jsonPayload, err := api.CustomPages(context.Background(), &acc) + case "cloudflare_bot_management": + botManagement, err := api.GetBotManagement(context.Background(), identifier) if err != nil { log.Fatal(err) } + var jsonPayload []cloudflare.BotManagement + jsonPayload = append(jsonPayload, botManagement) - resourceCount = len(jsonPayload) + resourceCount = 1 m, _ := json.Marshal(jsonPayload) err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - } else { - zo := cloudflare.CustomPageOptions{ZoneID: zoneID} - jsonPayload, err := api.CustomPages(context.Background(), &zo) + + jsonStructData[0].(map[string]interface{})["id"] = zoneID + case "cloudflare_byo_ip_prefix": + jsonPayload, err := api.ListPrefixes(context.Background(), accountID) if err != nil { log.Fatal(err) } @@ -376,486 +320,545 @@ func generateResources() func(cmd *cobra.Command, args []string) { if err != nil { log.Fatal(err) } - } - - var newJsonStructData []interface{} - // remap ID to the "type" field - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["type"] = jsonStructData[i].(map[string]interface{})["id"] - // we only want repsonses that have 'url' - if jsonStructData[i].(map[string]interface{})["url"] != nil { - newJsonStructData = append(newJsonStructData, jsonStructData[i]) - } - } - jsonStructData = newJsonStructData - resourceCount = len(jsonStructData) - - case "cloudflare_custom_hostname_fallback_origin": - var jsonPayload []cloudflare.CustomHostnameFallbackOrigin - apiCall, err := api.CustomHostnameFallbackOrigin(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - - if apiCall.Origin != "" { - resourceCount = 1 - jsonPayload = append(jsonPayload, apiCall) - } - - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["id"] = sanitiseTerraformResourceName(jsonStructData[i].(map[string]interface{})["origin"].(string)) - jsonStructData[i].(map[string]interface{})["status"] = nil - } - case "cloudflare_filter": - jsonPayload, _, err := api.Filters(context.Background(), identifier, cloudflare.FilterListParams{}) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_firewall_rule": - jsonPayload, _, err := api.FirewallRules(context.Background(), identifier, cloudflare.FirewallRuleListParams{}) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - - // remap Filter.ID to `filter_id` on the JSON payloads. - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["filter_id"] = jsonStructData[i].(map[string]interface{})["filter"].(map[string]interface{})["id"] - } - case "cloudflare_custom_hostname": - jsonPayload, _, err := api.CustomHostnames(context.Background(), zoneID, 1, cloudflare.CustomHostname{}) - if err != nil { - log.Fatal(err) - } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["ssl"].(map[string]interface{})["validation_errors"] = nil - } - case "cloudflare_custom_ssl": - jsonPayload, err := api.ListSSL(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_healthcheck": - jsonPayload, err := api.Healthchecks(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + // remap ID to prefix_id and advertised to advertisement on the JSON payloads. + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["prefix_id"] = jsonStructData[i].(map[string]interface{})["id"] - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_load_balancer": - jsonPayload, err := api.ListLoadBalancers(context.Background(), identifier, cloudflare.ListLoadBalancerParams{}) - if err != nil { - log.Fatal(err) - } + if jsonStructData[i].(map[string]interface{})["advertised"].(bool) { + jsonStructData[i].(map[string]interface{})["advertisement"] = "on" + } else { + jsonStructData[i].(map[string]interface{})["advertisement"] = "off" + } + } + case "cloudflare_certificate_pack": + jsonPayload, err := api.ListCertificatePacks(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + var customerManagedCertificates []cloudflare.CertificatePack + for _, r := range jsonPayload { + if r.Type != "universal" { + customerManagedCertificates = append(customerManagedCertificates, r) + } + } + jsonPayload = customerManagedCertificates - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["default_pool_ids"] = jsonStructData[i].(map[string]interface{})["default_pools"] - jsonStructData[i].(map[string]interface{})["fallback_pool_id"] = jsonStructData[i].(map[string]interface{})["fallback_pool"] + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_custom_pages": + if accountID != "" { + acc := cloudflare.CustomPageOptions{AccountID: accountID} + jsonPayload, err := api.CustomPages(context.Background(), &acc) + if err != nil { + log.Fatal(err) + } - if jsonStructData[i].(map[string]interface{})["country_pools"] != nil { - original := jsonStructData[i].(map[string]interface{})["country_pools"] - jsonStructData[i].(map[string]interface{})["country_pools"] = []interface{}{} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } else { + zo := cloudflare.CustomPageOptions{ZoneID: zoneID} + jsonPayload, err := api.CustomPages(context.Background(), &zo) + if err != nil { + log.Fatal(err) + } - for country, popIDs := range original.(map[string]interface{}) { - jsonStructData[i].(map[string]interface{})["country_pools"] = append(jsonStructData[i].(map[string]interface{})["country_pools"].([]interface{}), map[string]interface{}{"country": country, "pool_ids": popIDs}) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } } - if jsonStructData[i].(map[string]interface{})["region_pools"] != nil { - original := jsonStructData[i].(map[string]interface{})["region_pools"] - jsonStructData[i].(map[string]interface{})["region_pools"] = []interface{}{} - - for region, popIDs := range original.(map[string]interface{}) { - jsonStructData[i].(map[string]interface{})["region_pools"] = append(jsonStructData[i].(map[string]interface{})["region_pools"].([]interface{}), map[string]interface{}{"region": region, "pool_ids": popIDs}) + var newJsonStructData []interface{} + // remap ID to the "type" field + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["type"] = jsonStructData[i].(map[string]interface{})["id"] + // we only want repsonses that have 'url' + if jsonStructData[i].(map[string]interface{})["url"] != nil { + newJsonStructData = append(newJsonStructData, jsonStructData[i]) } } + jsonStructData = newJsonStructData + resourceCount = len(jsonStructData) - if jsonStructData[i].(map[string]interface{})["pop_pools"] != nil { - original := jsonStructData[i].(map[string]interface{})["pop_pools"] - jsonStructData[i].(map[string]interface{})["pop_pools"] = []interface{}{} + case "cloudflare_custom_hostname_fallback_origin": + var jsonPayload []cloudflare.CustomHostnameFallbackOrigin + apiCall, err := api.CustomHostnameFallbackOrigin(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - for pop, popIDs := range original.(map[string]interface{}) { - jsonStructData[i].(map[string]interface{})["pop_pools"] = append(jsonStructData[i].(map[string]interface{})["pop_pools"].([]interface{}), map[string]interface{}{"pop": pop, "pool_ids": popIDs}) - } + if apiCall.Origin != "" { + resourceCount = 1 + jsonPayload = append(jsonPayload, apiCall) } - } - case "cloudflare_load_balancer_pool": - jsonPayload, err := api.ListLoadBalancerPools(context.Background(), identifier, cloudflare.ListLoadBalancerPoolParams{}) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["id"] = sanitiseTerraformResourceName(jsonStructData[i].(map[string]interface{})["origin"].(string)) + jsonStructData[i].(map[string]interface{})["status"] = nil + } + case "cloudflare_filter": + jsonPayload, _, err := api.Filters(context.Background(), identifier, cloudflare.FilterListParams{}) + if err != nil { + log.Fatal(err) + } - for i := 0; i < resourceCount; i++ { - for originCounter := range jsonStructData[i].(map[string]interface{})["origins"].([]interface{}) { - if jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"] != nil { - jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["header"] = "Host" - jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["values"] = jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["Host"] - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_firewall_rule": + jsonPayload, _, err := api.FirewallRules(context.Background(), identifier, cloudflare.FirewallRuleListParams{}) + if err != nil { + log.Fatal(err) } - } - case "cloudflare_load_balancer_monitor": - jsonPayload, err := api.ListLoadBalancerMonitors(context.Background(), identifier, cloudflare.ListLoadBalancerMonitorParams{}) - if err != nil { - log.Fatal(err) - } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_logpush_job": - jsonPayload, err := api.ListLogpushJobs(context.Background(), identifier, cloudflare.ListLogpushJobsParams{}) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // remap Filter.ID to `filter_id` on the JSON payloads. + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["filter_id"] = jsonStructData[i].(map[string]interface{})["filter"].(map[string]interface{})["id"] + } + case "cloudflare_custom_hostname": + jsonPayload, _, err := api.CustomHostnames(context.Background(), zoneID, 1, cloudflare.CustomHostname{}) + if err != nil { + log.Fatal(err) + } - for i := 0; i < resourceCount; i++ { - // Workaround for LogpushJob.Filter being empty with a custom - // marshaler and returning `{"where":{}}` as the "empty" value. - if jsonStructData[i].(map[string]interface{})["filter"] == `{"where":{}}` { - jsonStructData[i].(map[string]interface{})["filter"] = nil + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } - } - case "cloudflare_managed_headers": - // only grab the enabled headers - jsonPayload, err := api.ListZoneManagedHeaders(context.Background(), cloudflare.ResourceIdentifier(zoneID), cloudflare.ListManagedHeadersParams{Status: "enabled"}) - if err != nil { - log.Fatal(err) - } - var managedHeaders []cloudflare.ManagedHeaders - managedHeaders = append(managedHeaders, jsonPayload) + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["ssl"].(map[string]interface{})["validation_errors"] = nil + } + case "cloudflare_custom_ssl": + jsonPayload, err := api.ListSSL(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - resourceCount = len(managedHeaders) - m, _ := json.Marshal(managedHeaders) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_healthcheck": + jsonPayload, err := api.Healthchecks(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["id"] = zoneID - } - case "cloudflare_origin_ca_certificate": - jsonPayload, err := api.ListOriginCACertificates(context.Background(), cloudflare.ListOriginCertificatesParams{ZoneID: zoneID}) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_load_balancer": + jsonPayload, err := api.ListLoadBalancers(context.Background(), identifier, cloudflare.ListLoadBalancerParams{}) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_page_rule": - jsonPayload, err := api.ListPageRules(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["default_pool_ids"] = jsonStructData[i].(map[string]interface{})["default_pools"] + jsonStructData[i].(map[string]interface{})["fallback_pool_id"] = jsonStructData[i].(map[string]interface{})["fallback_pool"] - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["target"] = jsonStructData[i].(map[string]interface{})["targets"].([]interface{})[0].(map[string]interface{})["constraint"].(map[string]interface{})["value"] - jsonStructData[i].(map[string]interface{})["actions"] = flattenAttrMap(jsonStructData[i].(map[string]interface{})["actions"].([]interface{})) - - // Have to remap the cache_ttl_by_status to conform to Terraform's more human-friendly structure. - if cache, ok := jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_ttl_by_status"].(map[string]interface{}); ok { - cache_ttl_by_status := []map[string]interface{}{} - - for codes, ttl := range cache { - if ttl == "no-cache" { - ttl = 0 - } else if ttl == "no-store" { - ttl = -1 + if jsonStructData[i].(map[string]interface{})["country_pools"] != nil { + original := jsonStructData[i].(map[string]interface{})["country_pools"] + jsonStructData[i].(map[string]interface{})["country_pools"] = []interface{}{} + + for country, popIDs := range original.(map[string]interface{}) { + jsonStructData[i].(map[string]interface{})["country_pools"] = append(jsonStructData[i].(map[string]interface{})["country_pools"].([]interface{}), map[string]interface{}{"country": country, "pool_ids": popIDs}) } - elem := map[string]interface{}{ - "codes": codes, - "ttl": ttl, + } + + if jsonStructData[i].(map[string]interface{})["region_pools"] != nil { + original := jsonStructData[i].(map[string]interface{})["region_pools"] + jsonStructData[i].(map[string]interface{})["region_pools"] = []interface{}{} + + for region, popIDs := range original.(map[string]interface{}) { + jsonStructData[i].(map[string]interface{})["region_pools"] = append(jsonStructData[i].(map[string]interface{})["region_pools"].([]interface{}), map[string]interface{}{"region": region, "pool_ids": popIDs}) } + } - cache_ttl_by_status = append(cache_ttl_by_status, elem) + if jsonStructData[i].(map[string]interface{})["pop_pools"] != nil { + original := jsonStructData[i].(map[string]interface{})["pop_pools"] + jsonStructData[i].(map[string]interface{})["pop_pools"] = []interface{}{} + + for pop, popIDs := range original.(map[string]interface{}) { + jsonStructData[i].(map[string]interface{})["pop_pools"] = append(jsonStructData[i].(map[string]interface{})["pop_pools"].([]interface{}), map[string]interface{}{"pop": pop, "pool_ids": popIDs}) + } } + } - sort.SliceStable(cache_ttl_by_status, func(i int, j int) bool { - return cache_ttl_by_status[i]["codes"].(string) < cache_ttl_by_status[j]["codes"].(string) - }) + case "cloudflare_load_balancer_pool": + jsonPayload, err := api.ListLoadBalancerPools(context.Background(), identifier, cloudflare.ListLoadBalancerPoolParams{}) + if err != nil { + log.Fatal(err) + } - jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_ttl_by_status"] = cache_ttl_by_status + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } - // Remap cache_key_fields.query_string.include & .exclude wildcards (not in an array) to the appropriate "ignore" field value in Terraform. - if c, ok := jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{}); ok { - if s, sok := c["query_string"].(map[string]interface{})["include"].(string); sok && s == "*" { - jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["include"] = nil - jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["ignore"] = false + for i := 0; i < resourceCount; i++ { + for originCounter := range jsonStructData[i].(map[string]interface{})["origins"].([]interface{}) { + if jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"] != nil { + jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["header"] = "Host" + jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["values"] = jsonStructData[i].(map[string]interface{})["origins"].([]interface{})[originCounter].(map[string]interface{})["header"].(map[string]interface{})["Host"] + } } - if s, sok := c["query_string"].(map[string]interface{})["exclude"].(string); sok && s == "*" { - jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["exclude"] = nil - jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["ignore"] = true + } + case "cloudflare_load_balancer_monitor": + jsonPayload, err := api.ListLoadBalancerMonitors(context.Background(), identifier, cloudflare.ListLoadBalancerMonitorParams{}) + if err != nil { + log.Fatal(err) + } + + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_logpush_job": + jsonPayload, err := api.ListLogpushJobs(context.Background(), identifier, cloudflare.ListLogpushJobsParams{}) + if err != nil { + log.Fatal(err) + } + + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + + for i := 0; i < resourceCount; i++ { + // Workaround for LogpushJob.Filter being empty with a custom + // marshaler and returning `{"where":{}}` as the "empty" value. + if jsonStructData[i].(map[string]interface{})["filter"] == `{"where":{}}` { + jsonStructData[i].(map[string]interface{})["filter"] = nil } } - } - case "cloudflare_rate_limit": - jsonPayload, err := api.ListAllRateLimits(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + case "cloudflare_managed_headers": + // only grab the enabled headers + jsonPayload, err := api.ListZoneManagedHeaders(context.Background(), cloudflare.ResourceIdentifier(zoneID), cloudflare.ListManagedHeadersParams{Status: "enabled"}) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + var managedHeaders []cloudflare.ManagedHeaders + managedHeaders = append(managedHeaders, jsonPayload) - for i := 0; i < resourceCount; i++ { - var bypassItems []string + resourceCount = len(managedHeaders) + m, _ := json.Marshal(managedHeaders) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["id"] = zoneID + } + case "cloudflare_origin_ca_certificate": + jsonPayload, err := api.ListOriginCACertificates(context.Background(), cloudflare.ListOriginCertificatesParams{ZoneID: zoneID}) + if err != nil { + log.Fatal(err) + } + + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_page_rule": + jsonPayload, err := api.ListPageRules(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - // Remap match.request.url to match.request.url_pattern - jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["request"].(map[string]interface{})["url_pattern"] = jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["request"].(map[string]interface{})["url"] + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["target"] = jsonStructData[i].(map[string]interface{})["targets"].([]interface{})[0].(map[string]interface{})["constraint"].(map[string]interface{})["value"] + jsonStructData[i].(map[string]interface{})["actions"] = flattenAttrMap(jsonStructData[i].(map[string]interface{})["actions"].([]interface{})) + + // Have to remap the cache_ttl_by_status to conform to Terraform's more human-friendly structure. + if cache, ok := jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_ttl_by_status"].(map[string]interface{}); ok { + cache_ttl_by_status := []map[string]interface{}{} - // Remap bypass to bypass_url_patterns - if jsonStructData[i].(map[string]interface{})["bypass"] != nil { - for _, item := range jsonStructData[i].(map[string]interface{})["bypass"].([]interface{}) { - bypassItems = append(bypassItems, item.(map[string]interface{})["value"].(string)) + for codes, ttl := range cache { + if ttl == "no-cache" { + ttl = 0 + } else if ttl == "no-store" { + ttl = -1 + } + elem := map[string]interface{}{ + "codes": codes, + "ttl": ttl, + } + + cache_ttl_by_status = append(cache_ttl_by_status, elem) + } + + sort.SliceStable(cache_ttl_by_status, func(i int, j int) bool { + return cache_ttl_by_status[i]["codes"].(string) < cache_ttl_by_status[j]["codes"].(string) + }) + + jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_ttl_by_status"] = cache_ttl_by_status } - jsonStructData[i].(map[string]interface{})["bypass_url_patterns"] = bypassItems + + // Remap cache_key_fields.query_string.include & .exclude wildcards (not in an array) to the appropriate "ignore" field value in Terraform. + if c, ok := jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{}); ok { + if s, sok := c["query_string"].(map[string]interface{})["include"].(string); sok && s == "*" { + jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["include"] = nil + jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["ignore"] = false + } + if s, sok := c["query_string"].(map[string]interface{})["exclude"].(string); sok && s == "*" { + jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["exclude"] = nil + jsonStructData[i].(map[string]interface{})["actions"].(map[string]interface{})["cache_key_fields"].(map[string]interface{})["query_string"].(map[string]interface{})["ignore"] = true + } + } + } + case "cloudflare_rate_limit": + jsonPayload, err := api.ListAllRateLimits(context.Background(), zoneID) + if err != nil { + log.Fatal(err) } - // Remap match.response.status to match.response.statuses - jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["statuses"] = jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["status"] - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - case "cloudflare_record": - simpleDNSTypes := []string{"A", "AAAA", "CNAME", "TXT", "MX", "NS", "PTR"} - jsonPayload, _, err := api.ListDNSRecords(context.Background(), identifier, cloudflare.ListDNSRecordsParams{}) - if err != nil { - log.Fatal(err) - } + for i := 0; i < resourceCount; i++ { + var bypassItems []string - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // Remap match.request.url to match.request.url_pattern + jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["request"].(map[string]interface{})["url_pattern"] = jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["request"].(map[string]interface{})["url"] - for i := 0; i < resourceCount; i++ { - // Drop the proxiable values as they are not usable - jsonStructData[i].(map[string]interface{})["proxiable"] = nil + // Remap bypass to bypass_url_patterns + if jsonStructData[i].(map[string]interface{})["bypass"] != nil { + for _, item := range jsonStructData[i].(map[string]interface{})["bypass"].([]interface{}) { + bypassItems = append(bypassItems, item.(map[string]interface{})["value"].(string)) + } + jsonStructData[i].(map[string]interface{})["bypass_url_patterns"] = bypassItems + } - if jsonStructData[i].(map[string]interface{})["name"].(string) != jsonStructData[i].(map[string]interface{})["zone_name"].(string) { - jsonStructData[i].(map[string]interface{})["name"] = strings.ReplaceAll(jsonStructData[i].(map[string]interface{})["name"].(string), "."+jsonStructData[i].(map[string]interface{})["zone_name"].(string), "") + // Remap match.response.status to match.response.statuses + jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["statuses"] = jsonStructData[i].(map[string]interface{})["match"].(map[string]interface{})["response"].(map[string]interface{})["status"] } - // We only want to remap the "value" to the "content" value for simple - // DNS types as the aggregate types use `data` for the structure. - if contains(simpleDNSTypes, jsonStructData[i].(map[string]interface{})["type"].(string)) { - jsonStructData[i].(map[string]interface{})["value"] = jsonStructData[i].(map[string]interface{})["content"] + case "cloudflare_record": + simpleDNSTypes := []string{"A", "AAAA", "CNAME", "TXT", "MX", "NS", "PTR"} + jsonPayload, _, err := api.ListDNSRecords(context.Background(), identifier, cloudflare.ListDNSRecordsParams{}) + if err != nil { + log.Fatal(err) } - } - case "cloudflare_ruleset": - jsonPayload, err := api.ListRulesets(context.Background(), identifier, cloudflare.ListRulesetsParams{}) - if err != nil { - log.Fatal(err) - } - var nonManagedRules []cloudflare.Ruleset + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + + for i := 0; i < resourceCount; i++ { + // Drop the proxiable values as they are not usable + jsonStructData[i].(map[string]interface{})["proxiable"] = nil + + if jsonStructData[i].(map[string]interface{})["name"].(string) != jsonStructData[i].(map[string]interface{})["zone_name"].(string) { + jsonStructData[i].(map[string]interface{})["name"] = strings.ReplaceAll(jsonStructData[i].(map[string]interface{})["name"].(string), "."+jsonStructData[i].(map[string]interface{})["zone_name"].(string), "") + } - // A little annoying but makes more sense doing it this way. Only append - // the non-managed rules to the usable nonManagedRules variable instead - // of attempting to delete from an existing slice and just reassign. - for _, r := range jsonPayload { - if r.Kind != string(cloudflare.RulesetKindManaged) { - nonManagedRules = append(nonManagedRules, r) + // We only want to remap the "value" to the "content" value for simple + // DNS types as the aggregate types use `data` for the structure. + if contains(simpleDNSTypes, jsonStructData[i].(map[string]interface{})["type"].(string)) { + jsonStructData[i].(map[string]interface{})["value"] = jsonStructData[i].(map[string]interface{})["content"] + } } - } - jsonPayload = nonManagedRules - ruleHeaders := map[string][]map[string]interface{}{} - for i, rule := range nonManagedRules { - ruleset, _ := api.GetRuleset(context.Background(), identifier, rule.ID) - jsonPayload[i].Rules = ruleset.Rules - - if ruleset.Rules != nil { - for _, rule := range ruleset.Rules { - if rule.ActionParameters != nil && rule.ActionParameters.Headers != nil { - // Sort the headers to have deterministic config output - keys := make([]string, 0, len(rule.ActionParameters.Headers)) - for k := range rule.ActionParameters.Headers { - keys = append(keys, k) - } - sort.Strings(keys) - - // The structure of the API response for headers differs from the - // structure terraform requires. So we collect all the headers - // indexed by rule.ID to massage the jsonStructData later - for _, headerName := range keys { - header := map[string]interface{}{ - "name": headerName, - "operation": rule.ActionParameters.Headers[headerName].Operation, - "expression": rule.ActionParameters.Headers[headerName].Expression, - "value": rule.ActionParameters.Headers[headerName].Value, + case "cloudflare_ruleset": + jsonPayload, err := api.ListRulesets(context.Background(), identifier, cloudflare.ListRulesetsParams{}) + if err != nil { + log.Fatal(err) + } + + var nonManagedRules []cloudflare.Ruleset + + // A little annoying but makes more sense doing it this way. Only append + // the non-managed rules to the usable nonManagedRules variable instead + // of attempting to delete from an existing slice and just reassign. + for _, r := range jsonPayload { + if r.Kind != string(cloudflare.RulesetKindManaged) { + nonManagedRules = append(nonManagedRules, r) + } + } + jsonPayload = nonManagedRules + ruleHeaders := map[string][]map[string]interface{}{} + for i, rule := range nonManagedRules { + ruleset, _ := api.GetRuleset(context.Background(), identifier, rule.ID) + jsonPayload[i].Rules = ruleset.Rules + + if ruleset.Rules != nil { + for _, rule := range ruleset.Rules { + if rule.ActionParameters != nil && rule.ActionParameters.Headers != nil { + // Sort the headers to have deterministic config output + keys := make([]string, 0, len(rule.ActionParameters.Headers)) + for k := range rule.ActionParameters.Headers { + keys = append(keys, k) + } + sort.Strings(keys) + + // The structure of the API response for headers differs from the + // structure terraform requires. So we collect all the headers + // indexed by rule.ID to massage the jsonStructData later + for _, headerName := range keys { + header := map[string]interface{}{ + "name": headerName, + "operation": rule.ActionParameters.Headers[headerName].Operation, + "expression": rule.ActionParameters.Headers[headerName].Expression, + "value": rule.ActionParameters.Headers[headerName].Value, + } + ruleHeaders[rule.ID] = append(ruleHeaders[rule.ID], header) } - ruleHeaders[rule.ID] = append(ruleHeaders[rule.ID], header) } } } } - } - sort.Slice(jsonPayload, func(i, j int) bool { - return jsonPayload[i].Phase < jsonPayload[j].Phase - }) + sort.Slice(jsonPayload, func(i, j int) bool { + return jsonPayload[i].Phase < jsonPayload[j].Phase + }) - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - // Make the rules have the correct header structure - for i, ruleset := range jsonStructData { - if ruleset.(map[string]interface{})["rules"] != nil { - for j, rule := range ruleset.(map[string]interface{})["rules"].([]interface{}) { - ID := rule.(map[string]interface{})["id"] - if ID != nil { - headers, exists := ruleHeaders[ID.(string)] - if exists { - jsonStructData[i].(map[string]interface{})["rules"].([]interface{})[j].(map[string]interface{})["action_parameters"].(map[string]interface{})["headers"] = headers + // Make the rules have the correct header structure + for i, ruleset := range jsonStructData { + if ruleset.(map[string]interface{})["rules"] != nil { + for j, rule := range ruleset.(map[string]interface{})["rules"].([]interface{}) { + ID := rule.(map[string]interface{})["id"] + if ID != nil { + headers, exists := ruleHeaders[ID.(string)] + if exists { + jsonStructData[i].(map[string]interface{})["rules"].([]interface{})[j].(map[string]interface{})["action_parameters"].(map[string]interface{})["headers"] = headers + } } } } } - } - - // log custom fields specific transformation fields - logCustomFieldsTransform := []string{"cookie_fields", "request_fields", "response_fields"} - for i := 0; i < resourceCount; i++ { - rules := jsonStructData[i].(map[string]interface{})["rules"] - if rules != nil { - for ruleCounter := range rules.([]interface{}) { - // should the `ref` be the default `id`, don't output it - // as we don't need to track a computed default. - id := rules.([]interface{})[ruleCounter].(map[string]interface{})["id"] - ref := rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"] - if id == ref { - rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"] = nil - } + // log custom fields specific transformation fields + logCustomFieldsTransform := []string{"cookie_fields", "request_fields", "response_fields"} + + for i := 0; i < resourceCount; i++ { + rules := jsonStructData[i].(map[string]interface{})["rules"] + if rules != nil { + for ruleCounter := range rules.([]interface{}) { + // should the `ref` be the default `id`, don't output it + // as we don't need to track a computed default. + id := rules.([]interface{})[ruleCounter].(map[string]interface{})["id"] + ref := rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"] + if id == ref { + rules.([]interface{})[ruleCounter].(map[string]interface{})["ref"] = nil + } - actionParams := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"] - if actionParams != nil { - // check for log custom fields that need to be transformed - for _, logCustomFields := range logCustomFieldsTransform { - // check if the field exists and make sure it has at least one element - if actionParams.(map[string]interface{})[logCustomFields] != nil && len(actionParams.(map[string]interface{})[logCustomFields].([]interface{})) > 0 { - // Create a new list to store the data in. - var newLogCustomFields []interface{} - // iterate over each of the keys and add them to a generic list - for logCustomFieldsCounter := range actionParams.(map[string]interface{})[logCustomFields].([]interface{}) { - newLogCustomFields = append(newLogCustomFields, actionParams.(map[string]interface{})[logCustomFields].([]interface{})[logCustomFieldsCounter].(map[string]interface{})["name"]) + actionParams := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"] + if actionParams != nil { + // check for log custom fields that need to be transformed + for _, logCustomFields := range logCustomFieldsTransform { + // check if the field exists and make sure it has at least one element + if actionParams.(map[string]interface{})[logCustomFields] != nil && len(actionParams.(map[string]interface{})[logCustomFields].([]interface{})) > 0 { + // Create a new list to store the data in. + var newLogCustomFields []interface{} + // iterate over each of the keys and add them to a generic list + for logCustomFieldsCounter := range actionParams.(map[string]interface{})[logCustomFields].([]interface{}) { + newLogCustomFields = append(newLogCustomFields, actionParams.(map[string]interface{})[logCustomFields].([]interface{})[logCustomFieldsCounter].(map[string]interface{})["name"]) + } + actionParams.(map[string]interface{})[logCustomFields] = newLogCustomFields } - actionParams.(map[string]interface{})[logCustomFields] = newLogCustomFields } - } - // check if our ruleset is of action 'skip' - if rules.([]interface{})[ruleCounter].(map[string]interface{})["action"] == "skip" { - for rule := range actionParams.(map[string]interface{}) { - // "rules" is the only map[string][]string we need to remap. The others are all []string and are handled naturally. - if rule == "rules" { - for key, value := range actionParams.(map[string]interface{})[rule].(map[string]interface{}) { - var rulesList []string - for _, val := range value.([]interface{}) { - rulesList = append(rulesList, val.(string)) + // check if our ruleset is of action 'skip' + if rules.([]interface{})[ruleCounter].(map[string]interface{})["action"] == "skip" { + for rule := range actionParams.(map[string]interface{}) { + // "rules" is the only map[string][]string we need to remap. The others are all []string and are handled naturally. + if rule == "rules" { + for key, value := range actionParams.(map[string]interface{})[rule].(map[string]interface{}) { + var rulesList []string + for _, val := range value.([]interface{}) { + rulesList = append(rulesList, val.(string)) + } + actionParams.(map[string]interface{})[rule].(map[string]interface{})[key] = strings.Join(rulesList, ",") } - actionParams.(map[string]interface{})[rule].(map[string]interface{})[key] = strings.Join(rulesList, ",") } } } - } - // Cache Rules transformation - if jsonStructData[i].(map[string]interface{})["phase"] == "http_request_cache_settings" { - if ck, ok := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"]; ok { - if c, cok := ck.(map[string]interface{})["custom_key"]; cok { - if qs, qok := c.(map[string]interface{})["query_string"]; qok { - if s, sok := qs.(map[string]interface{})["include"]; sok && s == "*" { - rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["include"] = []interface{}{"*"} - } - if s, sok := qs.(map[string]interface{})["exclude"]; sok && s == "*" { - rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["exclude"] = []interface{}{"*"} + // Cache Rules transformation + if jsonStructData[i].(map[string]interface{})["phase"] == "http_request_cache_settings" { + if ck, ok := rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"]; ok { + if c, cok := ck.(map[string]interface{})["custom_key"]; cok { + if qs, qok := c.(map[string]interface{})["query_string"]; qok { + if s, sok := qs.(map[string]interface{})["include"]; sok && s == "*" { + rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["include"] = []interface{}{"*"} + } + if s, sok := qs.(map[string]interface{})["exclude"]; sok && s == "*" { + rules.([]interface{})[ruleCounter].(map[string]interface{})["action_parameters"].(map[string]interface{})["cache_key"].(map[string]interface{})["custom_key"].(map[string]interface{})["query_string"].(map[string]interface{})["exclude"] = []interface{}{"*"} + } } } } @@ -864,394 +867,394 @@ func generateResources() func(cmd *cobra.Command, args []string) { } } } - } - case "cloudflare_spectrum_application": - jsonPayload, err := api.SpectrumApplications(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_tunnel": - log.Debug("only requesting the first 1000 active Cloudflare Tunnels due to the service not providing correct pagination responses") - jsonPayload, _, err := api.ListTunnels( - context.Background(), - cloudflare.AccountIdentifier(accountID), - cloudflare.TunnelListParams{ - IsDeleted: cloudflare.BoolPtr(false), - ResultInfo: cloudflare.ResultInfo{ - PerPage: 1000, - Page: 1, - }, - }) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + case "cloudflare_spectrum_application": + jsonPayload, err := api.SpectrumApplications(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - for i := 0; i < resourceCount; i++ { - secret, err := api.GetTunnelToken( + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_tunnel": + log.Debug("only requesting the first 1000 active Cloudflare Tunnels due to the service not providing correct pagination responses") + jsonPayload, _, err := api.ListTunnels( context.Background(), cloudflare.AccountIdentifier(accountID), - jsonStructData[i].(map[string]interface{})["id"].(string), - ) + cloudflare.TunnelListParams{ + IsDeleted: cloudflare.BoolPtr(false), + ResultInfo: cloudflare.ResultInfo{ + PerPage: 1000, + Page: 1, + }, + }) if err != nil { log.Fatal(err) } - jsonStructData[i].(map[string]interface{})["secret"] = secret - jsonStructData[i].(map[string]interface{})["account_id"] = accountID - jsonStructData[i].(map[string]interface{})["connections"] = nil - } - case "cloudflare_turnstile_widget": - jsonPayload, _, err := api.ListTurnstileWidgets(context.Background(), identifier, cloudflare.ListTurnstileWidgetParams{}) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + for i := 0; i < resourceCount; i++ { + secret, err := api.GetTunnelToken( + context.Background(), + cloudflare.AccountIdentifier(accountID), + jsonStructData[i].(map[string]interface{})["id"].(string), + ) + if err != nil { + log.Fatal(err) + } + jsonStructData[i].(map[string]interface{})["secret"] = secret + jsonStructData[i].(map[string]interface{})["account_id"] = accountID - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["id"] = jsonStructData[i].(map[string]interface{})["sitekey"] - } - case "cloudflare_url_normalization_settings": - jsonPayload, err := api.URLNormalizationSettings(context.Background(), &cloudflare.ResourceContainer{Identifier: zoneID, Level: cloudflare.ZoneRouteLevel}) - if err != nil { - log.Fatal(err) - } - var newJsonPayload []interface{} - newJsonPayload = append(newJsonPayload, jsonPayload) - resourceCount = len(newJsonPayload) - m, _ := json.Marshal(newJsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + jsonStructData[i].(map[string]interface{})["connections"] = nil + } + case "cloudflare_turnstile_widget": + jsonPayload, _, err := api.ListTurnstileWidgets(context.Background(), identifier, cloudflare.ListTurnstileWidgetParams{}) + if err != nil { + log.Fatal(err) + } - // this is only every a 1:1 so we can just verify if the 0th element has they key we expect - jsonStructData[0].(map[string]interface{})["id"] = zoneID - case "cloudflare_waiting_room": - jsonPayload, err := api.ListWaitingRooms(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - for i := 0; i < resourceCount; i++ { - if jsonStructData[i].(map[string]interface{})["queueing_status_code"].(float64) == 0 { - jsonStructData[i].(map[string]interface{})["queueing_status_code"] = nil + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["id"] = jsonStructData[i].(map[string]interface{})["sitekey"] } - } - case "cloudflare_waiting_room_event": - waitingRooms, err := api.ListWaitingRooms(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - for i := 0; i < len(waitingRooms); i++ { - roomEvents, err := api.ListWaitingRoomEvents(context.Background(), zoneID, waitingRooms[i].ID) + case "cloudflare_url_normalization_settings": + jsonPayload, err := api.URLNormalizationSettings(context.Background(), &cloudflare.ResourceContainer{Identifier: zoneID, Level: cloudflare.ZoneRouteLevel}) + if err != nil { + log.Fatal(err) + } + var newJsonPayload []interface{} + newJsonPayload = append(newJsonPayload, jsonPayload) + resourceCount = len(newJsonPayload) + m, _ := json.Marshal(newJsonPayload) + err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - m, err := json.Marshal(roomEvents) + + // this is only every a 1:1 so we can just verify if the 0th element has they key we expect + jsonStructData[0].(map[string]interface{})["id"] = zoneID + case "cloudflare_waiting_room": + jsonPayload, err := api.ListWaitingRooms(context.Background(), zoneID) if err != nil { log.Fatal(err) } - jsonRoomEvents := []interface{}{} - err = json.Unmarshal(m, &jsonRoomEvents) + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - for i := 0; i < len(jsonRoomEvents); i++ { - jsonRoomEvents[i].(map[string]interface{})["waiting_room_id"] = waitingRooms[i].ID + + for i := 0; i < resourceCount; i++ { + if jsonStructData[i].(map[string]interface{})["queueing_status_code"].(float64) == 0 { + jsonStructData[i].(map[string]interface{})["queueing_status_code"] = nil + } } - jsonStructData = append(jsonStructData, jsonRoomEvents...) - } - resourceCount = len(jsonStructData) - case "cloudflare_waiting_room_rules": - waitingRooms, err := api.ListWaitingRooms(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - roomRules := []struct { - WaitingRoomID string `json:"waiting_room_id"` - Rules []cloudflare.WaitingRoomRule `json:"rules"` - }{} - for i := 0; i < len(waitingRooms); i++ { - rules, err := api.ListWaitingRoomRules(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListWaitingRoomRuleParams{ - WaitingRoomID: waitingRooms[i].ID, - }) + case "cloudflare_waiting_room_event": + waitingRooms, err := api.ListWaitingRooms(context.Background(), zoneID) if err != nil { log.Fatal(err) } - roomRules = append(roomRules, struct { + for i := 0; i < len(waitingRooms); i++ { + roomEvents, err := api.ListWaitingRoomEvents(context.Background(), zoneID, waitingRooms[i].ID) + if err != nil { + log.Fatal(err) + } + m, err := json.Marshal(roomEvents) + if err != nil { + log.Fatal(err) + } + jsonRoomEvents := []interface{}{} + err = json.Unmarshal(m, &jsonRoomEvents) + if err != nil { + log.Fatal(err) + } + for i := 0; i < len(jsonRoomEvents); i++ { + jsonRoomEvents[i].(map[string]interface{})["waiting_room_id"] = waitingRooms[i].ID + } + jsonStructData = append(jsonStructData, jsonRoomEvents...) + } + resourceCount = len(jsonStructData) + case "cloudflare_waiting_room_rules": + waitingRooms, err := api.ListWaitingRooms(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } + roomRules := []struct { WaitingRoomID string `json:"waiting_room_id"` Rules []cloudflare.WaitingRoomRule `json:"rules"` - }{ - WaitingRoomID: waitingRooms[i].ID, - Rules: rules, - }) - } - resourceCount = len(roomRules) - m, err := json.Marshal(roomRules) - if err != nil { - log.Fatal(err) - } - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_waiting_room_settings": - jsonPayload, err := api.GetWaitingRoomSettings(context.Background(), cloudflare.ZoneIdentifier(zoneID)) - if err != nil { - log.Fatal(err) - } - resourceCount = 1 - var jsonPayloadInterface interface{} - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonPayloadInterface) - if err != nil { - log.Fatal(err) - } - jsonStructData = []interface{}{jsonPayloadInterface} - case "cloudflare_workers_kv_namespace": - jsonPayload, _, err := api.ListWorkersKVNamespaces(context.Background(), identifier, cloudflare.ListWorkersKVNamespacesParams{}) - if err != nil { - log.Fatal(err) - } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_worker_route": - jsonPayload, err := api.ListWorkerRoutes(context.Background(), identifier, cloudflare.ListWorkerRoutesParams{}) - if err != nil { - log.Fatal(err) - } - resourceCount = len(jsonPayload.Routes) - m, _ := json.Marshal(jsonPayload.Routes) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - - // remap "script_name" to the "script" value. - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["script_name"] = jsonStructData[i].(map[string]interface{})["script"] - } - case "cloudflare_zone": - jsonPayload, err := api.ListZones(context.Background()) - if err != nil { - log.Fatal(err) - } - - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - - // - remap "zone" to the "name" value - // - remap "plan" to "legacy_id" value - // - drop meta and name_servers - // - pull in the account_id field - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["zone"] = jsonStructData[i].(map[string]interface{})["name"] - jsonStructData[i].(map[string]interface{})["plan"] = jsonStructData[i].(map[string]interface{})["plan"].(map[string]interface{})["legacy_id"].(string) - jsonStructData[i].(map[string]interface{})["meta"] = nil - jsonStructData[i].(map[string]interface{})["name_servers"] = nil - jsonStructData[i].(map[string]interface{})["status"] = nil - jsonStructData[i].(map[string]interface{})["account_id"] = jsonStructData[i].(map[string]interface{})["account"].(map[string]interface{})["id"].(string) - } - case "cloudflare_zone_lockdown": - jsonPayload, _, err := api.ListZoneLockdowns(context.Background(), identifier, cloudflare.LockdownListParams{}) - if err != nil { - log.Fatal(err) - } + }{} + for i := 0; i < len(waitingRooms); i++ { + rules, err := api.ListWaitingRoomRules(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListWaitingRoomRuleParams{ + WaitingRoomID: waitingRooms[i].ID, + }) + if err != nil { + log.Fatal(err) + } + roomRules = append(roomRules, struct { + WaitingRoomID string `json:"waiting_room_id"` + Rules []cloudflare.WaitingRoomRule `json:"rules"` + }{ + WaitingRoomID: waitingRooms[i].ID, + Rules: rules, + }) + } + resourceCount = len(roomRules) + m, err := json.Marshal(roomRules) + if err != nil { + log.Fatal(err) + } + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_waiting_room_settings": + jsonPayload, err := api.GetWaitingRoomSettings(context.Background(), cloudflare.ZoneIdentifier(zoneID)) + if err != nil { + log.Fatal(err) + } + resourceCount = 1 + var jsonPayloadInterface interface{} + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonPayloadInterface) + if err != nil { + log.Fatal(err) + } + jsonStructData = []interface{}{jsonPayloadInterface} + case "cloudflare_workers_kv_namespace": + jsonPayload, _, err := api.ListWorkersKVNamespaces(context.Background(), identifier, cloudflare.ListWorkersKVNamespacesParams{}) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_worker_route": + jsonPayload, err := api.ListWorkerRoutes(context.Background(), identifier, cloudflare.ListWorkerRoutesParams{}) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload.Routes) + m, _ := json.Marshal(jsonPayload.Routes) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = len(jsonPayload) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // remap "script_name" to the "script" value. + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["script_name"] = jsonStructData[i].(map[string]interface{})["script"] + } + case "cloudflare_zone": + jsonPayload, err := api.ListZones(context.Background()) + if err != nil { + log.Fatal(err) + } - case "cloudflare_zone_settings_override": - jsonPayload, err := api.ZoneSettings(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - resourceCount = 1 - m, _ := json.Marshal(jsonPayload.Result) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // - remap "zone" to the "name" value + // - remap "plan" to "legacy_id" value + // - drop meta and name_servers + // - pull in the account_id field + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["zone"] = jsonStructData[i].(map[string]interface{})["name"] + jsonStructData[i].(map[string]interface{})["plan"] = jsonStructData[i].(map[string]interface{})["plan"].(map[string]interface{})["legacy_id"].(string) + jsonStructData[i].(map[string]interface{})["meta"] = nil + jsonStructData[i].(map[string]interface{})["name_servers"] = nil + jsonStructData[i].(map[string]interface{})["status"] = nil + jsonStructData[i].(map[string]interface{})["account_id"] = jsonStructData[i].(map[string]interface{})["account"].(map[string]interface{})["id"].(string) + } + case "cloudflare_zone_lockdown": + jsonPayload, _, err := api.ListZoneLockdowns(context.Background(), identifier, cloudflare.LockdownListParams{}) + if err != nil { + log.Fatal(err) + } - zoneSettingsStruct := make(map[string]interface{}) - for _, data := range jsonStructData { - keyName := data.(map[string]interface{})["id"].(string) - value := data.(map[string]interface{})["value"] - zoneSettingsStruct[keyName] = value - } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - // Remap all settings under "settings" block as well as some of the - // attributes that are not 1:1 with the API. - for i := 0; i < resourceCount; i++ { - jsonStructData[i].(map[string]interface{})["id"] = zoneID - jsonStructData[i].(map[string]interface{})["settings"] = zoneSettingsStruct + case "cloudflare_zone_settings_override": + jsonPayload, err := api.ZoneSettings(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - // zero RTT - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["zero_rtt"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["0rtt"] + resourceCount = 1 + m, _ := json.Marshal(jsonPayload.Result) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } - // Mobile subdomain redirects - if jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["mobile_redirect"].(map[string]interface{})["status"] == "off" { - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["mobile_redirect"] = nil + zoneSettingsStruct := make(map[string]interface{}) + for _, data := range jsonStructData { + keyName := data.(map[string]interface{})["id"].(string) + value := data.(map[string]interface{})["value"] + zoneSettingsStruct[keyName] = value } - // HSTS - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["enabled"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["enabled"] - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["include_subdomains"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["include_subdomains"] - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["max_age"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["max_age"] - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["preload"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["preload"] - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["nosniff"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["nosniff"] + // Remap all settings under "settings" block as well as some of the + // attributes that are not 1:1 with the API. + for i := 0; i < resourceCount; i++ { + jsonStructData[i].(map[string]interface{})["id"] = zoneID + jsonStructData[i].(map[string]interface{})["settings"] = zoneSettingsStruct - // tls_1_2_only is deprecated in favour of min_tls - jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["tls_1_2_only"] = nil - } - case "cloudflare_tiered_cache": - tieredCache, err := api.GetTieredCache(context.Background(), &cloudflare.ResourceContainer{Identifier: zoneID}) - if err != nil { - log.Fatal(err) - } - var jsonPayload []cloudflare.TieredCache - jsonPayload = append(jsonPayload, tieredCache) + // zero RTT + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["zero_rtt"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["0rtt"] - resourceCount = 1 - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + // Mobile subdomain redirects + if jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["mobile_redirect"].(map[string]interface{})["status"] == "off" { + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["mobile_redirect"] = nil + } - jsonStructData[0].(map[string]interface{})["id"] = zoneID - jsonStructData[0].(map[string]interface{})["cache_type"] = tieredCache.Type.String() - default: - fmt.Fprintf(cmd.OutOrStderr(), "%q is not yet supported for automatic generation", resourceType) - return - } - // If we don't have any resources to generate, just bail out early. - if resourceCount == 0 { - fmt.Fprint(cmd.OutOrStderr(), "no resources found to generate") - return - } + // HSTS + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["enabled"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["enabled"] + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["include_subdomains"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["include_subdomains"] + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["max_age"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["max_age"] + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["preload"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["preload"] + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["nosniff"] = jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["security_header"].(map[string]interface{})["strict_transport_security"].(map[string]interface{})["nosniff"] - f := hclwrite.NewEmptyFile() - rootBody := f.Body() - for i := 0; i < resourceCount; i++ { - structData := jsonStructData[i].(map[string]interface{}) + // tls_1_2_only is deprecated in favour of min_tls + jsonStructData[i].(map[string]interface{})["settings"].(map[string]interface{})["tls_1_2_only"] = nil + } + case "cloudflare_tiered_cache": + tieredCache, err := api.GetTieredCache(context.Background(), &cloudflare.ResourceContainer{Identifier: zoneID}) + if err != nil { + log.Fatal(err) + } + var jsonPayload []cloudflare.TieredCache + jsonPayload = append(jsonPayload, tieredCache) - resourceID := "" - if os.Getenv("USE_STATIC_RESOURCE_IDS") == "true" { - resourceID = "terraform_managed_resource" - } else { - id := "" - switch structData["id"].(type) { - case float64: - id = fmt.Sprintf("%f", structData["id"].(float64)) - default: - id = structData["id"].(string) + resourceCount = 1 + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) } - resourceID = fmt.Sprintf("terraform_managed_resource_%s", id) + jsonStructData[0].(map[string]interface{})["id"] = zoneID + jsonStructData[0].(map[string]interface{})["cache_type"] = tieredCache.Type.String() + default: + fmt.Fprintf(cmd.OutOrStderr(), "%q is not yet supported for automatic generation", resourceType) + return } - resource := rootBody.AppendNewBlock("resource", []string{resourceType, resourceID}).Body() - - sortedBlockAttributes := make([]string, 0, len(r.Block.Attributes)) - for k := range r.Block.Attributes { - sortedBlockAttributes = append(sortedBlockAttributes, k) + // If we don't have any resources to generate, just bail out early. + if resourceCount == 0 { + fmt.Fprint(cmd.OutOrStderr(), "no resources found to generate") + return } - sort.Strings(sortedBlockAttributes) - // Block attributes are for any attributes where assignment is involved. - for _, attrName := range sortedBlockAttributes { - // Don't bother outputting the ID for the resource as that is only for - // internal use (such as importing state). - if attrName == "id" { - continue - } + f := hclwrite.NewEmptyFile() + rootBody := f.Body() + for i := 0; i < resourceCount; i++ { + structData := jsonStructData[i].(map[string]interface{}) - // No need to output computed attributes that are also not - // optional. - if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional { - continue - } - if attrName == "account_id" && accountID != "" { - writeAttrLine(attrName, accountID, "", resource) - continue + resourceID := "" + if os.Getenv("USE_STATIC_RESOURCE_IDS") == "true" { + resourceID = "terraform_managed_resource" + } else { + id := "" + switch structData["id"].(type) { + case float64: + id = fmt.Sprintf("%f", structData["id"].(float64)) + default: + id = structData["id"].(string) + } + + resourceID = fmt.Sprintf("terraform_managed_resource_%s", id) } + resource := rootBody.AppendNewBlock("resource", []string{resourceType, resourceID}).Body() - if attrName == "zone_id" && zoneID != "" && accountID == "" { - writeAttrLine(attrName, zoneID, "", resource) - continue + sortedBlockAttributes := make([]string, 0, len(r.Block.Attributes)) + for k := range r.Block.Attributes { + sortedBlockAttributes = append(sortedBlockAttributes, k) } + sort.Strings(sortedBlockAttributes) + + // Block attributes are for any attributes where assignment is involved. + for _, attrName := range sortedBlockAttributes { + // Don't bother outputting the ID for the resource as that is only for + // internal use (such as importing state). + if attrName == "id" { + continue + } - ty := r.Block.Attributes[attrName].AttributeType - switch { - case ty.IsPrimitiveType(): - switch ty { - case cty.String, cty.Bool, cty.Number: - writeAttrLine(attrName, structData[attrName], "", resource) - delete(structData, attrName) - default: - log.Debugf("unexpected primitive type %q", ty.FriendlyName()) + // No need to output computed attributes that are also not + // optional. + if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional { + continue + } + if attrName == "account_id" && accountID != "" { + writeAttrLine(attrName, accountID, "", resource) + continue } - case ty.IsCollectionType(): + + if attrName == "zone_id" && zoneID != "" && accountID == "" { + writeAttrLine(attrName, zoneID, "", resource) + continue + } + + ty := r.Block.Attributes[attrName].AttributeType switch { - case ty.IsListType(), ty.IsSetType(), ty.IsMapType(): - writeAttrLine(attrName, structData[attrName], "", resource) - delete(structData, attrName) + case ty.IsPrimitiveType(): + switch ty { + case cty.String, cty.Bool, cty.Number: + writeAttrLine(attrName, structData[attrName], "", resource) + delete(structData, attrName) + default: + log.Debugf("unexpected primitive type %q", ty.FriendlyName()) + } + case ty.IsCollectionType(): + switch { + case ty.IsListType(), ty.IsSetType(), ty.IsMapType(): + writeAttrLine(attrName, structData[attrName], "", resource) + delete(structData, attrName) + default: + log.Debugf("unexpected collection type %q", ty.FriendlyName()) + } + case ty.IsTupleType(): + fmt.Printf("tuple found. attrName %s\n", attrName) + case ty.IsObjectType(): + fmt.Printf("object found. attrName %s\n", attrName) default: - log.Debugf("unexpected collection type %q", ty.FriendlyName()) + log.Debugf("attribute %q (attribute type of %q) has not been generated", attrName, ty.FriendlyName()) } - case ty.IsTupleType(): - fmt.Printf("tuple found. attrName %s\n", attrName) - case ty.IsObjectType(): - fmt.Printf("object found. attrName %s\n", attrName) - default: - log.Debugf("attribute %q (attribute type of %q) has not been generated", attrName, ty.FriendlyName()) } + + processBlocks(r.Block, jsonStructData[i].(map[string]interface{}), resource, "") + f.Body().AppendNewline() } - processBlocks(r.Block, jsonStructData[i].(map[string]interface{}), resource, "") - f.Body().AppendNewline() + tfOutput := string(hclwrite.Format(f.Bytes())) + fmt.Fprint(cmd.OutOrStdout(), tfOutput) } - - tfOutput := string(hclwrite.Format(f.Bytes())) - fmt.Fprint(cmd.OutOrStdout(), tfOutput) } } diff --git a/internal/app/cf-terraforming/cmd/import.go b/internal/app/cf-terraforming/cmd/import.go index fe6d91d87..d02dc5236 100644 --- a/internal/app/cf-terraforming/cmd/import.go +++ b/internal/app/cf-terraforming/cmd/import.go @@ -72,109 +72,235 @@ func runImport() func(cmd *cobra.Command, args []string) { identifier = cloudflare.ZoneIdentifier(zoneID) } - switch resourceType { - case "cloudflare_access_group": - jsonPayload, _, err := api.ListAccessGroups(context.Background(), identifier, cloudflare.ListAccessGroupsParams{}) - if err != nil { - log.Fatal(err) - } - - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_access_rule": - if accountID != "" { - jsonPayload, err := api.ListAccountAccessRules(context.Background(), accountID, cloudflare.AccessRule{}, 1) + resources := strings.Split(resourceType, ",") + for _, resourceType := range resources { + switch resourceType { + case "cloudflare_access_group": + jsonPayload, _, err := api.ListAccessGroups(context.Background(), identifier, cloudflare.ListAccessGroupsParams{}) if err != nil { log.Fatal(err) } - m, _ := json.Marshal(jsonPayload.Result) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_access_rule": + if accountID != "" { + jsonPayload, err := api.ListAccountAccessRules(context.Background(), accountID, cloudflare.AccessRule{}, 1) + if err != nil { + log.Fatal(err) + } + + m, _ := json.Marshal(jsonPayload.Result) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } else { + jsonPayload, err := api.ListZoneAccessRules(context.Background(), zoneID, cloudflare.AccessRule{}, 1) + if err != nil { + log.Fatal(err) + } + + m, _ := json.Marshal(jsonPayload.Result) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } + case "cloudflare_account_member": + jsonPayload, _, err := api.AccountMembers(context.Background(), accountID, cloudflare.PaginationOptions{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - } else { - jsonPayload, err := api.ListZoneAccessRules(context.Background(), zoneID, cloudflare.AccessRule{}, 1) + case "cloudflare_argo": + jsonPayload := []cloudflare.ArgoFeatureSetting{{ + ID: fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String()))), + }} + + m, _ := json.Marshal(jsonPayload) + err := json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_bot_management": + botManagement, err := api.GetBotManagement(context.Background(), identifier) if err != nil { log.Fatal(err) } + var jsonPayload []cloudflare.BotManagement + jsonPayload = append(jsonPayload, botManagement) - m, _ := json.Marshal(jsonPayload.Result) + m, _ := json.Marshal(jsonPayload) err = json.Unmarshal(m, &jsonStructData) if err != nil { log.Fatal(err) } - } - case "cloudflare_account_member": - jsonPayload, _, err := api.AccountMembers(context.Background(), accountID, cloudflare.PaginationOptions{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_argo": - jsonPayload := []cloudflare.ArgoFeatureSetting{{ - ID: fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String()))), - }} - - m, _ := json.Marshal(jsonPayload) - err := json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_bot_management": - botManagement, err := api.GetBotManagement(context.Background(), identifier) - if err != nil { - log.Fatal(err) - } - var jsonPayload []cloudflare.BotManagement - jsonPayload = append(jsonPayload, botManagement) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } + jsonStructData[0].(map[string]interface{})["id"] = zoneID + case "cloudflare_byo_ip_prefix": + jsonPayload, err := api.ListPrefixes(context.Background(), accountID) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_certificate_pack": + jsonPayload, err := api.ListCertificatePacks(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - jsonStructData[0].(map[string]interface{})["id"] = zoneID - case "cloudflare_byo_ip_prefix": - jsonPayload, err := api.ListPrefixes(context.Background(), accountID) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_certificate_pack": - jsonPayload, err := api.ListCertificatePacks(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + var customerManagedCertificates []cloudflare.CertificatePack + for _, r := range jsonPayload { + if r.Type != "universal" { + customerManagedCertificates = append(customerManagedCertificates, r) + } + } + jsonPayload = customerManagedCertificates - var customerManagedCertificates []cloudflare.CertificatePack - for _, r := range jsonPayload { - if r.Type != "universal" { - customerManagedCertificates = append(customerManagedCertificates, r) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_custom_pages": + if accountID != "" { + jsonPayload, err := api.CustomPages(context.Background(), &cloudflare.CustomPageOptions{AccountID: accountID}) + if err != nil { + log.Fatal(err) + } + + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } else { + jsonPayload, err := api.CustomPages(context.Background(), &cloudflare.CustomPageOptions{ZoneID: zoneID}) + if err != nil { + log.Fatal(err) + } + + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + } + case "cloudflare_filter": + jsonPayload, _, err := api.Filters(context.Background(), identifier, cloudflare.FilterListParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_firewall_rule": + jsonPayload, _, err := api.FirewallRules(context.Background(), identifier, cloudflare.FirewallRuleListParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_healthcheck": + jsonPayload, err := api.Healthchecks(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_custom_hostname": + jsonPayload, _, err := api.CustomHostnames(context.Background(), zoneID, 1, cloudflare.CustomHostname{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_custom_ssl": + jsonPayload, err := api.ListSSL(context.Background(), zoneID) + if err != nil { + log.Fatal(err) } - } - jsonPayload = customerManagedCertificates - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_custom_pages": - if accountID != "" { - jsonPayload, err := api.CustomPages(context.Background(), &cloudflare.CustomPageOptions{AccountID: accountID}) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_ip_list": + jsonPayload, err := api.ListIPLists(context.Background(), accountID) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_load_balancer": + jsonPayload, err := api.ListLoadBalancers(context.Background(), identifier, cloudflare.ListLoadBalancerParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_load_balancer_pool": + jsonPayload, err := api.ListLoadBalancerPools(context.Background(), identifier, cloudflare.ListLoadBalancerPoolParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_load_balancer_monitor": + jsonPayload, err := api.ListLoadBalancerMonitors(context.Background(), identifier, cloudflare.ListLoadBalancerMonitorParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_logpush_job": + jsonPayload, err := api.ListLogpushJobs(context.Background(), identifier, cloudflare.ListLogpushJobsParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_origin_ca_certificate": + jsonPayload, err := api.ListOriginCACertificates(context.Background(), cloudflare.ListOriginCertificatesParams{ZoneID: zoneID}) if err != nil { log.Fatal(err) } @@ -184,8 +310,8 @@ func runImport() func(cmd *cobra.Command, args []string) { if err != nil { log.Fatal(err) } - } else { - jsonPayload, err := api.CustomPages(context.Background(), &cloudflare.CustomPageOptions{ZoneID: zoneID}) + case "cloudflare_page_rule": + jsonPayload, err := api.ListPageRules(context.Background(), zoneID) if err != nil { log.Fatal(err) } @@ -195,319 +321,196 @@ func runImport() func(cmd *cobra.Command, args []string) { if err != nil { log.Fatal(err) } - } - case "cloudflare_filter": - jsonPayload, _, err := api.Filters(context.Background(), identifier, cloudflare.FilterListParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_firewall_rule": - jsonPayload, _, err := api.FirewallRules(context.Background(), identifier, cloudflare.FirewallRuleListParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_healthcheck": - jsonPayload, err := api.Healthchecks(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_custom_hostname": - jsonPayload, _, err := api.CustomHostnames(context.Background(), zoneID, 1, cloudflare.CustomHostname{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_custom_ssl": - jsonPayload, err := api.ListSSL(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + case "cloudflare_rate_limit": + jsonPayload, err := api.ListAllRateLimits(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_ip_list": - jsonPayload, err := api.ListIPLists(context.Background(), accountID) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_load_balancer": - jsonPayload, err := api.ListLoadBalancers(context.Background(), identifier, cloudflare.ListLoadBalancerParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_load_balancer_pool": - jsonPayload, err := api.ListLoadBalancerPools(context.Background(), identifier, cloudflare.ListLoadBalancerPoolParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_load_balancer_monitor": - jsonPayload, err := api.ListLoadBalancerMonitors(context.Background(), identifier, cloudflare.ListLoadBalancerMonitorParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_logpush_job": - jsonPayload, err := api.ListLogpushJobs(context.Background(), identifier, cloudflare.ListLogpushJobsParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_origin_ca_certificate": - jsonPayload, err := api.ListOriginCACertificates(context.Background(), cloudflare.ListOriginCertificatesParams{ZoneID: zoneID}) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_record": + jsonPayload, _, err := api.ListDNSRecords(context.Background(), identifier, cloudflare.ListDNSRecordsParams{}) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_ruleset": + jsonPayload, err := api.ListRulesets(context.Background(), identifier, cloudflare.ListRulesetsParams{}) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_page_rule": - jsonPayload, err := api.ListPageRules(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + // Customers can read-only Managed Rulesets, so we don't want to + // have them try to import something they can't manage with terraform + var nonManagedRules []cloudflare.Ruleset + for _, r := range jsonPayload { + if r.Kind != string(cloudflare.RulesetKindManaged) { + nonManagedRules = append(nonManagedRules, r) + } + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_rate_limit": - jsonPayload, err := api.ListAllRateLimits(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(nonManagedRules) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_spectrum_application": + jsonPayload, err := api.SpectrumApplications(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_record": - jsonPayload, _, err := api.ListDNSRecords(context.Background(), identifier, cloudflare.ListDNSRecordsParams{}) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_ruleset": - jsonPayload, err := api.ListRulesets(context.Background(), identifier, cloudflare.ListRulesetsParams{}) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_tunnel": + log.Debug("only requesting the first 1000 active Cloudflare Tunnels due to the service not providing correct pagination responses") + jsonPayload, _, err := api.ListTunnels( + context.Background(), + cloudflare.AccountIdentifier(accountID), + cloudflare.TunnelListParams{ + IsDeleted: cloudflare.BoolPtr(false), + ResultInfo: cloudflare.ResultInfo{ + PerPage: 1000, + Page: 1, + }, + }) + if err != nil { + log.Fatal(err) + } - // Customers can read-only Managed Rulesets, so we don't want to - // have them try to import something they can't manage with terraform - var nonManagedRules []cloudflare.Ruleset - for _, r := range jsonPayload { - if r.Kind != string(cloudflare.RulesetKindManaged) { - nonManagedRules = append(nonManagedRules, r) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_turnstile_widget": + jsonPayload, _, err := api.ListTurnstileWidgets(context.Background(), identifier, cloudflare.ListTurnstileWidgetParams{}) + if err != nil { + log.Fatal(err) } - } - m, _ := json.Marshal(nonManagedRules) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_spectrum_application": - jsonPayload, err := api.SpectrumApplications(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + for i := 0; i < len(jsonStructData); i++ { + jsonStructData[i].(map[string]interface{})["id"] = jsonStructData[i].(map[string]interface{})["sitekey"] + } + case "cloudflare_waf_override": + jsonPayload, err := api.ListWAFOverrides(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_tunnel": - log.Debug("only requesting the first 1000 active Cloudflare Tunnels due to the service not providing correct pagination responses") - jsonPayload, _, err := api.ListTunnels( - context.Background(), - cloudflare.AccountIdentifier(accountID), - cloudflare.TunnelListParams{ - IsDeleted: cloudflare.BoolPtr(false), - ResultInfo: cloudflare.ResultInfo{ - PerPage: 1000, - Page: 1, - }, - }) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_waf_package": + jsonPayload, err := api.ListWAFPackages(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_waiting_room": + jsonPayload, err := api.ListWaitingRooms(context.Background(), zoneID) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_workers_kv_namespace": + jsonPayload, _, err := api.ListWorkersKVNamespaces(context.Background(), identifier, cloudflare.ListWorkersKVNamespacesParams{}) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_turnstile_widget": - jsonPayload, _, err := api.ListTurnstileWidgets(context.Background(), identifier, cloudflare.ListTurnstileWidgetParams{}) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_worker_route": + jsonPayload, err := api.ListWorkerRoutes(context.Background(), identifier, cloudflare.ListWorkerRoutesParams{}) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - for i := 0; i < len(jsonStructData); i++ { - jsonStructData[i].(map[string]interface{})["id"] = jsonStructData[i].(map[string]interface{})["sitekey"] - } - case "cloudflare_waf_override": - jsonPayload, err := api.ListWAFOverrides(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } + m, _ := json.Marshal(jsonPayload.Routes) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_zone": + jsonPayload, err := api.ListZones(context.Background()) + if err != nil { + log.Fatal(err) + } + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + case "cloudflare_zone_lockdown": + jsonPayload, _, err := api.ListZoneLockdowns(context.Background(), identifier, cloudflare.LockdownListParams{}) + if err != nil { + log.Fatal(err) + } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_waf_package": - jsonPayload, err := api.ListWAFPackages(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_waiting_room": - jsonPayload, err := api.ListWaitingRooms(context.Background(), zoneID) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_workers_kv_namespace": - jsonPayload, _, err := api.ListWorkersKVNamespaces(context.Background(), identifier, cloudflare.ListWorkersKVNamespacesParams{}) - if err != nil { - log.Fatal(err) + m, _ := json.Marshal(jsonPayload) + err = json.Unmarshal(m, &jsonStructData) + if err != nil { + log.Fatal(err) + } + default: + fmt.Fprintf(cmd.OutOrStderr(), "%q is not yet supported for state import", resourceType) + return } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_worker_route": - jsonPayload, err := api.ListWorkerRoutes(context.Background(), identifier, cloudflare.ListWorkerRoutesParams{}) - if err != nil { - log.Fatal(err) - } + importFile := hclwrite.NewEmptyFile() + importBody := importFile.Body() - m, _ := json.Marshal(jsonPayload.Routes) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_zone": - jsonPayload, err := api.ListZones(context.Background()) - if err != nil { - log.Fatal(err) - } - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) - } - case "cloudflare_zone_lockdown": - jsonPayload, _, err := api.ListZoneLockdowns(context.Background(), identifier, cloudflare.LockdownListParams{}) - if err != nil { - log.Fatal(err) - } + for _, data := range jsonStructData { + id := data.(map[string]interface{})["id"].(string) - m, _ := json.Marshal(jsonPayload) - err = json.Unmarshal(m, &jsonStructData) - if err != nil { - log.Fatal(err) + if useModernImportBlock { + idvalue := buildRawImportAddress(resourceType, id) + imp := importBody.AppendNewBlock("import", []string{}).Body() + imp.SetAttributeRaw("to", hclwrite.TokensForIdentifier(fmt.Sprintf("%s.%s", resourceType, fmt.Sprintf("%s_%s", terraformResourceNamePrefix, id)))) + imp.SetAttributeValue("id", cty.StringVal(idvalue)) + importFile.Body().AppendNewline() + } else { + fmt.Fprint(cmd.OutOrStdout(), buildTerraformImportCommand(resourceType, id)) + } } - default: - fmt.Fprintf(cmd.OutOrStderr(), "%q is not yet supported for state import", resourceType) - return - } - - importFile := hclwrite.NewEmptyFile() - importBody := importFile.Body() - - for _, data := range jsonStructData { - id := data.(map[string]interface{})["id"].(string) if useModernImportBlock { - idvalue := buildRawImportAddress(resourceType, id) - imp := importBody.AppendNewBlock("import", []string{}).Body() - imp.SetAttributeRaw("to", hclwrite.TokensForIdentifier(fmt.Sprintf("%s.%s", resourceType, fmt.Sprintf("%s_%s", terraformResourceNamePrefix, id)))) - imp.SetAttributeValue("id", cty.StringVal(idvalue)) - importFile.Body().AppendNewline() - } else { - fmt.Fprint(cmd.OutOrStdout(), buildTerraformImportCommand(resourceType, id)) + // don't format the output; there is a bug in hclwrite.Format that + // splits incorrectly on certain characters. instead, manually + // insert new lines on the block. + fmt.Fprint(cmd.OutOrStdout(), string(importFile.Bytes())) } } - - if useModernImportBlock { - // don't format the output; there is a bug in hclwrite.Format that - // splits incorrectly on certain characters. instead, manually - // insert new lines on the block. - fmt.Fprint(cmd.OutOrStdout(), string(importFile.Bytes())) - } } } diff --git a/internal/app/cf-terraforming/cmd/root.go b/internal/app/cf-terraforming/cmd/root.go index 1a6b827cd..74b2faa11 100644 --- a/internal/app/cf-terraforming/cmd/root.go +++ b/internal/app/cf-terraforming/cmd/root.go @@ -44,10 +44,10 @@ func init() { rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", home+"/.cf-terraforming.yaml", "Path to config file") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Specify verbose output (same as setting log level to debug)") - rootCmd.PersistentFlags().StringVar(&resourceType, "resource-type", "", "Which resource you wish to generate") + rootCmd.PersistentFlags().StringVar(&resourceType, "resource-type", "", "Comma delimitered string of which resource(s) you wish to generate") 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+") - rootCmd.PersistentFlags().StringVarP(&zoneID, "zone", "z", "", "Limit the export to a single zone ID") + rootCmd.PersistentFlags().StringVarP(&zoneID, "zone", "z", "", "Target the provided zone ID for the command") if err = viper.BindPFlag("zone", rootCmd.PersistentFlags().Lookup("zone")); err != nil { log.Fatal(err) } @@ -55,7 +55,7 @@ func init() { log.Fatal(err) } - rootCmd.PersistentFlags().StringVarP(&accountID, "account", "a", "", "Use specific account ID for commands") + rootCmd.PersistentFlags().StringVarP(&accountID, "account", "a", "", "Target the provided account ID for the command") if err = viper.BindPFlag("account", rootCmd.PersistentFlags().Lookup("account")); err != nil { log.Fatal(err) }