diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e3184fd..e575fe9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,6 +17,7 @@ on: schedule: - cron: '0 13 * * 1-5' # UTC + workflow_dispatch: jobs: test: diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d9c66..220dded 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.3.3 (October 18, 2023) + +### Notes + +- Release date: **(August 18, 2023)** +- Supported Terraform version: **v1.x.x** + +### Bug Fixes + +- [PR #139](https://github.com/zscaler/zscaler-terraformer/pull/139) Fixed ZIA ``zia_location_management`` resource to ensure sub-locations are also imported. + ## 0.3.2 (October 3, 2023) ### Notes diff --git a/docs/guides/release-notes.md b/docs/guides/release-notes.md index 0eb3e87..ba87694 100644 --- a/docs/guides/release-notes.md +++ b/docs/guides/release-notes.md @@ -12,10 +12,32 @@ Track all Zscaler Terraformer Tool releases. New resources, features, and bug fi --- -``Last updated: v0.3.1`` +``Last updated: v0.3.3`` --- +## 0.3.3 (October 18, 2023) + +### Notes + +- Release date: **(August 18, 2023)** +- Supported Terraform version: **v1.x.x** + +### Bug Fixes + +- [PR #139](https://github.com/zscaler/zscaler-terraformer/pull/139) Fixed ZIA ``zia_location_management`` resource to ensure sub-locations are also imported. + +## 0.3.2 (October 3, 2023) + +### Notes + +- Release date: **(October 3, 2023)** +- Supported Terraform version: **v1.x.x** + +### Bug Fixes + +- [PR #134](https://github.com/zscaler/zscaler-terraformer/pull/134) Implement condition to exclude ``applications`` block from the ZPA resources `zpa_segment_group` and `zpa_server_group`. + ## 0.3.1 (September 17, 2023) ### Notes diff --git a/go.mod b/go.mod index 65d54f9..6d8c9b5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zscaler/zscaler-terraformer -go 1.20 +go 1.19 require ( github.com/dnaeon/go-vcr v1.2.0 @@ -15,7 +15,7 @@ require ( github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 github.com/zclconf/go-cty v1.14.1 - github.com/zscaler/zscaler-sdk-go/v2 v2.1.3 + github.com/zscaler/zscaler-sdk-go/v2 v2.1.4 ) require ( diff --git a/go.sum b/go.sum index ed541bf..fd326ef 100644 --- a/go.sum +++ b/go.sum @@ -264,8 +264,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zscaler/zscaler-sdk-go/v2 v2.1.3 h1:DKFlvNVGXGgHTQjggj0kFs7S9IGGoP2ICc+svbn8E4A= -github.com/zscaler/zscaler-sdk-go/v2 v2.1.3/go.mod h1:PQscsdJVbmOXn7xqkRz3MdwYrt2UGHg37ZlON77iptg= +github.com/zscaler/zscaler-sdk-go/v2 v2.1.4 h1:bL7vAtMozSVGHZq28jXfHnnYmraddZ++Vl6R8GbdPLc= +github.com/zscaler/zscaler-sdk-go/v2 v2.1.4/go.mod h1:PQscsdJVbmOXn7xqkRz3MdwYrt2UGHg37ZlON77iptg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= diff --git a/internal/app/zscaler-terraformer/cmd/generate.go b/internal/app/zscaler-terraformer/cmd/generate.go index 794f559..d6ce4af 100644 --- a/internal/app/zscaler-terraformer/cmd/generate.go +++ b/internal/app/zscaler-terraformer/cmd/generate.go @@ -84,7 +84,7 @@ func init() { var generateCmd = &cobra.Command{ Use: "generate", - Short: "Fetch resources from the Cloudflare API and generate the respective Terraform stanzas", + Short: "Fetch resources from the ZIA and Or ZPA API and generate the respective Terraform stanzas", Run: generateResources(), PreRun: sharedPreRun, } @@ -147,6 +147,10 @@ func buildResourceName(resourceType string, structData map[string]interface{}) s resID = strings.ReplaceAll(resID, `"`, "") resID = strings.ReplaceAll(resID, `'`, "") resID = strings.ReplaceAll(resID, "`", "") + + // Remove any double underscores + resID = strings.ReplaceAll(resID, "__", "_") + return resID } @@ -601,6 +605,7 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_location_management": + // Get all parent locations jsonPayload, err := api.zia.locationmanagement.GetAll() if err != nil { log.Fatal(err) @@ -608,6 +613,21 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) + + // Get all sublocations + sublocationsPayload, err := api.zia.locationmanagement.GetAllSublocations() + if err != nil { + log.Fatal(err) + } + m, _ = json.Marshal(sublocationsPayload) + subResourceCount := len(sublocationsPayload) + var subJsonStructData []interface{} + _ = json.Unmarshal(m, &subJsonStructData) + + // Append sublocations to the main jsonStructData slice + jsonStructData = append(jsonStructData, subJsonStructData...) + + resourceCount += subResourceCount case "zia_url_categories": list, err := api.zia.urlcategories.GetAll() if err != nil { @@ -722,11 +742,9 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { // No need to output computed attributes that are also not // optional. - if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional && attrName != "static_ip_id" { - continue - } - - if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional && attrName != "tunnel_id" { + // Here is the corrected part for handling "static_ip_id" and "tunnel_id" + bypassAttributes := []string{"static_ip_id", "tunnel_id"} + if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional && isInList(attrName, bypassAttributes) { continue } @@ -734,12 +752,25 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { switch { case ty.IsPrimitiveType(): switch ty { - case cty.String, cty.Bool, cty.Number: + case cty.String, cty.Bool: value := structData[apiAttrName] - if resourceType == "zia_dlp_notification_templates" && isInList(attrName, []string{"subject", "plain_text_message", "html_message"}) { + // Handle any string modifications here, if necessary. + + output += writeAttrLine(attrName, value, false) + + case cty.Number: + value := structData[apiAttrName] + + // Handle parent_id specially to prevent scientific notation. + if attrName == "parent_id" { + intValue, ok := value.(float64) + if ok { + output += fmt.Sprintf("%s = %d\n", attrName, int64(intValue)) + continue + } + } else if resourceType == "zia_dlp_notification_templates" && isInList(attrName, []string{"subject", "plain_text_message", "html_message"}) { value = strings.ReplaceAll(value.(string), "${", "$${") - } - if resourceType == "zpa_service_edge_group" && attrName == "is_public" { + } else if resourceType == "zpa_service_edge_group" && attrName == "is_public" { if value == nil { value = false } else { @@ -747,10 +778,13 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { value = isPublic } } + output += writeAttrLine(attrName, value, false) + default: log.Debugf("unexpected primitive type %q", ty.FriendlyName()) } + case ty.IsCollectionType(): switch { case ty.IsListType(), ty.IsSetType(), ty.IsMapType(): diff --git a/internal/app/zscaler-terraformer/cmd/import.go b/internal/app/zscaler-terraformer/cmd/import.go index 58bbada..4784106 100644 --- a/internal/app/zscaler-terraformer/cmd/import.go +++ b/internal/app/zscaler-terraformer/cmd/import.go @@ -460,6 +460,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_location_management": + // Get all parent locations jsonPayload, err := api.zia.locationmanagement.GetAll() if err != nil { log.Fatal(err) @@ -467,6 +468,22 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) + + // Get all sublocations + sublocationsPayload, err := api.zia.locationmanagement.GetAllSublocations() + if err != nil { + log.Fatal(err) + } + m, _ = json.Marshal(sublocationsPayload) + subResourceCount := len(sublocationsPayload) + var subJsonStructData []interface{} + _ = json.Unmarshal(m, &subJsonStructData) + + // Append sublocations to the main jsonStructData slice + jsonStructData = append(jsonStructData, subJsonStructData...) + + resourceCount += subResourceCount + case "zia_url_categories": list, err := api.zia.urlcategories.GetAll() if err != nil { @@ -538,9 +555,11 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string) { log.Printf("%q is not yet supported for state import", resourceType) return } + if resourceCount == 0 { return } + tf, _, workingDir := initTf(resourceType) f, err := os.Create(strings.TrimSuffix(workingDir, "/") + "/" + resourceType + ".tf") if err != nil { @@ -548,6 +567,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string) { } generate(cmd, f, resourceType) f.Close() + for _, data := range jsonStructData { strcutData := data.(map[string]interface{}) resourceID, ok := strcutData["id"].(string)