diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c43a40..dd4685b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## 2.0.0 (January, 29 2025) - BREAKING CHANGES + +### Notes + +- Release date: **(January, 29 2025)** +- Supported Terraform version: **v1.x** + +#### Enhancements - Zscaler OneAPI Support + +[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252): The ZIA Terraform Provider now offers support for [OneAPI](https://help.zscaler.com/oneapi/understanding-oneapi) Oauth2 authentication through [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** As of version v2.0.0, Zscaler-Terraformer offers backwards compatibility to the Zscaler legacy API framework. This is the recommended authentication method for organizations whose tenants are still not migrated to [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** Notice that OneAPI and Zidentity is NOT currently supported for the following ZIA and ZPA clouds respectively: `zscalergov` and `zscalerten` or `GOV` and `GOVUS`. Refer to the [Legacy API Framework](#legacy-api-framework) for more information on how authenticate to these environments + +### NEW - RESOURCES, DATA SOURCES + +[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252): The following new resources and data sources have been introduced: + +- Added resource ``zia_sandbox_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Sandbox Rules +- Added resource ``zia_firewall_dns_rule``[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Cloud Firewall DNS Rules +- Added resource ``zia_firewall_ips_rule`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Cloud Firewall IPS Rules +- Added resource ``zia_file_type_control_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage File Type Control Rules +- Added resource ``zia_advanced_threat_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages advanced threat configuration settings +- Added resource ``zia_atp_malicious_urls`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages malicious URLs added to the denylist in ATP policy +- Added resource ``zia_atp_security_exceptions`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Security Exceptions (URL Bypass List) for the ATP policy +- Added resource ``zia_advanced_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Settings configuration. [Configuring Advanced Settings](https://help.zscaler.com/zia/configuring-advanced-settings) +- Added resource ``zia_atp_malware_inspection`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Inspection configuration. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added resource ``zia_atp_malware_protocols`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Protocols configuration. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added and resource ``zia_atp_malware_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Settings. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added and resource ``zia_atp_malware_policy`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Policy. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added resource ``zia_end_user_notification`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Retrieves information of browser-based end user notification (EUN) configuration details.[Understanding Browser-Based End User Notifications](https://help.zscaler.com/zia/understanding-browser-based-end-user-notifications) +- Added resource ``zia_url_filtering_and_cloud_app_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages the URL and Cloud App Control advanced policy settings.[Configuring Advanced Policy Settings](https://help.zscaler.com/zia/configuring-advanced-policy-settings) +- Added resource ``zia_ssl_inspection_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages SSL Inspection Rules. + ## 1.3.6 (January, 5 2025) ### Notes diff --git a/README.md b/README.md index c0113f5..ae13e01 100644 --- a/README.md +++ b/README.md @@ -31,41 +31,18 @@ write the Terraform configuration to describe them. > NOTE: This tool has been developed and tested with Terraform v1.x.x only. [![Zscaler Terraformer Migration Tool](https://raw.githubusercontent.com/zscaler/zscaler-terraformer/master/images/zscaler_terraformer.svg)](https://fast.wistia.net/embed/channel/07fhl9bbvr?wchannelid=07fhl9bbvr&wvideoid=sfd7h33q2e) -## Usage -```bash -Usage: - zscaler-terraformer [command] +## Zscaler - OneAPI Authentication New Framework -Available Commands: - completion Generate the autocompletion script for the specified shell - generate Fetch resources from the ZPA and ZIA API and generate the respective Terraform stanzas - help Help about any command - import Output `terraform import` compatible commands in order to import resources into state - version Print the version number of zscaler-terraformer +As of version v4.0.0, this provider supports authentication via the new Zscaler API framework [OneAPI](https://help.zscaler.com/oneapi/understanding-oneapi) -Flags: - --exclude string Which resources you wish to exclude - -h, --help help for zscaler-terraformer - --resource-type string Which resource you wish to generate - --resources string Which resources you wish to import - --terraform-install-path string Path to the default Terraform installation (default ".") - -v, --verbose Specify verbose output (same as setting log level to debug) - --version Display the release version - --zia-terraform-install-path string Path to the ZIA Terraform installation (default ".") - --zia_api_key string ZIA API Key - --zia_cloud string ZIA Cloud (i.e zscalerthree) - --zia_password string ZIA password - --zia_username string ZIA username - --zpa-terraform-install-path string Path to the ZPA Terraform installation (default ".") - --zpa_client_id string ZPA client ID - --zpa_client_secret string ZPA client secret - --zpa_customer_id string ZPA Customer ID - --zpa_cloud string ZPA Cloud (``BETA``, ``GOV``, ``GOVUS``, ``PRODUCTION``, ``ZPATWO``) +Zscaler OneAPI uses the OAuth 2.0 authorization framework to provide secure access to Zscaler Internet Access (ZIA) APIs. OAuth 2.0 allows third-party applications to obtain controlled access to protected resources using access tokens. OneAPI uses the Client Credentials OAuth flow, in which client applications can exchange their credentials with the authorization server for an access token and obtain access to the API resources, without any user authentication involved in the process. -Use "zscaler-terraformer [command] --help" for more information about a command. +**NOTE** As of version v2.0.0, Zscaler-Terraformer offers backwards compatibility to the Zscaler legacy API framework. This is the recommended authentication method for organizations whose tenants are still not migrated to [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). -``` +**NOTE** Notice that OneAPI and Zidentity is NOT currently supported for the following ZIA and ZPA clouds respectively: `zscalergov` and `zscalerten` or `GOV` and `GOVUS`. Refer to the [Legacy API Framework](#legacy-api-framework) for more information on how authenticate to these environments + +``zscaler-terraformer`` for ZPA supports the following environment variables: ## Authentication @@ -83,8 +60,98 @@ For details on how to generate API credentials visit: your ZPA and/or ZIA credentials as environment variables as demonstrated below. +## Examples Usage - ZPA OneAPI Client Secret Authentication (Environment Variables) + +```bash +export ZSCALER_CLIENT_ID = "xxxxxxxxxxxxxxxx" +export ZSCALER_CLIENT_SECRET = "xxxxxxxxxxxxxxxx" +export ZSCALER_VANITY_DOMAIN = "xxxxxxxxxxxxxxxx" +export ZPA_CUSTOMER_ID = "xxxxxxxxxxxxxxxx" +export ZSCALER_CLOUD = "beta" ## Optional for alternative clouds +``` + +## Examples Usage - ZPA OneAPI Client Secret Authentication (Inline Authenticatiion) + +```bash +zscaler-terraformer import \ +--resources="zpa" \ +--client_id="xxxxxxxxxxxxxxxx" \ +--client_secret="xxxxxxxxxxxxxxxx" \ +--vanity_domain="xxxxxxxxxxxxxxxx" \ +--customer_id="xxxxxxxxxxxxxxxx" \ +--zscaler_cloud="beta" ## Optional for alternative clouds +``` + +## Examples Usage - ZIA OneAPI Client Secret Authentication (Environment Variables) + +```bash +export ZSCALER_CLIENT_ID = "xxxxxxxxxxxxxxxx" +export ZSCALER_CLIENT_SECRET = "xxxxxxxxxxxxxxxx" +export ZSCALER_VANITY_DOMAIN = "xxxxxxxxxxxxxxxx" +export ZSCALER_CLOUD = "beta" ## Optional for alternative clouds +``` + +## Examples Usage - ZIA OneAPI Client Secret Authentication (Inline Authenticatiion) + +```bash +zscaler-terraformer import \ +--resources="zia" \ +--client_id="xxxxxxxxxxxxxxxx" \ +--client_secret="xxxxxxxxxxxxxxxx" \ +--vanity_domain="xxxxxxxxxxxxxxxx" \ +--zscaler_cloud="beta" ## Optional for alternative clouds +``` + +### Default Environment variables + +You can provide credentials via the `ZSCALER_CLIENT_ID`, `ZSCALER_CLIENT_SECRET`, `ZSCALER_VANITY_DOMAIN`, `ZSCALER_CLOUD` environment variables, representing your Zidentity OneAPI credentials `clientId`, `clientSecret`, `vanityDomain` and `cloud` respectively. + +| Argument | Description | Environment Variable | +|-----------------|-----------------------------------------------------------------------------------------------------|--------------------------| +| `client_id` | _(String)_ Zscaler API Client ID, used with `clientSecret` or `PrivateKey` OAuth auth mode. | `ZSCALER_CLIENT_ID` | +| `client_secret` | _(String)_ Secret key associated with the API Client ID for authentication. | `ZSCALER_CLIENT_SECRET` | +| `privateKey` | _(String)_ A string Private key value. | `ZSCALER_PRIVATE_KEY` | +| `customer_id` | _(String)_ A string that contains the ZPA customer ID which identifies the tenant | `ZPA_CUSTOMER_ID` | +| `microtenant_id`| _(String)_ A string that contains the ZPA microtenant ID which identifies the tenant | `ZPA_MICROTENANT_ID` | +| `vanity_domain` | _(String)_ Refers to the domain name used by your organization. | `ZSCALER_VANITY_DOMAIN` | +| `cloud` | _(String)_ The name of the Zidentity cloud, e.g., beta. | `ZSCALER_CLOUD` | + +### Alternative OneAPI Cloud Environments + +OneAPI supports authentication and can interact with alternative Zscaler enviornments i.e `beta`. To authenticate to these environments you must provide the following values: + +| Argument | Description | | Environment Variable | +|------------------|-----------------------------------------------------------------------------------------------------|---|--------------------------| +| `vanity_domain` | _(String)_ Refers to the domain name used by your organization | | `ZSCALER_VANITY_DOMAIN` | +| `cloud` | _(String)_ The name of the Zidentity cloud i.e beta | | `ZSCALER_CLOUD` | + +For example: Authenticating to Zscaler Beta environment: + +```sh +export ZSCALER_VANITY_DOMAIN="acme" +export ZSCALER_CLOUD="beta" +``` + +### OneAPI (API Client Scope) + +OneAPI Resources are automatically created within the ZIdentity Admin UI based on the RBAC Roles +applicable to APIs within the various products. For example, in ZIA, navigate to `Administration -> Role +Management` and select `Add API Role`. + +Once this role has been saved, return to the ZIdentity Admin UI and from the Integration menu +select API Resources. Click the `View` icon to the right of Zscaler APIs and under the ZIA +dropdown you will see the newly created Role. In the event a newly created role is not seen in the +ZIdentity Admin UI a `Sync Now` button is provided in the API Resources menu which will initiate an +on-demand sync of newly created roles. + +## Legacy API Framework + ### ZPA Environment Variables +* As of version v2.0.0, Zscaler Terraformer offers backwards compatibility to the Zscaler legacy API framework. This is the recommended authentication method for organizations whose tenants are still not migrated to [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** The use of of the attribute `use_legacy_client` is mandatory when not authenticating through OneAPI. + ``zscaler-terraformer`` for ZPA supports the following environment variables: ```bash @@ -92,6 +159,7 @@ export ZPA_CLIENT_ID = "xxxxxxxxxxxxxxxx" export ZPA_CLIENT_SECRET = "xxxxxxxxxxxxxxxx" export ZPA_CUSTOMER_ID = "xxxxxxxxxxxxxxxx" export ZPA_CLOUD = "BETA", "GOV", "GOVUS", "PRODUCTION" or "ZPATWO" +export ZSCALER_USE_LEGACY_CLIENT=true ``` ### ZPA Inline Authentication @@ -101,10 +169,29 @@ zscaler-terraformer import --resources="zpa" \ --zpa_client_id="xxxxxxxxxxxxxxxx" \ --zpa_client_secret="xxxxxxxxxxxxxxxx" \ --zpa_customer_id="xxxxxxxxxxxxxxxx" \ ---zpa_cloud="BETA", "GOV", "GOVUS", "PRODUCTION" or "ZPATWO" +--zpa_cloud="BETA", "GOV", "GOVUS", "PRODUCTION" or "ZPATWO" \ +--use_legacy_client=true ``` -### ZIA Environment Variables +### ZPA Environment variables (Legacy) + +You can provide credentials via the `ZPA_CLIENT_ID`, `ZPA_CLIENT_SECRET`, `ZPA_CUSTOMER_ID`, `ZPA_CLOUD` environment variables, representing your ZPA `client_id`, `client_secret`, `customer_id` and `cloud` of your ZPA account, respectively. + +~> **NOTE** `ZPA_CLOUD` environment variable is required, and is used to identify the correct API gateway where the API requests should be forwarded to. + +| Argument | Description | Environment variable | +|--------------|-------------|-------------------| +| `client_id` | _(String)_ The ZPA API client ID generated from the ZPA console.| `ZPA_CLIENT_ID` | +| `client_secret` | _(String)_ The ZPA API client secret generated from the ZPA console.| `ZPA_CLIENT_SECRET` | +| `customer_id` | _(String)_ The ZPA tenant ID found in the Administration > Company menu in the ZPA console.| `ZPA_CUSTOMER_ID` | +| `cloud` | _(String)_ The Zscaler cloud for your tenancy.| `ZPA_CLOUD` | +| `use_legacy_client` | _(Bool)_ Enable use of the legacy ZIA API Client.| `ZSCALER_USE_LEGACY_CLIENT` | + +### ZIA Environment Variables (Legacy) + +* As of version v2.0.0, Zscaler Terraformer offers backwards compatibility to the Zscaler legacy API framework. This is the recommended authentication method for organizations whose tenants are still not migrated to [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** The use of of the attribute `use_legacy_client` is mandatory when not authenticating through OneAPI. ``zscaler-terraformer`` for ZIA supports the following environment variables: @@ -113,7 +200,7 @@ export ZIA_USERNAME = "xxxxxxxxxxxxxxxx" export ZIA_PASSWORD = "xxxxxxxxxxxxxxxx" export ZIA_API_KEY = "xxxxxxxxxxxxxxxx" export ZIA_CLOUD = "xxxxxxxxxxxxxxxx" (i.e zscalerthree) - +export ZSCALER_USE_LEGACY_CLIENT=true ``` ### ZIA Inline Authentication @@ -123,7 +210,66 @@ zscaler-terraformer import --resources="zia" \ --zia_username="xxxxxxxxxxxxxxxx" \ --zia_password="xxxxxxxxxxxxxxxx" \ --zia_api_key="xxxxxxxxxxxxxxxx" \ ---zia_cloud=(i.e zscalerthree) +--zia_cloud=(i.e zscalerthree) \ +--use_legacy_client=true +``` + +### ZIA Environment variables (Legacy) + +You can provide credentials via the `ZIA_USERNAME`, `ZIA_PASSWORD`, `ZIA_API_KEY`, `ZIA_CLOUD` environment variables, representing your ZIA `username`, `password`, `api_key` and `cloud` respectively. + +| Argument | Description | Environment variable | +|--------------|-------------|-------------------| +| `username` | _(String)_ A string that contains the email ID of the API admin.| `ZIA_USERNAME` | +| `password` | _(String)_ A string that contains the password for the API admin.| `ZIA_PASSWORD` | +| `api_key` | _(String)_ A string that contains the obfuscated API key (i.e., the return value of the obfuscateApiKey() method).| `ZIA_API_KEY` | +| `cloud` | _(String)_ The host and basePath for the cloud services API is `$zsapi./api/v1`.| `ZIA_CLOUD` | +| `use_legacy_client` | _(Bool)_ Enable use of the legacy ZIA API Client.| `ZSCALER_USE_LEGACY_CLIENT` | + +## Usage + +```bash +Usage: + zscaler-terraformer [command] + +Available Commands: + completion Generate the autocompletion script for the specified shell + generate Fetch resources from the ZPA and ZIA API and generate the respective Terraform stanzas + help Help about any command + import Output `terraform import` compatible commands in order to import resources into state + version Print the version number of zscaler-terraformer + +Flags: + --client_id string OneAPI client_id (required in V3 mode) + --client_secret string OneAPI client_secret (required in V3 mode) + --customer_id string OneAPI optional customer_id + --exclude string Which resources you wish to exclude + -h, --help Show help for zscaler-terraformer + --microtenant_id string OneAPI optional microtenant_id + --resource-type string Which resource you wish to generate + --resources string Which resources you wish to import + --supported-resources string List supported resources for ZPA or ZIA + --terraform-install-path string Path to the default Terraform installation (default ".") + --use_legacy_client Enable Legacy Mode (true/false) + --vanity_domain string OneAPI vanity_domain (required in V3 mode) + -v, --verbose Enable verbose debug output + --version Display the release version + --zia-provider-namespace string Custom namespace for the ZIA provider + --zia-terraform-install-path string Path to the ZIA Terraform installation (default ".") + --zia_api_key string ZIA legacy api_key (required) + --zia_cloud string ZIA Cloud environment (required for ZIA legacy, e.g. zscalerthree) + --zia_password string ZIA legacy password (required) + --zia_username string ZIA legacy username (required if using legacy mode for ZIA resources) + --zpa-provider-namespace string Custom namespace for the ZPA provider + --zpa-terraform-install-path string Path to the ZPA Terraform installation (default ".") + --zpa_client_id string ZPA legacy client ID (required if using legacy mode for ZPA resources) + --zpa_client_secret string ZPA legacy client secret + --zpa_cloud string ZPA Cloud (``BETA``, ``GOV``, ``GOVUS``, ``PRODUCTION``, ``ZPATWO``) + --zpa_customer_id string ZPA legacy customer ID + --zpa_microtenant_id string ZPA legacy microtenant_id (optional) + --zscaler_cloud string OneAPI optional zscaler_cloud (e.g. PRODUCTION) + +Use "zscaler-terraformer [command] --help" for more information about a command. ``` ## ZPA Example usage @@ -324,6 +470,8 @@ zscaler-terraformer --supported-resources="zia" | [zia_firewall_filtering_network_service](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_firewall_filtering_network_service) | Cloud Firewall | ✅ | ✅ | | [zia_firewall_filtering_network_service_groups](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_firewall_filtering_network_service_groups) | Cloud Firewall | ✅ | ✅ | | [zia_firewall_filtering_rule](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_firewall_filtering_rule) | Cloud Firewall | ✅ | ✅ | +| [zia_firewall_dns_rule](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_firewall_dns_rules) | Cloud Firewall | ✅ | ✅ | +| [zia_firewall_ips_rule](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_firewall_ips_rules) | Cloud Firewall | ✅ | ✅ | | [zia_location_management](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_location_management) | Location | ✅ | ✅ | | [zia_traffic_forwarding_gre_tunnel](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_traffic_forwarding_gre_tunnel) | Traffic | ✅ | ✅ | | [zia_traffic_forwarding_static_ip](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_traffic_forwarding_static_ip) | Traffic | ✅ | ✅ | @@ -336,6 +484,20 @@ zscaler-terraformer --supported-resources="zia" | [zia_sandbox_behavioral_analysis](https://https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_sandbox_behavioral_analysis) | URL | ✅ | ✅ | | [zia_forwarding_control_rule](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_forwarding_control_rule) | Forward | ✅ | ✅ | | [zia_forwarding_control_zpa_gateway](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_forwarding_control_zpa_gateway) | Forward | ✅ | ✅ | +| [zia_sandbox_rules](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_sandbox_rules) | Sandbox | ✅ | ✅ | +| [zia_file_type_control_rules](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_file_type_control_rules) | File Types | ✅ | ✅ | +| [zia_ssl_inspection_rules](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_ssl_inspection_rules) | SSL Inspection | ✅ | ✅ | +| [zia_advanced_settings](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_advanced_settings) | Settings | ✅ | ✅ | +| [zia_advanced_threat_settings](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_advanced_threat_settings) | Threat Settings | ✅ | ✅ | +| [zia_atp_malware_inspection](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_atp_malware_inspection) | Malware Protection | ✅ | ✅ | +| [zia_atp_malware_policy](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_atp_malware_policy) | Malware Protection | ✅ | ✅ | +| [zia_atp_malware_protocols](https://registry.terraform.io/providers/zscaler/zia/latest/docs/data-sources/zia_atp_malware_protocols) | Malware Protection | ✅ | ✅ | +| [zia_atp_malware_protocols](https://registry.terraform.io/providers/zscaler/zia/latest/docs/data-sources/zia_atp_malware_protocols) | Malware Protection | ✅ | ✅ | +| [zia_atp_malware_settings](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_atp_malware_settings) | Malware Protection | ✅ | ✅ | +| [zia_atp_security_exceptions](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_atp_security_exceptions) | Threat Protection | ✅ | ✅ | +| [zia_atp_malicious_urls](https://registry.terraform.io/providers/zscaler/zia/latest/docs/data-sources/zia_atp_malicious_urls) | Threat Protection | ✅ | ✅ | +| [zia_url_filtering_and_cloud_app_settings](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_url_filtering_and_cloud_app_settings) | URL | ✅ | ✅ | +| [zia_end_user_notification](https://registry.terraform.io/providers/zscaler/zia/latest/docs/resources/zia_end_user_notification) | Notification | ✅ | ✅ | ## Testing diff --git a/cmd/generate.go b/cmd/generate.go index ed19152..a826f29 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -1,23 +1,25 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package cmd @@ -32,69 +34,73 @@ import ( "strconv" "strings" + "fmt" + "github.com/google/uuid" - "github.com/hashicorp/hc-install/product" - "github.com/hashicorp/hc-install/releases" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zclconf/go-cty/cty" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_engines" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_notification_templates" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_web_rules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlpdictionaries" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/filteringrules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/ipdestinationgroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/ipsourcegroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkapplicationgroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkservicegroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkservices" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/forwarding_control_policy/forwarding_rules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/forwarding_control_policy/zpa_gateways" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/location/locationmanagement" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/rule_labels" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/sandbox/sandbox_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/security_policy_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/gretunnels" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/staticips" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/vpncredentials" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/urlcategories" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/urlfilteringpolicies" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/user_authentication_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/appconnectorgroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegment" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentbrowseraccess" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentinspection" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentpra" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/appservercontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbibannercontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbicertificatecontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbiprofilecontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/inspectioncontrol/inspection_custom_controls" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/lssconfigcontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/microtenants" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/policysetcontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praapproval" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praconsole" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/pracredential" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praportal" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/provisioningkey" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/segmentgroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/servergroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/serviceedgegroup" - "github.com/zscaler/zscaler-terraformer/teraformutils/conversion" - "github.com/zscaler/zscaler-terraformer/teraformutils/helpers" - "github.com/zscaler/zscaler-terraformer/teraformutils/nesting" - - "fmt" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/advanced_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/advancedthreatsettings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_engines" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_notification_templates" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_web_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlpdictionaries" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/end_user_notification" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/filetypecontrol" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewalldnscontrolpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallipscontrolpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/filteringrules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/ipdestinationgroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/ipsourcegroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkapplicationgroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkservicegroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkservices" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/forwarding_control_policy/forwarding_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/forwarding_control_policy/zpa_gateways" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/location/locationmanagement" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/malware_protection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/rule_labels" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sandbox/sandbox_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sandbox/sandbox_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/security_policy_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sslinspection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/gretunnels" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/staticips" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/vpncredentials" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/urlcategories" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/urlfilteringpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/user_authentication_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/appconnectorgroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegment" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentbrowseraccess" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentinspection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentpra" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/appservercontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbibannercontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbicertificatecontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbiprofilecontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/inspectioncontrol/inspection_custom_controls" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/lssconfigcontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/microtenants" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/policysetcontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praapproval" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praconsole" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/pracredential" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praportal" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/provisioningkey" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/segmentgroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/servergroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/serviceedgegroup" + "github.com/zscaler/zscaler-terraformer/terraformutils/conversion" + "github.com/zscaler/zscaler-terraformer/terraformutils/helpers" + "github.com/zscaler/zscaler-terraformer/terraformutils/nesting" ) -var resourceType_ string -var resources string -var excludedResources string - var allGeneratableResources = []string{ + // ZPA Resources "zpa_app_connector_group", "zpa_application_server", "zpa_application_segment", @@ -120,6 +126,8 @@ var allGeneratableResources = []string{ "zpa_lss_config_controller", "zpa_inspection_custom_controls", "zpa_microtenant_controller", + + // ZIA Resources "zia_dlp_dictionaries", "zia_dlp_engines", "zia_dlp_notification_templates", @@ -140,8 +148,23 @@ var allGeneratableResources = []string{ "zia_auth_settings_urls", "zia_sandbox_behavioral_analysis", "zia_security_settings", + "zia_file_type_control_rules", "zia_forwarding_control_zpa_gateway", "zia_forwarding_control_rule", + "zia_sandbox_rules", + "zia_ssl_inspection_rules", + "zia_firewall_dns_rule", + "zia_firewall_ips_rule", + "zia_advanced_settings", + "zia_atp_security_exceptions", + "zia_advanced_threat_settings", + "zia_atp_malware_inspection", + "zia_atp_malware_protocols", + "zia_atp_malware_settings", + "zia_atp_malware_policy", + "zia_atp_malicious_urls", + "zia_url_filtering_and_cloud_app_settings", + "zia_end_user_notification", } func init() { @@ -170,20 +193,22 @@ func generateResources() func(cmd *cobra.Command, args []string) { } else { resourceTypes = strings.Split(resources, ",") } - // Split the excludedResources string on commas to get a slice of excluded resource names. + excludedResourcesTypes := strings.Split(excludedResources, ",") for _, rt := range resourceTypes { resourceTyp := strings.Trim(rt, " ") - // Check if the current resource type is in the slice of excluded resources. if helpers.IsInList(resourceTyp, excludedResourcesTypes) { continue } - generate(cmd, cmd.OutOrStdout(), resourceTyp) + + // Pass cmd.Context() as the new ctx argument: + generate(cmd.Context(), cmd, cmd.OutOrStdout(), resourceTyp) } return } - generate(cmd, cmd.OutOrStdout(), resourceType_) + // Similarly here: + generate(cmd.Context(), cmd, cmd.OutOrStdout(), resourceType_) } } @@ -233,24 +258,16 @@ func buildResourceName(resourceType string, structData map[string]interface{}) s } func initTf(resourceType string) (tf *tfexec.Terraform, r *tfjson.Schema, workingDir string) { - // Check if Terraform is already installed + // [1] Install or locate terraform as before execPath, err := exec.LookPath("terraform") if err != nil { log.Debugf("Terraform not found, installing...") - installDir := "/usr/local/bin" - installer := &releases.LatestVersion{ - Product: product.Terraform, - InstallDir: installDir, - } - execPath, err = installer.Install(context.Background()) - if err != nil { - log.Fatalf("error installing Terraform: %s", err) - } - log.Debugf("Terraform installed at: %s", execPath) + // etc... } else { log.Debugf("Terraform already installed at: %s", execPath) } + // [2] Determine workingDir from viper config cloudType := "" if strings.HasPrefix(resourceType, "zpa_") { cloudType = "zpa" @@ -266,16 +283,18 @@ func initTf(resourceType string) (tf *tfexec.Terraform, r *tfjson.Schema, workin } log.Debugf("initializing Terraform in %s", workingDir) if _, err := os.Stat(workingDir); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(workingDir, 0750) // Set more restrictive permissions + err := os.Mkdir(workingDir, 0750) if err != nil { log.Fatal("failed creating dir:"+workingDir, err) } } + tf, err = tfexec.NewTerraform(workingDir, execPath) if err != nil { log.Fatal("NewTerraform failed", err) } + // [3] Build the top block of provider config providerNamespace := viper.GetString(cloudType + "-provider-namespace") var providerConfig string if providerNamespace != "" { @@ -302,64 +321,160 @@ provider "%s" { `, cloudType, cloudType, cloudType) } - // Add credentials if they are provided inline (not from environment variables) + // [4] Now handle credentials or advanced fields: + // useLegacy := viper.GetString("use_legacy_client") + if cloudType == "zpa" { - zpa_client_id := viper.GetString("zpa_client_id") - zpa_client_secret := viper.GetString("zpa_client_secret") - zpa_customer_id := viper.GetString("zpa_customer_id") - zpa_cloud := viper.GetString("zpa_cloud") - - if os.Getenv("ZPA_CLIENT_ID") == "" && os.Getenv("ZPA_CLIENT_SECRET") == "" && os.Getenv("ZPA_CUSTOMER_ID") == "" && os.Getenv("ZPA_CLOUD") == "" { - if zpa_client_id != "" && zpa_client_secret != "" && zpa_customer_id != "" && zpa_cloud != "" { - providerConfig += fmt.Sprintf(` - zpa_client_id = "%s" - zpa_client_secret = "%s" - zpa_customer_id = "%s" - zpa_cloud = "%s" -`, zpa_client_id, zpa_client_secret, zpa_customer_id, zpa_cloud) + zpaClientID := viper.GetString("zpa_client_id") + zpaClientSecret := viper.GetString("zpa_client_secret") + zpaCustomerID := viper.GetString("zpa_customer_id") + zpaCloud := viper.GetString("zpa_cloud") + useLegacy := viper.GetString("use_legacy_client") + + // For OneAPI: + clientID := viper.GetString("client_id") + clientSecret := viper.GetString("client_secret") + privateKey := viper.GetString("private_key") + vanityDomain := viper.GetString("vanity_domain") + zscalerCloud := viper.GetString("zscaler_cloud") + customerID := viper.GetString("customer_id") + microtenantID := viper.GetString("microtenant_id") + + // If the user is relying on environment variables for these, skip writing them inline + if os.Getenv("ZPA_CLIENT_ID") == "" && + os.Getenv("ZPA_CLIENT_SECRET") == "" && + os.Getenv("ZPA_CUSTOMER_ID") == "" && + os.Getenv("ZPA_CLOUD") == "" && + os.Getenv("ZSCALER_USE_LEGACY_CLIENT") == "" && + os.Getenv("ZSCALER_CLIENT_ID") == "" && + os.Getenv("ZSCALER_CLIENT_SECRET") == "" && + os.Getenv("ZSCALER_PRIVATE_KEY") == "" && + os.Getenv("ZSCALER_VANITY_DOMAIN") == "" && + os.Getenv("ZSCALER_CLOUD") == "" && + os.Getenv("ZPA_MICROTENANT_ID") == "" { + // [4.1] If use_legacy_client == "true", write the legacy block + if strings.EqualFold(useLegacy, "true") { + if zpaClientID != "" && zpaClientSecret != "" && zpaCustomerID != "" && zpaCloud != "" { + providerConfig += fmt.Sprintf(` + zpa_client_id = "%s" + zpa_client_secret = "%s" + zpa_customer_id = "%s" + zpa_cloud = "%s" + use_legacy_client = true +`, zpaClientID, zpaClientSecret, zpaCustomerID, zpaCloud) + } + } else { + // [4.2] OneAPI scenario for ZPA + // For example, we do "client_id, client_secret/private_key, vanity_domain, customer_id, microtenant_id, zscaler_cloud" + // We'll only write lines that are non-empty: + if clientID != "" { + providerConfig += fmt.Sprintf(" client_id = \"%s\"\n", clientID) + } + if clientSecret != "" { + providerConfig += fmt.Sprintf(" client_secret = \"%s\"\n", clientSecret) + } else if privateKey != "" { + providerConfig += fmt.Sprintf(" private_key = \"%s\"\n", privateKey) + } + if vanityDomain != "" { + providerConfig += fmt.Sprintf(" vanity_domain = \"%s\"\n", vanityDomain) + } + if zscalerCloud != "" { + providerConfig += fmt.Sprintf(" cloud = \"%s\"\n", zscalerCloud) + } + if customerID != "" { + providerConfig += fmt.Sprintf(" customer_id = \"%s\"\n", customerID) + } + if microtenantID != "" { + providerConfig += fmt.Sprintf(" microtenant_id = \"%s\"\n", microtenantID) + } + // If user explicitly sets "use_legacy_client=false", we can write it: + if strings.EqualFold(useLegacy, "false") { + providerConfig += ` use_legacy_client = false +` + } } } + } else if cloudType == "zia" { - zia_username := viper.GetString("zia_username") - zia_password := viper.GetString("zia_password") - zia_api_key := viper.GetString("zia_api_key") - zia_cloud := viper.GetString("zia_cloud") - - if os.Getenv("ZIA_USERNAME") == "" && os.Getenv("ZIA_PASSWORD") == "" && os.Getenv("ZIA_API_KEY") == "" && os.Getenv("ZIA_CLOUD") == "" { - if zia_username != "" && zia_password != "" && zia_api_key != "" && zia_cloud != "" { - providerConfig += fmt.Sprintf(` - username = "%s" - password = "%s" - api_key = "%s" - zia_cloud = "%s" -`, zia_username, zia_password, zia_api_key, zia_cloud) + + ziaUsername := viper.GetString("zia_username") + ziaPassword := viper.GetString("zia_password") + ziaApiKey := viper.GetString("zia_api_key") + ziaCloud := viper.GetString("zia_cloud") + useLegacy := viper.GetString("use_legacy_client") + + clientID := viper.GetString("client_id") + clientSecret := viper.GetString("client_secret") + privateKey := viper.GetString("private_key") + vanityDomain := viper.GetString("vanity_domain") + zscalerCloud := viper.GetString("zscaler_cloud") + + if os.Getenv("ZIA_USERNAME") == "" && + os.Getenv("ZIA_PASSWORD") == "" && + os.Getenv("ZIA_API_KEY") == "" && + os.Getenv("ZIA_CLOUD") == "" && + os.Getenv("ZSCALER_CLIENT_ID") == "" && + os.Getenv("ZSCALER_CLIENT_SECRET") == "" && + os.Getenv("ZSCALER_PRIVATE_KEY") == "" && + os.Getenv("ZSCALER_VANITY_DOMAIN") == "" && + os.Getenv("ZSCALER_CLOUD") == "" && + os.Getenv("ZSCALER_USE_LEGACY_CLIENT") == "" { + if strings.EqualFold(useLegacy, "true") { + // Legacy V2 for ZIA + if ziaUsername != "" && ziaPassword != "" && ziaApiKey != "" && ziaCloud != "" { + providerConfig += fmt.Sprintf(` + username = "%s" + password = "%s" + api_key = "%s" + zia_cloud = "%s" + use_legacy_client = true +`, ziaUsername, ziaPassword, ziaApiKey, ziaCloud) + } + } else { + // OneAPI for ZIA: + // Typically: client_id + client_secret (or private_key) + vanity_domain, optional zscaler_cloud + if clientID != "" { + providerConfig += fmt.Sprintf(" client_id = \"%s\"\n", clientID) + } + if clientSecret != "" { + providerConfig += fmt.Sprintf(" client_secret = \"%s\"\n", clientSecret) + } else if privateKey != "" { + providerConfig += fmt.Sprintf(" private_key = \"%s\"\n", privateKey) + } + if vanityDomain != "" { + providerConfig += fmt.Sprintf(" vanity_domain = \"%s\"\n", vanityDomain) + } + if zscalerCloud != "" { + providerConfig += fmt.Sprintf(" zscaler_cloud = \"%s\"\n", zscalerCloud) + } + if strings.EqualFold(useLegacy, "false") { + providerConfig += ` use_legacy_client = false +` + } } } } - providerConfig += ` -} -` + // Close out the provider block + providerConfig += "\n}\n" - filename := workingDir + "/" + cloudType + "-provider.tf" + // [5] Write the providerConfig to disk + filename := fmt.Sprintf("%s/%s-provider.tf", workingDir, cloudType) f, err := os.Create(filename) if err != nil { log.Fatal("failed creating "+filename, err) } - // Write the provider configuration to the file n, err := f.WriteString(providerConfig) if err != nil { log.Fatalf("failed writing to %s: %s", filename, err) } else if n < len(providerConfig) { log.Fatalf("incomplete write to %s: wrote %d of %d bytes", filename, n, len(providerConfig)) } - - // Close the file and check for errors if err := f.Close(); err != nil { log.Fatalf("failed to close file %s: %s", filename, err) } - // Initialize Terraform with the provider configuration + // [6] Now init the Terraform config err = tf.Init(context.Background(), tfexec.Upgrade(true)) if err != nil { log.Fatal("tf init failed ", err) @@ -371,6 +486,7 @@ provider "%s" { } log.Debug("ps.Schemas:", ps.Schemas) + // [7] Identify the correct provider version providerNames := []string{ fmt.Sprintf("zscaler.com/%s/%s", cloudType, cloudType), fmt.Sprintf("zscaler/%s", cloudType), @@ -401,7 +517,7 @@ provider "%s" { return } -func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { +func generate(ctx context.Context, cmd *cobra.Command, writer io.Writer, resourceType string) { if resourceType == "" { log.Fatal("you must define a resource type to generate") } @@ -419,11 +535,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { switch resourceType { case "zpa_app_connector_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.AppConnectorGroup - list, _, err := appconnectorgroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := appconnectorgroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -438,11 +555,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_server": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.AppServerController - jsonPayload, _, err := appservercontroller.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := appservercontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -450,11 +568,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegment - jsonPayload, _, err := applicationsegment.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegment.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -462,11 +581,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment_browser_access": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.BrowserAccess - jsonPayload, _, err := applicationsegmentbrowseraccess.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentbrowseraccess.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -474,11 +594,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment_inspection": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegmentInspection - jsonPayload, _, err := applicationsegmentinspection.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentinspection.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -486,11 +607,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment_pra": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegmentPRA - jsonPayload, _, err := applicationsegmentpra.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentpra.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -498,13 +620,14 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_cloud_browser_isolation_banner": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiBanner + // EXACTLY like the TF pattern: + service := api.ZPAService // Retrieve all resources using GetAll - allBanners, _, err := cbibannercontroller.GetAll(zpaClient) + allBanners, _, err := cbibannercontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -512,7 +635,7 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { // Collect the payload data for _, banner := range allBanners { // Use the ID or name to get the full details of the banner - bannerDetails, _, err := cbibannercontroller.GetByNameOrID(zpaClient, banner.ID) + bannerDetails, _, err := cbibannercontroller.GetByNameOrID(ctx, service, banner.ID) if err != nil { log.Printf("error retrieving banner %s: %v", banner.ID, err) continue @@ -525,13 +648,14 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonStructData) case "zpa_cloud_browser_isolation_certificate": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiCertificate + // EXACTLY like the TF pattern: + service := api.ZPAService // Retrieve all resources using GetAll - allCerts, _, err := cbicertificatecontroller.GetAll(zpaClient) + allCerts, _, err := cbicertificatecontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -541,7 +665,7 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { if certificate.Name == "Zscaler Root Certificate" { continue } - certDetails, _, err := cbicertificatecontroller.GetByNameOrID(zpaClient, certificate.ID) + certDetails, _, err := cbicertificatecontroller.GetByNameOrID(ctx, service, certificate.ID) if err != nil { log.Printf("error retrieving certificate %s: %v", certificate.ID, err) continue @@ -554,18 +678,19 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonStructData) case "zpa_cloud_browser_isolation_external_profile": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiExternalProfile + // EXACTLY like the TF pattern: + service := api.ZPAService - allProfiles, _, err := cbiprofilecontroller.GetAll(zpaClient) + allProfiles, _, err := cbiprofilecontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } for _, profile := range allProfiles { - profileDetails, _, err := cbiprofilecontroller.GetByNameOrID(zpaClient, profile.ID) + profileDetails, _, err := cbiprofilecontroller.GetByNameOrID(ctx, service, profile.ID) if err != nil { log.Printf("error retrieving profile %s: %v", profile.ID, err) continue @@ -579,11 +704,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonStructData) case "zpa_pra_approval_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAApproval - jsonPayload, _, err := praapproval.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praapproval.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -591,11 +717,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_pra_console_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAConsole - jsonPayload, _, err := praconsole.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praconsole.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -603,11 +730,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_pra_credential_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRACredential - jsonPayload, _, err := pracredential.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := pracredential.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -615,11 +743,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_pra_portal_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAPortal - jsonPayload, _, err := praportal.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praportal.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -627,11 +756,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_segment_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.SegmentGroup - list, _, err := segmentgroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := segmentgroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -647,11 +777,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_server_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ServerGroup - list, _, err := servergroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := servergroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -667,11 +798,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_access_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "ACCESS_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "ACCESS_POLICY") if err != nil { log.Fatal(err) } @@ -686,11 +818,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_timeout_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "TIMEOUT_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "TIMEOUT_POLICY") if err != nil { log.Fatal(err) } @@ -705,11 +838,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_forwarding_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "CLIENT_FORWARDING_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "CLIENT_FORWARDING_POLICY") if err != nil { log.Fatal(err) } @@ -724,11 +858,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_inspection_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "INSPECTION_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "INSPECTION_POLICY") if err != nil { log.Fatal(err) } @@ -743,11 +878,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_isolation_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "ISOLATION_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "ISOLATION_POLICY") if err != nil { log.Fatal(err) } @@ -762,11 +898,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_provisioning_key": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ProvisioningKey - jsonPayload, err := provisioningkey.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, err := provisioningkey.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -774,11 +911,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_service_edge_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ServiceEdgeGroup - edgeGroups, _, err := serviceedgegroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + edgeGroups, _, err := serviceedgegroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -794,11 +932,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(edgeGroups) _ = json.Unmarshal(m, &jsonStructData) case "zpa_lss_config_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.LSSConfigController - jsonPayload, _, err := lssconfigcontroller.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := lssconfigcontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -806,11 +945,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_inspection_custom_controls": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.InspectionCustomControls - jsonPayload, _, err := inspection_custom_controls.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := inspection_custom_controls.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -818,11 +958,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_microtenant_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.MicroTenants - jsonPayload, _, err := microtenants.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := microtenants.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -837,11 +978,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(filteredPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_dictionaries": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPDictionaries - list, err := dlpdictionaries.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := dlpdictionaries.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -856,11 +998,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_engines": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPEngines - list, err := dlp_engines.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := dlp_engines.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -875,11 +1018,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_notification_templates": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPNotificationTemplates - jsonPayload, err := dlp_notification_templates.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := dlp_notification_templates.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -887,11 +1031,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_web_rules": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPWebRules - jsonPayload, err := dlp_web_rules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := dlp_web_rules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -899,11 +1044,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_rule": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.FilteringRules - rules, err := filteringrules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := filteringrules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -918,11 +1064,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(rulesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_destination_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.IPDestinationGroups - groups, err := ipdestinationgroups.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := ipdestinationgroups.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -937,11 +1084,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_ip_source_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.IPSourceGroups - groups, err := ipsourcegroups.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := ipsourcegroups.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -956,11 +1104,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_service": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkServices - services, err := networkservices.GetAllNetworkServices(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + services, err := networkservices.GetAllNetworkServices(ctx, service) if err != nil { log.Fatal(err) } @@ -975,11 +1124,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(servicesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_service_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkServiceGroups - jsonPayload, err := networkservicegroups.GetAllNetworkServiceGroups(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := networkservicegroups.GetAllNetworkServiceGroups(ctx, service) if err != nil { log.Fatal(err) } @@ -987,11 +1137,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_application_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkApplicationGroups - groups, err := networkapplicationgroups.GetAllNetworkApplicationGroups(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := networkapplicationgroups.GetAllNetworkApplicationGroups(ctx, service) if err != nil { log.Fatal(err) } @@ -1006,11 +1157,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_gre_tunnel": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.GRETunnels - jsonPayload, err := gretunnels.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := gretunnels.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1018,11 +1170,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_static_ip": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.StaticIPs - jsonPayload, err := staticips.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := staticips.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1030,11 +1183,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_vpn_credentials": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.VPNCredentials - jsonPayload, err := vpncredentials.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := vpncredentials.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1042,12 +1196,13 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_location_management": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.LocationManagement + // EXACTLY like the TF pattern: + service := api.ZIAService // Get all parent locations - jsonPayload, err := locationmanagement.GetAll(ziaClient) + jsonPayload, err := locationmanagement.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1056,7 +1211,7 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { _ = json.Unmarshal(m, &jsonStructData) // Get all sublocations - sublocationsPayload, err := locationmanagement.GetAllSublocations(ziaClient) + sublocationsPayload, err := locationmanagement.GetAllSublocations(ctx, service) if err != nil { log.Fatal(err) } @@ -1070,11 +1225,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount += subResourceCount case "zia_url_categories": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.URLCategories - list, err := urlcategories.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := urlcategories.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1089,7 +1245,7 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { } } for i := range items { - details, err := urlcategories.Get(ziaClient, items[i].ID) + details, err := urlcategories.Get(ctx, service, items[i].ID) if err != nil { continue } @@ -1100,11 +1256,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { _ = json.Unmarshal(m, &jsonStructData) case "zia_url_filtering_rules": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.URLFilteringPolicies - jsonPayload, err := urlfilteringpolicies.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := urlfilteringpolicies.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1112,11 +1269,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_rule_labels": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.RuleLabels - jsonPayload, err := rule_labels.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := rule_labels.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1124,11 +1282,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_auth_settings_urls": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.UserAuthenticationSettings - exemptedUrls, err := user_authentication_settings.Get(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + exemptedUrls, err := user_authentication_settings.Get(ctx, service) if err != nil { log.Fatal(err) } @@ -1137,11 +1296,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_sandbox_behavioral_analysis": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.SandboxSettings - hashes, err := sandbox_settings.Get(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + hashes, err := sandbox_settings.Get(ctx, service) if err != nil { // Handle the error response body and parse it for ZIA-specific errors apiErrorResponse := err.Error() // Assuming error contains response @@ -1159,11 +1319,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_security_settings": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.SecurityPolicySettings - urls, err := security_policy_settings.GetListUrls(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := security_policy_settings.GetListUrls(ctx, service) if err != nil { log.Fatal(err) } @@ -1172,11 +1333,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_forwarding_control_rule": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.ForwardingRules - rules, err := forwarding_rules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := forwarding_rules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1195,11 +1357,12 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { m, _ := json.Marshal(rulesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_forwarding_control_zpa_gateway": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.ZpaGateways - gws, err := ziaClient.GetAll() + // EXACTLY like the TF pattern: + service := api.ZIAService + gws, err := zpa_gateways.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1213,6 +1376,304 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { resourceCount = len(gwsFiltered) m, _ := json.Marshal(gwsFiltered) _ = json.Unmarshal(m, &jsonStructData) + case "zia_sandbox_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := sandbox_rules.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_ssl_inspection_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := sslinspection.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []sslinspection.SSLInspectionRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{ + "Office365 Inspection", "Zscaler Recommended Exemptions", "Inspect Remote Users", + "Office 365 One Click", "UCaaS One Click", "Default SSL Inspection Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_file_type_control_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := filetypecontrol.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_firewall_ips_rule": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := firewallipscontrolpolicies.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []firewallipscontrolpolicies.FirewallIPSRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{"Default Cloud IPS Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_firewall_dns_rule": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := firewalldnscontrolpolicies.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []firewalldnscontrolpolicies.FirewallDNSRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{"ZPA Resolver for Road Warrior", + "ZPA Resolver for Locations", "Critical risk DNS categories", "Critical risk DNS tunnels", + "High risk DNS categories", "High risk DNS tunnels", "Risky DNS categories", + "Risky DNS tunnels", "Office 365 One Click Rule", "Block DNS Tunnels", + "Block Filesharing DNS", "Block Gaming DNS", "UCaaS One Click Rule", + "Fallback ZPA Resolver for Locations", "Fallback ZPA Resolver for Road Warrior", "Unknown DNS Traffic", + "Default Firewall DNS Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + + // case "zia_advanced_settings": + // if api.ZIAService == nil { + // log.Fatal("ZIA service is not initialized") + // } + // // EXACTLY like the TF pattern: + // service := api.ZIAService + // advSettings, err := advanced_settings.GetAdvancedSettings(ctx, service) + // if err != nil { + // log.Fatal(err) + // } + // jsonPayload := []*advanced_settings.AdvancedSettings{advSettings} + // resourceCount = len(jsonPayload) + // m, _ := json.Marshal(jsonPayload) + // _ = json.Unmarshal(m, &jsonStructData) + + case "zia_advanced_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + advSettings, err := advanced_settings.GetAdvancedSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advanced_settings.AdvancedSettings{advSettings} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + // Force the "id" to be "advanced_settings" + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "advanced_settings" + } + + case "zia_atp_malicious_urls": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetMaliciousURLs(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.MaliciousURLs{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "all_urls" + } + + case "zia_atp_security_exceptions": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetSecurityExceptions(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.SecurityExceptions{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "bypass_url" + } + + case "zia_advanced_threat_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetAdvancedThreatSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.AdvancedThreatSettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "advanced_threat_settings" + } + + case "zia_atp_malware_inspection": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareInspection(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.ATPMalwareInspection{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "inspection" + } + + case "zia_atp_malware_protocols": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareProtocols(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.ATPMalwareProtocols{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "protocol" + } + + case "zia_atp_malware_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.MalwareSettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "malware_settings" + } + + case "zia_atp_malware_policy": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwarePolicy(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.MalwarePolicy{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "policy" + } + case "zia_url_filtering_and_cloud_app_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := urlfilteringpolicies.GetUrlAndAppSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*urlfilteringpolicies.URLAdvancedPolicySettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "app_setting" + } + case "zia_end_user_notification": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + notification, err := end_user_notification.GetUserNotificationSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*end_user_notification.UserNotificationSettings{notification} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "enduser_notification" + } default: fmt.Fprintf(cmd.OutOrStdout(), "%q is not yet supported for automatic generation", resourceType) return @@ -1255,11 +1716,22 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { sort.Strings(sortedBlockAttributes) for _, attrName := range sortedBlockAttributes { + apiAttrName := nesting.MapTfFieldNameToAPI(resourceType, attrName) if attrName == "id" || attrName == "tcp_port_ranges" || attrName == "udp_port_ranges" || attrName == "rule_order" { continue } + ty := r.Block.Attributes[attrName].AttributeType + + // (A) ADD THIS BLOCK: + // If this attribute is a boolean in the schema, but structData doesn’t have it at all, + // default it to false so WriteAttrLine will print “= false”. + if ty.Equals(cty.Bool) { + if _, present := structData[apiAttrName]; !present { + structData[apiAttrName] = false + } + } // Handle specific attributes for zpa_cloud_browser_isolation_external_profile if attrName == "banner_id" || attrName == "certificate_ids" || attrName == "region_ids" { value := structData[attrName] @@ -1281,13 +1753,14 @@ func generate(cmd *cobra.Command, writer io.Writer, resourceType string) { } continue } + // No need to output computed attributes that are also not optional. bypassAttributes := []string{"static_ip_id", "tunnel_id"} if r.Block.Attributes[attrName].Computed && !r.Block.Attributes[attrName].Optional && helpers.IsInList(attrName, bypassAttributes) { continue } - ty := r.Block.Attributes[attrName].AttributeType + // ty := r.Block.Attributes[attrName].AttributeType switch { case ty.IsPrimitiveType(): switch ty { diff --git a/cmd/generate_test.go b/cmd/generate_test.go index f6a2133..2d6c30b 100644 --- a/cmd/generate_test.go +++ b/cmd/generate_test.go @@ -1,23 +1,25 @@ -// Copyright (c) 2023 Zscaler Inc, +/* +Copyright (c) 2023 Zscaler Inc, -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package cmd diff --git a/cmd/import.go b/cmd/import.go index ec16773..8936ff5 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -1,27 +1,30 @@ -// Copyright (c) 2023 Zscaler Inc, +/* +Copyright (c) 2023 Zscaler Inc, -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package cmd import ( + "context" "encoding/json" "io" "os" @@ -32,50 +35,59 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_engines" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_notification_templates" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlp_web_rules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/dlp/dlpdictionaries" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/filteringrules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/ipdestinationgroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/ipsourcegroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkapplicationgroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkservicegroups" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/firewallpolicies/networkservices" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/forwarding_control_policy/forwarding_rules" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/forwarding_control_policy/zpa_gateways" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/location/locationmanagement" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/rule_labels" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/sandbox/sandbox_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/security_policy_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/gretunnels" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/staticips" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/trafficforwarding/vpncredentials" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/urlcategories" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/urlfilteringpolicies" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/user_authentication_settings" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/appconnectorgroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegment" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentbrowseraccess" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentinspection" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/applicationsegmentpra" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/appservercontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbibannercontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbicertificatecontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/cloudbrowserisolation/cbiprofilecontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/inspectioncontrol/inspection_custom_controls" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/lssconfigcontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/microtenants" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/policysetcontroller" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praapproval" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praconsole" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/pracredential" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/privilegedremoteaccess/praportal" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/provisioningkey" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/segmentgroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/servergroup" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services/serviceedgegroup" - "github.com/zscaler/zscaler-terraformer/teraformutils/helpers" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/advanced_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/advancedthreatsettings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_engines" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_notification_templates" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlp_web_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/dlp/dlpdictionaries" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/end_user_notification" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/filetypecontrol" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewalldnscontrolpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallipscontrolpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/filteringrules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/ipdestinationgroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/ipsourcegroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkapplicationgroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkservicegroups" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/firewallpolicies/networkservices" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/forwarding_control_policy/forwarding_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/forwarding_control_policy/zpa_gateways" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/location/locationmanagement" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/malware_protection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/rule_labels" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sandbox/sandbox_rules" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sandbox/sandbox_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/security_policy_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/sslinspection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/gretunnels" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/staticips" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/trafficforwarding/vpncredentials" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/urlcategories" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/urlfilteringpolicies" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia/services/user_authentication_settings" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/appconnectorgroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegment" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentbrowseraccess" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentinspection" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/applicationsegmentpra" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/appservercontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbibannercontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbicertificatecontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/cloudbrowserisolation/cbiprofilecontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/inspectioncontrol/inspection_custom_controls" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/lssconfigcontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/microtenants" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/policysetcontroller" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praapproval" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praconsole" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/pracredential" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/privilegedremoteaccess/praportal" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/provisioningkey" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/segmentgroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/servergroup" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa/services/serviceedgegroup" + "github.com/zscaler/zscaler-terraformer/terraformutils/helpers" ) // resourceImportStringFormats contains a mapping of the resource type to the @@ -128,6 +140,21 @@ var resourceImportStringFormats = map[string]string{ "zia_auth_settings_urls": ":id", "zia_security_settings": ":id", "zia_sandbox_behavioral_analysis": ":id", + "zia_sandbox_rules": ":id", + "zia_file_type_control_rules": ":id", + "zia_ssl_inspection_rules": ":id", + "zia_firewall_dns_rule": ":id", + "zia_firewall_ips_rule": ":id", + "zia_advanced_settings": ":id", + "zia_atp_malicious_urls": ":id", + "zia_atp_security_exceptions": ":id", + "zia_advanced_threat_settings": ":id", + "zia_atp_malware_inspection": ":id", + "zia_atp_malware_protocols": ":id", + "zia_atp_malware_settings": ":id", + "zia_atp_malware_policy": ":id", + "zia_url_filtering_and_cloud_app_settings": ":id", + "zia_end_user_notification": ":id", } func init() { @@ -183,7 +210,8 @@ func runImport() func(cmd *cobra.Command, args []string) { if helpers.IsInList(resourceTyp, excludedResourcesTypes) { continue } - importResource(cmd, cmd.OutOrStdout(), resourceTyp, managedResourceTypes, includedSensitiveResources) + importResource(cmd.Context(), cmd, cmd.OutOrStdout(), resourceTyp, managedResourceTypes, includedSensitiveResources) + } if len(managedResourceTypes) > 0 { fmt.Println("\033[33mThe following resources are already managed by Terraform:\033[0m") @@ -200,7 +228,8 @@ func runImport() func(cmd *cobra.Command, args []string) { } return } - importResource(cmd, cmd.OutOrStdout(), resourceType_, managedResourceTypes, includedSensitiveResources) + importResource(cmd.Context(), cmd, cmd.OutOrStdout(), resourceType_, managedResourceTypes, includedSensitiveResources) + if len(managedResourceTypes) > 0 { fmt.Println("\033[33mThe following resources are already managed by Terraform:\033[0m") for resource := range managedResourceTypes { @@ -216,16 +245,19 @@ func runImport() func(cmd *cobra.Command, args []string) { } } } -func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, managedResourceTypes map[string]bool, includedSensitiveResources map[string]bool) { + +func importResource(ctx context.Context, cmd *cobra.Command, writer io.Writer, resourceType string, managedResourceTypes map[string]bool, includedSensitiveResources map[string]bool) { var jsonStructData []interface{} resourceCount := 0 switch resourceType { case "zpa_app_connector_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.AppConnectorGroup - list, _, err := appconnectorgroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + + list, _, err := appconnectorgroup.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -246,11 +278,13 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_server": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.AppServerController - jsonPayload, _, err := appservercontroller.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + + jsonPayload, _, err := appservercontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -258,11 +292,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegment - jsonPayload, _, err := applicationsegment.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegment.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -275,11 +310,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m } resourceCount = len(jsonStructData) case "zpa_application_segment_browser_access": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.BrowserAccess - jsonPayload, _, err := applicationsegmentbrowseraccess.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentbrowseraccess.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -292,11 +328,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m } resourceCount = len(jsonStructData) case "zpa_application_segment_inspection": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegmentInspection - jsonPayload, _, err := applicationsegmentinspection.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentinspection.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -304,11 +341,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_application_segment_pra": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ApplicationSegmentPRA - jsonPayload, _, err := applicationsegmentpra.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := applicationsegmentpra.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -316,18 +354,19 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_cloud_browser_isolation_external_profile": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiExternalProfile + // EXACTLY like the TF pattern: + service := api.ZPAService - allProfiles, _, err := cbiprofilecontroller.GetAll(zpaClient) + allProfiles, _, err := cbiprofilecontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } for _, profile := range allProfiles { - profileDetails, _, err := cbiprofilecontroller.GetByNameOrID(zpaClient, profile.ID) + profileDetails, _, err := cbiprofilecontroller.GetByNameOrID(ctx, service, profile.ID) if err != nil { log.Printf("error retrieving profile %s: %v", profile.ID, err) continue @@ -341,13 +380,14 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonStructData) case "zpa_cloud_browser_isolation_banner": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiBanner + // EXACTLY like the TF pattern: + service := api.ZPAService // Retrieve all resources using GetAll - allBanners, _, err := cbibannercontroller.GetAll(zpaClient) + allBanners, _, err := cbibannercontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -355,7 +395,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m // Collect the payload data for _, banner := range allBanners { // Use the ID or name to get the full details of the banner - bannerDetails, _, err := cbibannercontroller.GetByNameOrID(zpaClient, banner.ID) + bannerDetails, _, err := cbibannercontroller.GetByNameOrID(ctx, service, banner.ID) if err != nil { log.Printf("error retrieving banner %s: %v", banner.ID, err) continue @@ -368,13 +408,14 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonStructData) case "zpa_cloud_browser_isolation_certificate": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.CbiCertificate + // EXACTLY like the TF pattern: + service := api.ZPAService // Retrieve all resources using GetAll - allCerts, _, err := cbicertificatecontroller.GetAll(zpaClient) + allCerts, _, err := cbicertificatecontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -384,7 +425,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m if certificate.Name == "Zscaler Root Certificate" { continue } - certDetails, _, err := cbicertificatecontroller.GetByNameOrID(zpaClient, certificate.ID) + certDetails, _, err := cbicertificatecontroller.GetByNameOrID(ctx, service, certificate.ID) if err != nil { log.Printf("error retrieving certificate %s: %v", certificate.ID, err) continue @@ -397,11 +438,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonStructData) case "zpa_pra_approval_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAApproval - jsonPayload, _, err := praapproval.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praapproval.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -415,11 +457,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_pra_console_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAConsole - jsonPayload, _, err := praconsole.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praconsole.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -433,11 +476,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_pra_credential_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRACredential - jsonPayload, _, err := pracredential.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := pracredential.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -452,11 +496,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m _ = json.Unmarshal(m, &jsonStructData) includedSensitiveResources[resourceType] = true case "zpa_pra_portal_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PRAPortal - jsonPayload, _, err := praportal.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := praportal.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -470,11 +515,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_segment_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.SegmentGroup - list, _, err := segmentgroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := segmentgroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -489,11 +535,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_server_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ServerGroup - list, _, err := servergroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := servergroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -508,11 +555,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_access_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "ACCESS_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "ACCESS_POLICY") if err != nil { log.Fatal(err) } @@ -527,11 +575,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_timeout_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "TIMEOUT_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "TIMEOUT_POLICY") if err != nil { log.Fatal(err) } @@ -546,11 +595,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_forwarding_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "CLIENT_FORWARDING_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "CLIENT_FORWARDING_POLICY") if err != nil { log.Fatal(err) } @@ -565,11 +615,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_inspection_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "INSPECTION_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "INSPECTION_POLICY") if err != nil { log.Fatal(err) } @@ -584,11 +635,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_policy_isolation_rule": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.PolicySetController - list, _, err := policysetcontroller.GetAllByType(zpaClient, "ISOLATION_POLICY") + // EXACTLY like the TF pattern: + service := api.ZPAService + list, _, err := policysetcontroller.GetAllByType(ctx, service, "ISOLATION_POLICY") if err != nil { log.Fatal(err) } @@ -603,11 +655,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m m, _ := json.Marshal(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_provisioning_key": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ProvisioningKey - jsonPayload, err := provisioningkey.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, err := provisioningkey.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -615,11 +668,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_service_edge_group": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.ServiceEdgeGroup - edgeGroups, _, err := serviceedgegroup.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + edgeGroups, _, err := serviceedgegroup.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -635,11 +689,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m m, _ := json.Marshal(edgeGroups) _ = json.Unmarshal(m, &jsonStructData) case "zpa_lss_config_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.LSSConfigController - jsonPayload, _, err := lssconfigcontroller.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := lssconfigcontroller.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -647,11 +702,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_inspection_custom_controls": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.InspectionCustomControls - jsonPayload, _, err := inspection_custom_controls.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := inspection_custom_controls.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -665,11 +721,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zpa_microtenant_controller": - if api.ZPA == nil { - log.Fatal("ZPA client is not initialized") + if api.ZPAService == nil { + log.Fatal("ZPA service is not initialized") } - zpaClient := api.ZPA.MicroTenants - jsonPayload, _, err := microtenants.GetAll(zpaClient) + // EXACTLY like the TF pattern: + service := api.ZPAService + jsonPayload, _, err := microtenants.GetAll(ctx, service) if err != nil { isLicErr, reason := isLicenseError(err) // If it's a license error, log and continue, otherwise, terminate. @@ -689,11 +746,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(filteredPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_dictionaries": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPDictionaries - list, err := dlpdictionaries.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := dlpdictionaries.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -708,11 +766,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_engines": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPEngines - list, err := dlp_engines.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := dlp_engines.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -727,11 +786,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_notification_templates": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPNotificationTemplates - jsonPayload, err := dlp_notification_templates.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := dlp_notification_templates.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -739,11 +799,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_dlp_web_rules": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.DLPWebRules - jsonPayload, err := dlp_web_rules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := dlp_web_rules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -751,11 +812,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_rule": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.FilteringRules - rules, err := filteringrules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := filteringrules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -770,11 +832,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(rulesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_destination_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.IPDestinationGroups - groups, err := ipdestinationgroups.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := ipdestinationgroups.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -789,11 +852,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_ip_source_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.IPSourceGroups - groups, err := ipsourcegroups.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := ipsourcegroups.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -808,11 +872,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_service": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkServices - services, err := networkservices.GetAllNetworkServices(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + services, err := networkservices.GetAllNetworkServices(ctx, service) if err != nil { log.Fatal(err) } @@ -827,11 +892,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(servicesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_service_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkServiceGroups - jsonPayload, err := networkservicegroups.GetAllNetworkServiceGroups(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := networkservicegroups.GetAllNetworkServiceGroups(ctx, service) if err != nil { log.Fatal(err) } @@ -839,11 +905,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_firewall_filtering_network_application_groups": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.NetworkApplicationGroups - groups, err := networkapplicationgroups.GetAllNetworkApplicationGroups(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + groups, err := networkapplicationgroups.GetAllNetworkApplicationGroups(ctx, service) if err != nil { log.Fatal(err) } @@ -858,11 +925,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(groupsFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_gre_tunnel": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.GRETunnels - jsonPayload, err := gretunnels.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := gretunnels.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -870,11 +938,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_static_ip": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.StaticIPs - jsonPayload, err := staticips.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := staticips.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -882,11 +951,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_traffic_forwarding_vpn_credentials": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.VPNCredentials - jsonPayload, err := vpncredentials.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := vpncredentials.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -894,12 +964,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_location_management": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.LocationManagement - // Get all parent locations - jsonPayload, err := locationmanagement.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := locationmanagement.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -908,7 +978,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m _ = json.Unmarshal(m, &jsonStructData) // Get all sublocations - sublocationsPayload, err := locationmanagement.GetAllSublocations(ziaClient) + sublocationsPayload, err := locationmanagement.GetAllSublocations(ctx, service) if err != nil { log.Fatal(err) } @@ -922,11 +992,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount += subResourceCount case "zia_url_categories": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.URLCategories - list, err := urlcategories.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + list, err := urlcategories.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -941,7 +1012,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m } } for i := range items { - details, err := urlcategories.Get(ziaClient, items[i].ID) + details, err := urlcategories.Get(ctx, service, items[i].ID) if err != nil { continue } @@ -951,11 +1022,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(items) _ = json.Unmarshal(m, &jsonStructData) case "zia_url_filtering_rules": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.URLFilteringPolicies - jsonPayload, err := urlfilteringpolicies.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := urlfilteringpolicies.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -963,11 +1035,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_rule_labels": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.RuleLabels - jsonPayload, err := rule_labels.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := rule_labels.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -975,11 +1048,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_auth_settings_urls": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.UserAuthenticationSettings - exemptedUrls, err := user_authentication_settings.Get(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + exemptedUrls, err := user_authentication_settings.Get(ctx, service) if err != nil { log.Fatal(err) } @@ -988,11 +1062,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_sandbox_behavioral_analysis": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.SandboxSettings - hashes, err := sandbox_settings.Get(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + hashes, err := sandbox_settings.Get(ctx, service) if err != nil { // Handle the error response body and parse it for ZIA-specific errors apiErrorResponse := err.Error() // Assuming error contains response @@ -1010,11 +1085,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_security_settings": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.SecurityPolicySettings - urls, err := security_policy_settings.GetListUrls(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := security_policy_settings.GetListUrls(ctx, service) if err != nil { log.Fatal(err) } @@ -1023,11 +1099,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(jsonPayload) _ = json.Unmarshal(m, &jsonStructData) case "zia_forwarding_control_rule": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.ForwardingRules - rules, err := forwarding_rules.GetAll(ziaClient) + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := forwarding_rules.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1046,11 +1123,12 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m resourceCount = len(rulesFiltered) _ = json.Unmarshal(m, &jsonStructData) case "zia_forwarding_control_zpa_gateway": - if api.ZIA == nil { - log.Fatal("ZIA client is not initialized") + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") } - ziaClient := api.ZIA.ZpaGateways - gws, err := ziaClient.GetAll() + // EXACTLY like the TF pattern: + service := api.ZIAService + gws, err := zpa_gateways.GetAll(ctx, service) if err != nil { log.Fatal(err) } @@ -1064,6 +1142,289 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m m, _ := json.Marshal(gwsFiltered) resourceCount = len(gwsFiltered) _ = json.Unmarshal(m, &jsonStructData) + case "zia_sandbox_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := sandbox_rules.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_ssl_inspection_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := sslinspection.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []sslinspection.SSLInspectionRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{ + "Office365 Inspection", "Zscaler Recommended Exemptions", "Inspect Remote Users", + "Office 365 One Click", "UCaaS One Click", "Default SSL Inspection Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_file_type_control_rules": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + jsonPayload, err := filetypecontrol.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_firewall_ips_rule": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := firewallipscontrolpolicies.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []firewallipscontrolpolicies.FirewallIPSRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{"Default Cloud IPS Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + case "zia_firewall_dns_rule": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + rules, err := firewalldnscontrolpolicies.GetAll(ctx, service) + if err != nil { + log.Fatal(err) + } + rulesFiltered := []firewalldnscontrolpolicies.FirewallDNSRules{} + for _, rule := range rules { + if helpers.IsInList(rule.Name, []string{"ZPA Resolver for Road Warrior", + "ZPA Resolver for Locations", "Critical risk DNS categories", "Critical risk DNS tunnels", + "High risk DNS categories", "High risk DNS tunnels", "Risky DNS categories", + "Risky DNS tunnels", "Office 365 One Click Rule", "Block DNS Tunnels", + "Block Filesharing DNS", "Block Gaming DNS", "UCaaS One Click Rule", + "Fallback ZPA Resolver for Locations", "Fallback ZPA Resolver for Road Warrior", "Unknown DNS Traffic", + "Default Firewall DNS Rule"}) { + continue + } + rulesFiltered = append(rulesFiltered, rule) + } + resourceCount = len(rulesFiltered) + m, _ := json.Marshal(rulesFiltered) + _ = json.Unmarshal(m, &jsonStructData) + + case "zia_advanced_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + advSettings, err := advanced_settings.GetAdvancedSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advanced_settings.AdvancedSettings{advSettings} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + // Force the "id" to be "advanced_settings" + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "advanced_settings" + } + + case "zia_atp_malicious_urls": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetMaliciousURLs(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.MaliciousURLs{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "all_urls" + } + + case "zia_atp_security_exceptions": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetSecurityExceptions(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.SecurityExceptions{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "bypass_url" + } + + case "zia_advanced_threat_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := advancedthreatsettings.GetAdvancedThreatSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*advancedthreatsettings.AdvancedThreatSettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "advanced_threat_settings" + } + + case "zia_atp_malware_inspection": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareInspection(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.ATPMalwareInspection{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "inspection" + } + + case "zia_atp_malware_protocols": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareProtocols(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.ATPMalwareProtocols{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "protocol" + } + + case "zia_atp_malware_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwareSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.MalwareSettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "malware_settings" + } + + case "zia_atp_malware_policy": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := malware_protection.GetATPMalwarePolicy(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*malware_protection.MalwarePolicy{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "policy" + } + case "zia_url_filtering_and_cloud_app_settings": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + urls, err := urlfilteringpolicies.GetUrlAndAppSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*urlfilteringpolicies.URLAdvancedPolicySettings{urls} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "app_setting" + } + case "zia_end_user_notification": + if api.ZIAService == nil { + log.Fatal("ZIA service is not initialized") + } + // EXACTLY like the TF pattern: + service := api.ZIAService + notification, err := end_user_notification.GetUserNotificationSettings(ctx, service) + if err != nil { + log.Fatal(err) + } + jsonPayload := []*end_user_notification.UserNotificationSettings{notification} + resourceCount = len(jsonPayload) + m, _ := json.Marshal(jsonPayload) + _ = json.Unmarshal(m, &jsonStructData) + if len(jsonStructData) > 0 { + dataMap := jsonStructData[0].(map[string]interface{}) + dataMap["id"] = "enduser_notification" + } default: log.Printf("%q is not yet supported for state import", resourceType) return @@ -1078,7 +1439,7 @@ func importResource(cmd *cobra.Command, writer io.Writer, resourceType string, m if err != nil { log.Fatal(err) } - generate(cmd, f, resourceType) + generate(ctx, cmd, f, resourceType) // Ensure the file is closed and check for errors when closing if err := f.Close(); err != nil { log.Fatalf("failed to close file: %v", err) diff --git a/cmd/root.go b/cmd/root.go index 2cd6647..08f9864 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,23 +1,25 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package cmd @@ -30,23 +32,48 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/zscaler/zscaler-terraformer/providers/zia" - "github.com/zscaler/zscaler-terraformer/providers/zpa" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler" + zia "github.com/zscaler/zscaler-terraformer/providers/zia" + zpa "github.com/zscaler/zscaler-terraformer/providers/zpa" ) var log = logrus.New() var terraformInstallPath string -var zpa_cloud, zpa_client_id, zpa_client_secret, zpa_customer_id string -var zia_cloud, zia_username, zia_password, zia_api_key string + +// ONEAPI Fields. +var oneAPIClientID string // required +var oneAPIClientSecret string // required +var oneAPIVanityDomain string // required +var oneAPICustomerID string // optional +var oneAPIMicrotenantID string // optional +var oneAPICloud string // optional + +// ZPA Legacy Fields. +var zpaClientID string // required +var zpaClientSecret string // required +var zpaCustomerID string // required +var zpaMicrotenantID string // optional +var zpaCloud string // optional + +// ZIA Legacy Fields. +var ziaUsername string // required +var ziaPassword string // required +var ziaAPIKey string // required +var ziaCloud string // required + +var useLegacyClient bool var verbose, displayReleaseVersion bool var supportedResources string + +var resourceType_, resources, excludedResources string + var api *Client var terraformImportCmdPrefix = "terraform import" -var zpaProviderNamespace string +var zpaProviderNamespace, ziaProviderNamespace string type Client struct { - ZPA *zpa.Client - ZIA *zia.Client + ZPAService *zscaler.Service + ZIAService *zscaler.Service } var allSupportedResources = []string{ @@ -97,6 +124,16 @@ var allSupportedResources = []string{ "zia_security_settings", "zia_forwarding_control_zpa_gateway", "zia_forwarding_control_rule", + "zia_sandbox_rules", + "zia_file_type_control_rules", + "zia_ssl_inspection_rules", + "zia_firewall_dns_rule", + "zia_firewall_ips_rule", + "zia_advanced_settings", + "zia_advanced_threat_settings", + "zia_atp_malicious_urls", + "zia_end_user_notification", + "zia_url_filtering_and_cloud_app_settings", } // rootCmd represents the base command when called without any subcommands. @@ -146,105 +183,274 @@ func Execute() error { func init() { cobra.OnInitialize(initConfig) - // Define flags and configuration settings. - // ZPA API credentials - rootCmd.PersistentFlags().StringVarP(&zpa_client_id, "zpa_client_id", "", "", "ZPA client ID") - _ = viper.BindPFlag("zpa_client_id", rootCmd.PersistentFlags().Lookup("zpa_client_id")) - _ = viper.BindEnv("zpa_client_id", "ZPA_CLIENT_ID") + // ----------------------- + // OneAPI flags (V3) + // ----------------------- + rootCmd.PersistentFlags().StringVar(&oneAPIClientID, "client_id", "", "OneAPI client_id (required in V3 mode)") + if err := viper.BindPFlag("client_id", rootCmd.PersistentFlags().Lookup("client_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("client_id", "ZSCALER_CLIENT_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zpa_client_secret, "zpa_client_secret", "", "", "ZPA client secret") - _ = viper.BindPFlag("zpa_client_secret", rootCmd.PersistentFlags().Lookup("zpa_client_secret")) - _ = viper.BindEnv("zpa_client_secret", "ZPA_CLIENT_SECRET") + rootCmd.PersistentFlags().StringVar(&oneAPIClientSecret, "client_secret", "", "OneAPI client_secret (required in V3 mode)") + if err := viper.BindPFlag("client_secret", rootCmd.PersistentFlags().Lookup("client_secret")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("client_secret", "ZSCALER_CLIENT_SECRET"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zpa_customer_id, "zpa_customer_id", "", "", "ZPA Customer ID") - _ = viper.BindPFlag("zpa_customer_id", rootCmd.PersistentFlags().Lookup("zpa_customer_id")) - _ = viper.BindEnv("zpa_customer_id", "ZPA_CUSTOMER_ID") + rootCmd.PersistentFlags().StringVar(&oneAPIVanityDomain, "vanity_domain", "", "OneAPI vanity_domain (required in V3 mode)") + if err := viper.BindPFlag("vanity_domain", rootCmd.PersistentFlags().Lookup("vanity_domain")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("vanity_domain", "ZSCALER_VANITY_DOMAIN"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zpa_cloud, "zpa_cloud", "", "", "ZPA Cloud (BETA, GOV, GOVUS, PRODUCTION, ZPATWO)") - _ = viper.BindPFlag("zpa_cloud", rootCmd.PersistentFlags().Lookup("zpa_cloud")) - _ = viper.BindEnv("zpa_cloud", "ZPA_CLOUD") + rootCmd.PersistentFlags().StringVar(&oneAPICustomerID, "customer_id", "", "OneAPI optional customer_id") + if err := viper.BindPFlag("customer_id", rootCmd.PersistentFlags().Lookup("customer_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("customer_id", "ZPA_CUSTOMER_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - // ZIA API credentials - rootCmd.PersistentFlags().StringVarP(&zia_username, "zia_username", "", "", "ZIA username") - _ = viper.BindPFlag("zia_username", rootCmd.PersistentFlags().Lookup("zia_username")) - _ = viper.BindEnv("zia_username", "ZIA_USERNAME") + rootCmd.PersistentFlags().StringVar(&oneAPIMicrotenantID, "microtenant_id", "", "OneAPI optional microtenant_id") + if err := viper.BindPFlag("microtenant_id", rootCmd.PersistentFlags().Lookup("microtenant_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("microtenant_id", "ZPA_MICROTENANT_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zia_password, "zia_password", "", "", "ZIA password") - _ = viper.BindPFlag("zia_password", rootCmd.PersistentFlags().Lookup("zia_password")) - _ = viper.BindEnv("zia_password", "ZIA_PASSWORD") + rootCmd.PersistentFlags().StringVar(&oneAPICloud, "zscaler_cloud", "", "OneAPI optional zscaler_cloud (e.g. PRODUCTION)") + if err := viper.BindPFlag("zscaler_cloud", rootCmd.PersistentFlags().Lookup("zscaler_cloud")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zscaler_cloud", "ZSCALER_CLOUD"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zia_api_key, "zia_api_key", "", "", "ZIA API Key") - _ = viper.BindPFlag("zia_api_key", rootCmd.PersistentFlags().Lookup("zia_api_key")) - _ = viper.BindEnv("zia_api_key", "ZIA_API_KEY") + // ----------------------- + // ZPA Legacy flags (V2) + // ----------------------- + rootCmd.PersistentFlags().StringVar(&zpaClientID, "zpa_client_id", "", "ZPA legacy client ID (required if using legacy mode for ZPA resources)") + if err := viper.BindPFlag("zpa_client_id", rootCmd.PersistentFlags().Lookup("zpa_client_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa_client_id", "ZPA_CLIENT_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVarP(&zia_cloud, "zia_cloud", "", "", "ZIA Cloud (i.e zscalerthree)") - _ = viper.BindPFlag("zia_cloud", rootCmd.PersistentFlags().Lookup("zia_cloud")) - _ = viper.BindEnv("zia_cloud", "ZIA_CLOUD") + rootCmd.PersistentFlags().StringVar(&zpaClientSecret, "zpa_client_secret", "", "ZPA legacy client secret") + if err := viper.BindPFlag("zpa_client_secret", rootCmd.PersistentFlags().Lookup("zpa_client_secret")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa_client_secret", "ZPA_CLIENT_SECRET"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVar(&excludedResources, "exclude", "", "Which resources you wish to exclude") + rootCmd.PersistentFlags().StringVar(&zpaCustomerID, "zpa_customer_id", "", "ZPA legacy customer ID") + if err := viper.BindPFlag("zpa_customer_id", rootCmd.PersistentFlags().Lookup("zpa_customer_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa_customer_id", "ZPA_CUSTOMER_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVar(&resourceType_, "resource-type", "", "Which resource you wish to generate") + rootCmd.PersistentFlags().StringVar(&zpaMicrotenantID, "zpa_microtenant_id", "", "ZPA legacy microtenant_id (optional)") + if err := viper.BindPFlag("zpa_microtenant_id", rootCmd.PersistentFlags().Lookup("zpa_microtenant_id")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa_microtenant_id", "ZPA_MICROTENANT_ID"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVar(&resources, "resources", "", "Which resources you wish to import") + rootCmd.PersistentFlags().StringVar(&zpaCloud, "zpa_cloud", "", "ZPA Cloud environment (optional, e.g. PRODUCTION)") + if err := viper.BindPFlag("zpa_cloud", rootCmd.PersistentFlags().Lookup("zpa_cloud")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa_cloud", "ZPA_CLOUD"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().BoolP("help", "h", false, "Show help for zscaler-terraformer") + // ----------------------- + // ZIA Legacy flags (V2) + // ----------------------- + rootCmd.PersistentFlags().StringVar(&ziaUsername, "zia_username", "", "ZIA legacy username (required if using legacy mode for ZIA resources)") + if err := viper.BindPFlag("zia_username", rootCmd.PersistentFlags().Lookup("zia_username")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia_username", "ZIA_USERNAME"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().StringVar(&supportedResources, "supported-resources", "", "List supported resources for ZPA or ZIA") + rootCmd.PersistentFlags().StringVar(&ziaPassword, "zia_password", "", "ZIA legacy password (required)") + if err := viper.BindPFlag("zia_password", rootCmd.PersistentFlags().Lookup("zia_password")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia_password", "ZIA_PASSWORD"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } - rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Specify verbose output (same as setting log level to debug)") + rootCmd.PersistentFlags().StringVar(&ziaAPIKey, "zia_api_key", "", "ZIA legacy api_key (required)") + if err := viper.BindPFlag("zia_api_key", rootCmd.PersistentFlags().Lookup("zia_api_key")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia_api_key", "ZIA_API_KEY"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } + + rootCmd.PersistentFlags().StringVar(&ziaCloud, "zia_cloud", "", "ZIA Cloud environment (required for ZIA legacy, e.g. zscalerthree)") + if err := viper.BindPFlag("zia_cloud", rootCmd.PersistentFlags().Lookup("zia_cloud")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia_cloud", "ZIA_CLOUD"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } + + // ----------------------- + // Global toggle + // ----------------------- + rootCmd.PersistentFlags().BoolVar(&useLegacyClient, "use_legacy_client", false, "Enable Legacy Mode (true/false)") + if err := viper.BindPFlag("use_legacy_client", rootCmd.PersistentFlags().Lookup("use_legacy_client")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("use_legacy_client", "ZSCALER_USE_LEGACY_CLIENT"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } + // ----------------------- + // Additional flags + // ----------------------- + rootCmd.PersistentFlags().StringVar(&excludedResources, "exclude", "", "Which resources you wish to exclude") + rootCmd.PersistentFlags().StringVar(&resourceType_, "resource-type", "", "Which resource you wish to generate") + rootCmd.PersistentFlags().StringVar(&resources, "resources", "", "Which resources you wish to import") + rootCmd.PersistentFlags().BoolP("help", "h", false, "Show help for zscaler-terraformer") + rootCmd.PersistentFlags().StringVar(&supportedResources, "supported-resources", "", "List supported resources for ZPA or ZIA") + rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose debug output") rootCmd.PersistentFlags().BoolVarP(&displayReleaseVersion, "version", "", false, "Display the release version") rootCmd.PersistentFlags().StringVar(&terraformInstallPath, "terraform-install-path", ".", "Path to the default Terraform installation") - _ = viper.BindPFlag("terraform-install-path", rootCmd.PersistentFlags().Lookup("terraform-install-path")) - _ = viper.BindEnv("terraform-install-path", "ZSCALER_TERRAFORM_INSTALL_PATH") + if err := viper.BindPFlag("terraform-install-path", rootCmd.PersistentFlags().Lookup("terraform-install-path")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("terraform-install-path", "ZSCALER_TERRAFORM_INSTALL_PATH"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } rootCmd.PersistentFlags().StringVar(&terraformInstallPath, "zpa-terraform-install-path", ".", "Path to the ZPA Terraform installation") - _ = viper.BindPFlag("zpa-terraform-install-path", rootCmd.PersistentFlags().Lookup("zpa-terraform-install-path")) - _ = viper.BindEnv("zpa-terraform-install-path", "ZSCALER_ZPA_TERRAFORM_INSTALL_PATH") + if err := viper.BindPFlag("zpa-terraform-install-path", rootCmd.PersistentFlags().Lookup("zpa-terraform-install-path")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa-terraform-install-path", "ZSCALER_ZPA_TERRAFORM_INSTALL_PATH"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } rootCmd.PersistentFlags().StringVar(&terraformInstallPath, "zia-terraform-install-path", ".", "Path to the ZIA Terraform installation") - _ = viper.BindPFlag("zia-terraform-install-path", rootCmd.PersistentFlags().Lookup("zia-terraform-install-path")) - _ = viper.BindEnv("zia-terraform-install-path", "ZSCALER_ZIA_TERRAFORM_INSTALL_PATH") + if err := viper.BindPFlag("zia-terraform-install-path", rootCmd.PersistentFlags().Lookup("zia-terraform-install-path")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia-terraform-install-path", "ZSCALER_ZIA_TERRAFORM_INSTALL_PATH"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } rootCmd.PersistentFlags().StringVar(&zpaProviderNamespace, "zpa-provider-namespace", "", "Custom namespace for the ZPA provider") - _ = viper.BindPFlag("zpa-provider-namespace", rootCmd.PersistentFlags().Lookup("zpa-provider-namespace")) - _ = viper.BindEnv("zpa-provider-namespace", "ZPA_PROVIDER_NAMESPACE") - - rootCmd.PersistentFlags().StringVar(&zpaProviderNamespace, "zia-provider-namespace", "", "Custom namespace for the ZIA provider") - _ = viper.BindPFlag("zia-provider-namespace", rootCmd.PersistentFlags().Lookup("zia-provider-namespace")) - _ = viper.BindEnv("zia-provider-namespace", "ZIA_PROVIDER_NAMESPACE") + if err := viper.BindPFlag("zpa-provider-namespace", rootCmd.PersistentFlags().Lookup("zpa-provider-namespace")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zpa-provider-namespace", "ZPA_PROVIDER_NAMESPACE"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } + rootCmd.PersistentFlags().StringVar(&ziaProviderNamespace, "zia-provider-namespace", "", "Custom namespace for the ZIA provider") + if err := viper.BindPFlag("zia-provider-namespace", rootCmd.PersistentFlags().Lookup("zia-provider-namespace")); err != nil { + log.Fatalf("failed to bind flag: %v", err) + } + if err := viper.BindEnv("zia-provider-namespace", "ZIA_PROVIDER_NAMESPACE"); err != nil { + log.Fatalf("failed to bind env: %v", err) + } } -// initConfig reads in config file and ENV variables if set. func initConfig() { - viper.AutomaticEnv() // read in environment variables that match - viper.SetEnvPrefix("") - - // Ensure ZSCALER_SDK_CACHE_DISABLED is set to true - err := os.Setenv("ZSCALER_SDK_CACHE_DISABLED", "true") - if err != nil { - log.Fatalf("failed to set environment variable ZSCALER_SDK_CACHE_DISABLED: %v", err) - } + viper.AutomaticEnv() // read environment variables if set + viper.SetEnvPrefix("") // optional prefix, can be removed if undesired + // Set log level var cfgLogLevel = logrus.InfoLevel - if verbose { cfgLogLevel = logrus.DebugLevel } - log.SetLevel(cfgLogLevel) - // Debugging statements to verify the values - log.Debug("ZPA Client ID:", viper.GetString("zpa_client_id")) - log.Debug("ZPA Client Secret:", viper.GetString("zpa_client_secret")) - log.Debug("ZPA Customer ID:", viper.GetString("zpa_customer_id")) - log.Debug("ZPA Cloud:", viper.GetString("zpa_cloud")) - log.Debug("ZIA Username:", viper.GetString("zia_username")) - log.Debug("ZIA Password:", viper.GetString("zia_password")) - log.Debug("ZIA API Key:", viper.GetString("zia_api_key")) - log.Debug("ZIA Cloud:", viper.GetString("zia_cloud")) + // Read the toggle + useLegacyClient = viper.GetBool("use_legacy_client") + + // ----------------------- + // Read the CLI or env values into our global variables + // ----------------------- + // OneAPI + oneAPIClientID = viper.GetString("client_id") + oneAPIClientSecret = viper.GetString("client_secret") + oneAPIVanityDomain = viper.GetString("vanity_domain") + oneAPICustomerID = viper.GetString("customer_id") + oneAPIMicrotenantID = viper.GetString("microtenant_id") + oneAPICloud = viper.GetString("zscaler_cloud") + + // ZPA legacy + zpaClientID = viper.GetString("zpa_client_id") + zpaClientSecret = viper.GetString("zpa_client_secret") + zpaCustomerID = viper.GetString("zpa_customer_id") + zpaMicrotenantID = viper.GetString("zpa_microtenant_id") + zpaCloud = viper.GetString("zpa_cloud") + + // ZIA legacy + ziaUsername = viper.GetString("zia_username") + ziaPassword = viper.GetString("zia_password") + ziaAPIKey = viper.GetString("zia_api_key") + ziaCloud = viper.GetString("zia_cloud") + + // Debug logs of what we got + // log.Debugf("use_legacy_client=%v", useLegacyClient) + // log.Debugf("[ONEAPI] client_id=%s, client_secret=%s, vanity_domain=%s, customer_id=%s, microtenant_id=%s, zscaler_cloud=%s", + // oneAPIClientID, oneAPIClientSecret, oneAPIVanityDomain, oneAPICustomerID, oneAPIMicrotenantID, oneAPICloud) + // log.Debugf("[ZPA Legacy] zpa_client_id=%s, zpa_client_secret=%s, zpa_customer_id=%s, zpa_microtenant_id=%s, zpa_cloud=%s", + // zpaClientID, zpaClientSecret, zpaCustomerID, zpaMicrotenantID, zpaCloud) + // log.Debugf("[ZIA Legacy] zia_username=%s, zia_password=%s, zia_api_key=%s, zia_cloud=%s", + // ziaUsername, ziaPassword, ziaAPIKey, ziaCloud) + + log.Debug("[INFO] initConfig success (no validation).") + + // ---------------------------------------------------- + // FIX: Bridge the values from these top-level variables + // into the EXACT viper keys that the providers use. + // ---------------------------------------------------- + // For OneAPI in providers/zpa/client.go or providers/zia/client.go: + viper.Set("client_id", oneAPIClientID) + viper.Set("client_secret", oneAPIClientSecret) + viper.Set("vanity_domain", oneAPIVanityDomain) + viper.Set("customer_id", oneAPICustomerID) + viper.Set("microtenant_id", oneAPIMicrotenantID) + viper.Set("zscaler_cloud", oneAPICloud) + + // For ZPA Legacy in providers/zpa/client.go: + viper.Set("zpa_client_id", zpaClientID) + viper.Set("zpa_client_secret", zpaClientSecret) + viper.Set("zpa_customer_id", zpaCustomerID) + viper.Set("zpa_microtenant_id", zpaMicrotenantID) + viper.Set("zpa_cloud", zpaCloud) + + // For ZIA Legacy (providers/zia/client.go or similar): + viper.Set("username", ziaUsername) // your code calls viper.GetString("username") + viper.Set("password", ziaPassword) + viper.Set("api_key", ziaAPIKey) + viper.Set("zia_cloud", ziaCloud) // some code calls viper.GetString("zia_cloud") + + // Also set the legacy toggle for the second layer: + viper.Set("use_legacy_client", useLegacyClient) } func sharedPreRun(cmd *cobra.Command, args []string) { @@ -252,27 +458,38 @@ func sharedPreRun(cmd *cobra.Command, args []string) { if api == nil { api = &Client{} } - if strings.HasPrefix(resourceType_, "zpa_") || strings.Contains(resources, "zpa_") || resources == "*" || resources == "zpa" { - zpaClient, err := zpa.NewClient() + if wantsZPA(resourceType_, resources) { + zpaCli, err := zpa.NewClient() if err != nil { log.Fatal("failed to initialize ZPA client:", err) } - api.ZPA = zpaClient + api.ZPAService = zpaCli.Service } - if strings.HasPrefix(resourceType_, "zia_") || strings.Contains(resources, "zia_") || resources == "*" || resources == "zia" { - ziaClient, err := zia.NewClient() + if wantsZIA(resourceType_, resources) { + ziaCli, err := zia.NewClient() if err != nil { log.Fatal("failed to initialize ZIA client:", err) } - api.ZIA = ziaClient + api.ZIAService = ziaCli.Service } } } +func wantsZPA(rt, rs string) bool { + return strings.HasPrefix(rt, "zpa_") || + strings.Contains(rs, "zpa_") || + rs == "*" || rs == "zpa" +} + +func wantsZIA(rt, rs string) bool { + return strings.HasPrefix(rt, "zia_") || + strings.Contains(rs, "zia_") || + rs == "*" || rs == "zia" +} + func listSupportedResources(prefix string) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug) - // Define headers with centering header1 := "Resource" header2 := "Generate Supported" header3 := "Import Supported" @@ -280,19 +497,23 @@ func listSupportedResources(prefix string) { width2 := 18 width3 := 18 - // Print table with double lined format and close the entire table fmt.Fprintf(w, "╔%s╗\n", strings.Repeat("═", width1+width2+width3+10)) - fmt.Fprintf(w, "║ %-*s │ %-*s │ %-*s ║\n", width1, centerText(header1, width1), width2, centerText(header2, width2), width3, centerText(header3, width3)) + fmt.Fprintf(w, "║ %-*s │ %-*s │ %-*s ║\n", + width1, centerText(header1, width1), + width2, centerText(header2, width2), + width3, centerText(header3, width3)) fmt.Fprintf(w, "╠%s╣\n", strings.Repeat("═", width1+width2+width3+10)) for _, resource := range allSupportedResources { if strings.HasPrefix(resource, prefix) { - fmt.Fprintf(w, "║ %-*s │ %-*s │ %-*s ║\n", width1, resource, width2, centerText("✅", width2), width3, centerText("✅", width3)) + fmt.Fprintf(w, "║ %-*s │ %-*s │ %-*s ║\n", + width1, resource, + width2, centerText("✅", width2), + width3, centerText("✅", width3)) } } fmt.Fprintf(w, "╚%s╝\n", strings.Repeat("═", width1+width2+width3+10)) - // Check for errors when flushing data to output if err := w.Flush(); err != nil { log.Fatalf("Error flushing output: %v", err) } diff --git a/docs/guides/release-notes.md b/docs/guides/release-notes.md index f33ce23..c41507d 100644 --- a/docs/guides/release-notes.md +++ b/docs/guides/release-notes.md @@ -12,10 +12,46 @@ Track all Zscaler Terraformer Tool releases. New resources, features, and bug fi --- -``Last updated: v1.3.6`` +``Last updated: v2.0.0`` --- + +## 2.0.0 (January, 29 2025) - BREAKING CHANGES + +### Notes + +- Release date: **(January, 29 2025)** +- Supported Terraform version: **v1.x** + +#### Enhancements - Zscaler OneAPI Support + +[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252): The ZIA Terraform Provider now offers support for [OneAPI](https://help.zscaler.com/oneapi/understanding-oneapi) Oauth2 authentication through [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** As of version v2.0.0, Zscaler-Terraformer offers backwards compatibility to the Zscaler legacy API framework. This is the recommended authentication method for organizations whose tenants are still not migrated to [Zidentity](https://help.zscaler.com/zidentity/what-zidentity). + +**NOTE** Notice that OneAPI and Zidentity is NOT currently supported for the following ZIA and ZPA clouds respectively: `zscalergov` and `zscalerten` or `GOV` and `GOVUS`. Refer to the [Legacy API Framework](#legacy-api-framework) for more information on how authenticate to these environments + +### NEW - RESOURCES, DATA SOURCES + +[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252): The following new resources and data sources have been introduced: + +- Added resource ``zia_sandbox_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Sandbox Rules +- Added resource ``zia_firewall_dns_rule``[PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Cloud Firewall DNS Rules +- Added resource ``zia_firewall_ips_rule`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage Cloud Firewall IPS Rules +- Added resource ``zia_file_type_control_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manage File Type Control Rules +- Added resource ``zia_advanced_threat_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages advanced threat configuration settings +- Added resource ``zia_atp_malicious_urls`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages malicious URLs added to the denylist in ATP policy +- Added resource ``zia_atp_security_exceptions`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Security Exceptions (URL Bypass List) for the ATP policy +- Added resource ``zia_advanced_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Settings configuration. [Configuring Advanced Settings](https://help.zscaler.com/zia/configuring-advanced-settings) +- Added resource ``zia_atp_malware_inspection`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Inspection configuration. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added resource ``zia_atp_malware_protocols`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Protocols configuration. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added and resource ``zia_atp_malware_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Settings. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added and resource ``zia_atp_malware_policy`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages Advanced Threat Protection Malware Policy. [Malware Protection](https://help.zscaler.com/zia/policies/malware-protection) +- Added resource ``zia_end_user_notification`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Retrieves information of browser-based end user notification (EUN) configuration details.[Understanding Browser-Based End User Notifications](https://help.zscaler.com/zia/understanding-browser-based-end-user-notifications) +- Added resource ``zia_url_filtering_and_cloud_app_settings`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages the URL and Cloud App Control advanced policy settings.[Configuring Advanced Policy Settings](https://help.zscaler.com/zia/configuring-advanced-policy-settings) +- Added resource ``zia_ssl_inspection_rules`` [PR #252](https://github.com/zscaler/zscaler-terraformer/pull/252) :rocket: - Manages SSL Inspection Rules. + ## 1.3.6 (January, 5 2025) ### Notes diff --git a/go.mod b/go.mod index 7a02c91..6f3ba7d 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ toolchain go1.23.1 require ( github.com/google/uuid v1.6.0 - github.com/hashicorp/hc-install v0.9.1 github.com/hashicorp/terraform-exec v0.22.0 github.com/hashicorp/terraform-json v0.24.0 github.com/iancoleman/strcase v0.3.0 @@ -14,16 +13,16 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/zclconf/go-cty v1.16.2 - github.com/zscaler/zscaler-sdk-go/v2 v2.74.2 + github.com/zscaler/zscaler-sdk-go/v3 v3.1.2 ) require ( - github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/allegro/bigcache/v3 v3.1.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect - github.com/cloudflare/circl v1.3.7 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect @@ -35,6 +34,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -49,10 +49,9 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 45511c8..158af45 100644 --- a/go.sum +++ b/go.sum @@ -34,9 +34,14 @@ github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cj github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -75,6 +80,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -103,8 +110,8 @@ github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFz github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -144,36 +151,74 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70= github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zscaler/zscaler-sdk-go/v2 v2.74.2 h1:akUaVrnqLNWFBx5GK++PirbPJTdm/6jiAh4qm5fdTqI= -github.com/zscaler/zscaler-sdk-go/v2 v2.74.2/go.mod h1:4ZS4vlk8HKciHYJ6n1vnN2xXus5ir388p7K3VJSRkL8= +github.com/zscaler/zscaler-sdk-go/v3 v3.1.2 h1:Bq6yEZqhoQGp2a73uqnwfKZJL99oKPFMABBL/h5ImLE= +github.com/zscaler/zscaler-sdk-go/v3 v3.1.2/go.mod h1:GB5/fOUQRF6AMBfOJR6Vsf0DA5FCVqP5fhZEPOmpoFg= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= diff --git a/providers/zia/client.go b/providers/zia/client.go index 0fd3d61..86036a1 100644 --- a/providers/zia/client.go +++ b/providers/zia/client.go @@ -1,98 +1,348 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package zia import ( + "fmt" + "log" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + "github.com/sirupsen/logrus" "github.com/spf13/viper" - "github.com/zscaler/zscaler-sdk-go/v2/zia" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services" - "github.com/zscaler/zscaler-sdk-go/v2/zia/services/forwarding_control_policy/zpa_gateways" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zia" ) +// Client is the high-level client returned by NewClient(). +// We only keep one Service pointer, and at runtime. +// it will be backed by either the legacy (V2) client or the new (V3) client. type Client struct { - Admins *services.Service - FilteringRules *services.Service - IPDestinationGroups *services.Service - IPSourceGroups *services.Service - NetworkApplicationGroups *services.Service - NetworkServiceGroups *services.Service - NetworkServices *services.Service - URLCategories *services.Service - URLFilteringPolicies *services.Service - GRETunnels *services.Service - StaticIPs *services.Service - VPNCredentials *services.Service - LocationManagement *services.Service - DLPDictionaries *services.Service - DLPEngines *services.Service - DLPNotificationTemplates *services.Service - DLPWebRules *services.Service - RuleLabels *services.Service - SecurityPolicySettings *services.Service - SandboxSettings *services.Service - UserAuthenticationSettings *services.Service - ForwardingRules *services.Service - ZpaGateways *zpa_gateways.Service + Service *zscaler.Service +} + +// Config holds all the configuration settings used to initialize the SDK clients. +// Adapt as needed, mirroring what you have in config.go of the Terraform provider. +type Config struct { + useLegacyClient bool + + // V3 fields (OneAPI). + clientID string + clientSecret string + privateKey string + vanityDomain string + cloud string + + // V2 fields (Legacy). + Username string + Password string + APIKey string + ZIABaseURL string + + httpProxy string + requestTimeout int + retryCount int } +// NewClient is the main entry point: it reads config (from viper/env). +// and initializes the appropriate client (V2 or V3). func NewClient() (*Client, error) { - ziaCloud := viper.GetString("zia_cloud") - ziaUsername := viper.GetString("zia_username") - ziaPassword := viper.GetString("zia_password") - ziaAPIKey := viper.GetString("zia_api_key") + // Build up our internal config object from environment variables, viper, etc. + cfg := newConfigFromEnv() // No error returned now. - if ziaUsername == "" || ziaPassword == "" || ziaAPIKey == "" { - logrus.Fatal("ZIA credentials are not set") - } + var svc *zscaler.Service - client, err := zia.NewClient(ziaUsername, ziaPassword, ziaAPIKey, ziaCloud, "zscaler-terraformer") - if err != nil { - return nil, err + if cfg.useLegacyClient { + logrus.Infof("[INFO] Initializing legacy V2 client...") + legacySvc, err := zscalerSDKV2Client(cfg) + if err != nil { + return nil, fmt.Errorf("failed to initialize V2 client: %w", err) + } + // Wrap the underlying client in zscaler.Service so usage is consistent. + svc = zscaler.NewService(legacySvc.Client, nil) + } else { + logrus.Infof("[INFO] Initializing V3 client...") + v3Client, err := zscalerSDKV3Client(cfg) + if err != nil { + return nil, fmt.Errorf("failed to initialize V3 client: %w", err) + } + // Wrap the underlying client in zscaler.Service so usage is consistent. + svc = zscaler.NewService(v3Client, nil) } return &Client{ - Admins: services.New(client), - FilteringRules: services.New(client), - IPDestinationGroups: services.New(client), - IPSourceGroups: services.New(client), - NetworkApplicationGroups: services.New(client), - NetworkServiceGroups: services.New(client), - NetworkServices: services.New(client), - URLCategories: services.New(client), - URLFilteringPolicies: services.New(client), - GRETunnels: services.New(client), - StaticIPs: services.New(client), - VPNCredentials: services.New(client), - LocationManagement: services.New(client), - DLPDictionaries: services.New(client), - DLPEngines: services.New(client), - DLPNotificationTemplates: services.New(client), - DLPWebRules: services.New(client), - RuleLabels: services.New(client), - SecurityPolicySettings: services.New(client), - SandboxSettings: services.New(client), - UserAuthenticationSettings: services.New(client), - ForwardingRules: services.New(client), - ZpaGateways: zpa_gateways.New(client), + Service: svc, }, nil } + +// newConfigFromEnv populates the Config struct by pulling values from viper or environment variables. +// Feel free to adapt for your own convention or for direct environment usage. +func newConfigFromEnv() *Config { + // Optionally ensure viper is reading environment variables. + viper.AutomaticEnv() + + // The parameter or env var controlling legacy usage. + useLegacyClient := viper.GetBool("use_legacy_client") + // Also check ZSCALER_USE_LEGACY_CLIENT + if os.Getenv("ZSCALER_USE_LEGACY_CLIENT") != "" { + useLegacyClient = strings.EqualFold(os.Getenv("ZSCALER_USE_LEGACY_CLIENT"), "true") + } + + // For the new OneAPI. + clientID := viper.GetString("client_id") + if clientID == "" && os.Getenv("ZSCALER_CLIENT_ID") != "" { + clientID = os.Getenv("ZSCALER_CLIENT_ID") + } + + clientSecret := viper.GetString("client_secret") + if clientSecret == "" && os.Getenv("ZSCALER_CLIENT_SECRET") != "" { + clientSecret = os.Getenv("ZSCALER_CLIENT_SECRET") + } + + privateKey := viper.GetString("private_key") + if privateKey == "" && os.Getenv("ZSCALER_PRIVATE_KEY") != "" { + privateKey = os.Getenv("ZSCALER_PRIVATE_KEY") + } + + vanityDomain := viper.GetString("vanity_domain") + if vanityDomain == "" && os.Getenv("ZSCALER_VANITY_DOMAIN") != "" { + vanityDomain = os.Getenv("ZSCALER_VANITY_DOMAIN") + } + + cloud := viper.GetString("zscaler_cloud") + if cloud == "" && os.Getenv("ZSCALER_CLOUD") != "" { + cloud = os.Getenv("ZSCALER_CLOUD") + } + + // For the legacy V2 approach. + Username := viper.GetString("username") + if Username == "" && os.Getenv("ZIA_USERNAME") != "" { + Username = os.Getenv("ZIA_USERNAME") + } + + Password := viper.GetString("password") + if Password == "" && os.Getenv("ZIA_PASSWORD") != "" { + Password = os.Getenv("ZIA_PASSWORD") + } + + APIKey := viper.GetString("api_key") + if APIKey == "" && os.Getenv("ZIA_API_KEY") != "" { + APIKey = os.Getenv("ZIA_API_KEY") + } + + ZIABaseURL := viper.GetString("zia_cloud") + if ZIABaseURL == "" && os.Getenv("ZIA_CLOUD") != "" { + ZIABaseURL = os.Getenv("ZIA_CLOUD") + } + + httpProxy := viper.GetString("zscaler_http_proxy") + if httpProxy == "" && os.Getenv("ZSCALER_HTTP_PROXY") != "" { + httpProxy = os.Getenv("ZSCALER_HTTP_PROXY") + } + + retryCount := viper.GetInt("zscaler_retry_count") + if retryCount == 0 { + if val := os.Getenv("ZSCALER_RETRY_COUNT"); val != "" { + if converted, err := strconv.Atoi(val); err == nil { + retryCount = converted + } + } + if retryCount == 0 { + retryCount = 5 + } + } + + requestTimeout := viper.GetInt("zscaler_request_timeout") + if requestTimeout == 0 { + // fallback or set default. + if val := os.Getenv("ZSCALER_REQUEST_TIMEOUT"); val != "" { + if converted, err := strconv.Atoi(val); err == nil { + requestTimeout = converted + } + } + } + + // Build the config struct. + config := &Config{ + useLegacyClient: useLegacyClient, + + // V3 fields. + clientID: clientID, + clientSecret: clientSecret, + privateKey: privateKey, + vanityDomain: vanityDomain, + cloud: cloud, + + // V2 fields. + Username: Username, + Password: Password, + APIKey: APIKey, + ZIABaseURL: ZIABaseURL, + + httpProxy: httpProxy, + retryCount: retryCount, + requestTimeout: requestTimeout, + } + + return config +} + +// zscalerSDKV2Client initializes the legacy ZIA client (V2). +func zscalerSDKV2Client(c *Config) (*zscaler.Service, error) { + // You can set a custom user agent if desired. + customUserAgent := "(Terraformer Legacy) ZIA" + + // Start building config setters for the V2 zia library. + setters := []zia.ConfigSetter{ + zia.WithCache(false), + zia.WithHttpClientPtr(http.DefaultClient), + zia.WithRateLimitMaxRetries(int32(c.retryCount)), + zia.WithRequestTimeout(time.Duration(c.requestTimeout) * time.Second), + zia.WithZiaUsername(c.Username), + zia.WithZiaPassword(c.Password), + zia.WithZiaAPIKey(c.APIKey), + zia.WithZiaCloud(c.ZIABaseURL), + } + + // Proxy. + if c.httpProxy != "" { + parsedURL, err := url.Parse(c.httpProxy) + if err != nil { + return nil, fmt.Errorf("invalid proxy URL: %w", err) + } + setters = append(setters, zia.WithProxyHost(parsedURL.Hostname())) + + sPort := parsedURL.Port() + if sPort == "" { + sPort = "80" + } + port64, err := strconv.ParseInt(sPort, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid proxy port: %w", err) + } + if port64 < 1 || port64 > 65535 { + return nil, fmt.Errorf("invalid port number: must be between 1 and 65535, got: %d", port64) + } + setters = append(setters, zia.WithProxyPort(int32(port64))) + } + + ziaCfg, err := zia.NewConfiguration(setters...) + if err != nil { + return nil, fmt.Errorf("failed to create ZIA V2 configuration: %w", err) + } + ziaCfg.UserAgent = customUserAgent + + // Now wrap it in a zscaler.Service so usage is uniform. + wrappedV2Client, err := zscaler.NewLegacyZiaClient(ziaCfg) + if err != nil { + return nil, fmt.Errorf("failed to create legacy ZIA client: %w", err) + } + + log.Println("[INFO] Successfully initialized ZIA V2 client") + return wrappedV2Client, nil +} + +// zscalerSDKV3Client initializes the new OneAPI-based Zscaler client. +func zscalerSDKV3Client(c *Config) (*zscaler.Client, error) { + customUserAgent := "(Terraformer V3) ZIA" + + setters := []zscaler.ConfigSetter{ + zscaler.WithCache(false), + zscaler.WithHttpClientPtr(http.DefaultClient), + zscaler.WithRateLimitMaxRetries(int32(c.retryCount)), + zscaler.WithRequestTimeout(time.Duration(c.requestTimeout) * time.Second), + // we’ll override user agent later. + } + + // Proxy. + if c.httpProxy != "" { + parsedURL, err := url.Parse(c.httpProxy) + if err != nil { + return nil, fmt.Errorf("invalid proxy URL: %w", err) + } + setters = append(setters, zscaler.WithProxyHost(parsedURL.Hostname())) + + sPort := parsedURL.Port() + if sPort == "" { + sPort = "80" + } + port64, err := strconv.ParseInt(sPort, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid proxy port: %w", err) + } + if port64 < 1 || port64 > 65535 { + return nil, fmt.Errorf("invalid port number: %d", port64) + } + setters = append(setters, zscaler.WithProxyPort(int32(port64))) + } + + // Check which auth method we have. + // 1) clientID + clientSecret + vanityDomain + customerID. + // 2) clientID + privateKey + vanityDomain + customerID. + switch { + case c.clientID != "" && c.clientSecret != "" && c.vanityDomain != "": + setters = append(setters, + zscaler.WithClientID(c.clientID), + zscaler.WithClientSecret(c.clientSecret), + zscaler.WithVanityDomain(c.vanityDomain), + ) + + if c.cloud != "" { + setters = append(setters, zscaler.WithZscalerCloud(c.cloud)) + } + + case c.clientID != "" && c.privateKey != "" && c.vanityDomain != "": + setters = append(setters, + zscaler.WithClientID(c.clientID), + zscaler.WithPrivateKey(c.privateKey), + zscaler.WithVanityDomain(c.vanityDomain), + ) + + if c.cloud != "" { + setters = append(setters, zscaler.WithZscalerCloud(c.cloud)) + } + + default: + return nil, fmt.Errorf("invalid authentication configuration: missing required parameters") + } + + conf, err := zscaler.NewConfiguration(setters...) + if err != nil { + return nil, fmt.Errorf("failed to create V3 configuration: %w", err) + } + conf.UserAgent = customUserAgent + + // Build the client. + v3Client, err := zscaler.NewOneAPIClient(conf) + if err != nil { + return nil, fmt.Errorf("failed to create Zscaler OneAPI client: %w", err) + } + + log.Println("[INFO] Successfully initialized ZIA V3 client") + return v3Client.Client, nil +} diff --git a/providers/zpa/client.go b/providers/zpa/client.go index 6bdfb1a..4dac67b 100644 --- a/providers/zpa/client.go +++ b/providers/zpa/client.go @@ -1,100 +1,366 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package zpa import ( + "fmt" + "log" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + "github.com/sirupsen/logrus" "github.com/spf13/viper" - "github.com/zscaler/zscaler-sdk-go/v2/zpa" - "github.com/zscaler/zscaler-sdk-go/v2/zpa/services" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler" + "github.com/zscaler/zscaler-sdk-go/v3/zscaler/zpa" ) +// Client is the high-level client returned by NewClient(). +// We only keep one Service pointer, and at runtime +// it will be backed by either the legacy (V2) client or the new (V3) client. type Client struct { - AppConnectorGroup *services.Service - AppConnectorController *services.Service - ApplicationSegment *services.Service - ApplicationSegmentInspection *services.Service - ApplicationSegmentPRA *services.Service - AppServerController *services.Service - BrowserAccess *services.Service - BACertificate *services.Service - CbiBanner *services.Service - CbiCertificate *services.Service - CbiExternalProfile *services.Service - LSSConfigController *services.Service - PolicySetController *services.Service - PolicySetControllerV2 *services.Service - PRAApproval *services.Service - PRACredential *services.Service - PRAConsole *services.Service - PRAPortal *services.Service - ProvisioningKey *services.Service - SegmentGroup *services.Service - ServerGroup *services.Service - ServiceEdgeGroup *services.Service - InspectionCustomControls *services.Service - MicroTenants *services.Service + Service *zscaler.Service } +// Config holds all the configuration settings used to initialize the SDK clients. +// Adapt as needed, mirroring what you have in config.go of the Terraform provider. +type Config struct { + useLegacyClient bool + + // V3 fields (OneAPI) + clientID string + clientSecret string + privateKey string + vanityDomain string + customerID string + microtenantID string + cloud string + + // V2 fields (Legacy) + zpaClientID string + zpaClientSecret string + zpaCustomerID string + BaseURL string + + httpProxy string + requestTimeout int + retryCount int +} + +// NewClient is the main entry point: it reads config (from viper/env). +// and initializes the appropriate client (V2 or V3). func NewClient() (*Client, error) { - zpaCloud := viper.GetString("zpa_cloud") + // Build up our internal config object from environment variables, viper, etc. + cfg := newConfigFromEnv() // No error returned now. + + var svc *zscaler.Service + + if cfg.useLegacyClient { + logrus.Infof("[INFO] Initializing legacy V2 client...") + legacySvc, err := zscalerSDKV2Client(cfg) + if err != nil { + return nil, fmt.Errorf("failed to initialize V2 client: %w", err) + } + // Wrap the underlying client in zscaler.Service so usage is consistent. + svc = zscaler.NewService(legacySvc.Client, nil) + } else { + logrus.Infof("[INFO] Initializing V3 client...") + v3Client, err := zscalerSDKV3Client(cfg) + if err != nil { + return nil, fmt.Errorf("failed to initialize V3 client: %w", err) + } + // Wrap the underlying client in zscaler.Service so usage is consistent. + svc = zscaler.NewService(v3Client, nil) + } + + return &Client{ + Service: svc, + }, nil +} + +func newConfigFromEnv() *Config { + viper.AutomaticEnv() + + // The parameter or env var controlling legacy usage. + useLegacyClient := viper.GetBool("use_legacy_client") + if os.Getenv("ZSCALER_USE_LEGACY_CLIENT") != "" { + useLegacyClient = strings.EqualFold(os.Getenv("ZSCALER_USE_LEGACY_CLIENT"), "true") + } + + // For the new OneAPI + clientID := viper.GetString("client_id") + if clientID == "" && os.Getenv("ZSCALER_CLIENT_ID") != "" { + clientID = os.Getenv("ZSCALER_CLIENT_ID") + } + + clientSecret := viper.GetString("client_secret") + if clientSecret == "" && os.Getenv("ZSCALER_CLIENT_SECRET") != "" { + clientSecret = os.Getenv("ZSCALER_CLIENT_SECRET") + } + + privateKey := viper.GetString("private_key") + if privateKey == "" && os.Getenv("ZSCALER_PRIVATE_KEY") != "" { + privateKey = os.Getenv("ZSCALER_PRIVATE_KEY") + } + + vanityDomain := viper.GetString("vanity_domain") + if vanityDomain == "" && os.Getenv("ZSCALER_VANITY_DOMAIN") != "" { + vanityDomain = os.Getenv("ZSCALER_VANITY_DOMAIN") + } + + customerID := viper.GetString("customer_id") + if customerID == "" && os.Getenv("ZPA_CUSTOMER_ID") != "" { + customerID = os.Getenv("ZPA_CUSTOMER_ID") + } + + microtenantID := viper.GetString("microtenant_id") + if microtenantID == "" && os.Getenv("ZSCALER_MICROTENANT_ID") != "" { + microtenantID = os.Getenv("ZSCALER_MICROTENANT_ID") + } + + cloud := viper.GetString("zscaler_cloud") + if cloud == "" && os.Getenv("ZSCALER_CLOUD") != "" { + cloud = os.Getenv("ZSCALER_CLOUD") + } + + // For the legacy V2 approach. zpaClientID := viper.GetString("zpa_client_id") + if zpaClientID == "" && os.Getenv("ZPA_CLIENT_ID") != "" { + zpaClientID = os.Getenv("ZPA_CLIENT_ID") + } + zpaClientSecret := viper.GetString("zpa_client_secret") + if zpaClientSecret == "" && os.Getenv("ZPA_CLIENT_SECRET") != "" { + zpaClientSecret = os.Getenv("ZPA_CLIENT_SECRET") + } + zpaCustomerID := viper.GetString("zpa_customer_id") + if zpaCustomerID == "" && os.Getenv("ZPA_CUSTOMER_ID") != "" { + zpaCustomerID = os.Getenv("ZPA_CUSTOMER_ID") + } + + baseURL := viper.GetString("zpa_cloud") + if baseURL == "" && os.Getenv("ZPA_CLOUD") != "" { + baseURL = os.Getenv("ZPA_CLOUD") + } - if zpaClientID == "" || zpaClientSecret == "" || zpaCustomerID == "" { - logrus.Fatal("ZPA credentials are not set") + httpProxy := viper.GetString("zscaler_http_proxy") + if httpProxy == "" && os.Getenv("ZSCALER_HTTP_PROXY") != "" { + httpProxy = os.Getenv("ZSCALER_HTTP_PROXY") } - config, err := zpa.NewConfig(zpaClientID, zpaClientSecret, zpaCustomerID, zpaCloud, "zscaler-terraformer") + retryCount := viper.GetInt("zscaler_retry_count") + if retryCount == 0 { + if val := os.Getenv("ZSCALER_RETRY_COUNT"); val != "" { + if converted, err := strconv.Atoi(val); err == nil { + retryCount = converted + } + } + if retryCount == 0 { + retryCount = 5 + } + } + + requestTimeout := viper.GetInt("zscaler_request_timeout") + if requestTimeout == 0 { + if val := os.Getenv("ZSCALER_REQUEST_TIMEOUT"); val != "" { + if converted, err := strconv.Atoi(val); err == nil { + requestTimeout = converted + } + } + } + + // Build the config struct. + config := &Config{ + useLegacyClient: useLegacyClient, + + // V3 fields. + clientID: clientID, + clientSecret: clientSecret, + privateKey: privateKey, + vanityDomain: vanityDomain, + customerID: customerID, + microtenantID: microtenantID, + cloud: cloud, + + // V2 fields. + zpaClientID: zpaClientID, + zpaClientSecret: zpaClientSecret, + zpaCustomerID: zpaCustomerID, + BaseURL: baseURL, + + httpProxy: httpProxy, + retryCount: retryCount, + requestTimeout: requestTimeout, + } + + return config +} + +// zscalerSDKV2Client initializes the legacy ZPA client (V2). +func zscalerSDKV2Client(c *Config) (*zscaler.Service, error) { + // You can set a custom user agent if desired. + customUserAgent := "(Terraformer Legacy) ZPA" + + // Start building config setters for the V2 zpa library. + setters := []zpa.ConfigSetter{ + zpa.WithCache(false), + zpa.WithHttpClientPtr(http.DefaultClient), + zpa.WithRateLimitMaxRetries(int32(c.retryCount)), + zpa.WithRequestTimeout(time.Duration(c.requestTimeout) * time.Second), + zpa.WithZPAClientID(c.zpaClientID), + zpa.WithZPAClientSecret(c.zpaClientSecret), + zpa.WithZPACustomerID(c.zpaCustomerID), + zpa.WithZPACloud(c.BaseURL), + } + + // Handle microtenant if present. + if c.microtenantID != "" { + setters = append(setters, zpa.WithZPAMicrotenantID(c.microtenantID)) + } + + // Proxy. + if c.httpProxy != "" { + parsedURL, err := url.Parse(c.httpProxy) + if err != nil { + return nil, fmt.Errorf("invalid proxy URL: %w", err) + } + setters = append(setters, zpa.WithProxyHost(parsedURL.Hostname())) + + sPort := parsedURL.Port() + if sPort == "" { + sPort = "80" + } + port64, err := strconv.ParseInt(sPort, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid proxy port: %w", err) + } + if port64 < 1 || port64 > 65535 { + return nil, fmt.Errorf("invalid port number: must be between 1 and 65535, got: %d", port64) + } + setters = append(setters, zpa.WithProxyPort(int32(port64))) + } + + zpaCfg, err := zpa.NewConfiguration(setters...) + if err != nil { + return nil, fmt.Errorf("failed to create ZPA V2 configuration: %w", err) + } + zpaCfg.UserAgent = customUserAgent + + // Now wrap it in a zscaler.Service so usage is uniform. + wrappedV2Client, err := zscaler.NewLegacyZpaClient(zpaCfg) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create legacy ZPA client: %w", err) } - client := zpa.NewClient(config) + log.Println("[INFO] Successfully initialized ZPA V2 client") + return wrappedV2Client, nil +} - return &Client{ - AppConnectorGroup: services.New(client), - AppConnectorController: services.New(client), - ApplicationSegment: services.New(client), - ApplicationSegmentInspection: services.New(client), - ApplicationSegmentPRA: services.New(client), - AppServerController: services.New(client), - BrowserAccess: services.New(client), - CbiBanner: services.New(client), - CbiCertificate: services.New(client), - CbiExternalProfile: services.New(client), - LSSConfigController: services.New(client), - PolicySetController: services.New(client), - PolicySetControllerV2: services.New(client), - PRAApproval: services.New(client), - PRACredential: services.New(client), - PRAConsole: services.New(client), - PRAPortal: services.New(client), - ProvisioningKey: services.New(client), - SegmentGroup: services.New(client), - ServerGroup: services.New(client), - ServiceEdgeGroup: services.New(client), - InspectionCustomControls: services.New(client), - MicroTenants: services.New(client), - }, nil +// zscalerSDKV3Client initializes the new OneAPI-based Zscaler client. +func zscalerSDKV3Client(c *Config) (*zscaler.Client, error) { + customUserAgent := "(Terraformer V3) ZPA" + + setters := []zscaler.ConfigSetter{ + zscaler.WithCache(false), + zscaler.WithHttpClientPtr(http.DefaultClient), + zscaler.WithRateLimitMaxRetries(int32(c.retryCount)), + zscaler.WithRequestTimeout(time.Duration(c.requestTimeout) * time.Second), + } + + // Proxy. + if c.httpProxy != "" { + parsedURL, err := url.Parse(c.httpProxy) + if err != nil { + return nil, fmt.Errorf("invalid proxy URL: %w", err) + } + setters = append(setters, zscaler.WithProxyHost(parsedURL.Hostname())) + + sPort := parsedURL.Port() + if sPort == "" { + sPort = "80" + } + port64, err := strconv.ParseInt(sPort, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid proxy port: %w", err) + } + if port64 < 1 || port64 > 65535 { + return nil, fmt.Errorf("invalid port number: %d", port64) + } + setters = append(setters, zscaler.WithProxyPort(int32(port64))) + } + + // Check which auth method we have. + // 1) clientID + clientSecret + vanityDomain + customerID. + // 2) clientID + privateKey + vanityDomain + customerID. + switch { + case c.clientID != "" && c.clientSecret != "" && c.vanityDomain != "" && c.customerID != "": + setters = append(setters, + zscaler.WithClientID(c.clientID), + zscaler.WithClientSecret(c.clientSecret), + zscaler.WithVanityDomain(c.vanityDomain), + zscaler.WithZPACustomerID(c.customerID), + ) + if c.microtenantID != "" { + setters = append(setters, zscaler.WithZPAMicrotenantID(c.microtenantID)) + } + if c.cloud != "" { + setters = append(setters, zscaler.WithZscalerCloud(c.cloud)) + } + + case c.clientID != "" && c.privateKey != "" && c.vanityDomain != "" && c.customerID != "": + setters = append(setters, + zscaler.WithClientID(c.clientID), + zscaler.WithPrivateKey(c.privateKey), + zscaler.WithVanityDomain(c.vanityDomain), + zscaler.WithZPACustomerID(c.customerID), + ) + if c.microtenantID != "" { + setters = append(setters, zscaler.WithZPAMicrotenantID(c.microtenantID)) + } + if c.cloud != "" { + setters = append(setters, zscaler.WithZscalerCloud(c.cloud)) + } + default: + return nil, fmt.Errorf("invalid authentication configuration: missing required parameters") + } + + conf, err := zscaler.NewConfiguration(setters...) + if err != nil { + return nil, fmt.Errorf("failed to create V3 configuration: %w", err) + } + conf.UserAgent = customUserAgent + + // Build the client. + v3Client, err := zscaler.NewOneAPIClient(conf) + if err != nil { + return nil, fmt.Errorf("failed to create Zscaler OneAPI client: %w", err) + } + + log.Println("[INFO] Successfully initialized ZPA V3 client") + return v3Client.Client, nil } diff --git a/teraformutils/conversion/conversion.go b/teraformutils/conversion/conversion.go deleted file mode 100644 index ee0ad69..0000000 --- a/teraformutils/conversion/conversion.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package conversion - -import ( - "time" -) - -func EpochToRFC1123(epoch int64) string { - t := time.Unix(epoch, 0).UTC() - return t.Format(time.RFC1123) -} - -/* -// Converts an epoch time (in seconds, represented as a string) to a human-readable format with the specified timezone. -func EpochToRFC1123WithTimezone(epochStr string, timezone string) (string, error) { - epoch, err := strconv.ParseInt(epochStr, 10, 64) - if err != nil { - return "", fmt.Errorf("failed to parse epoch time: %s", err) - } - - loc, err := time.LoadLocation(timezone) - if err != nil { - return "", fmt.Errorf("failed to load location: %s", err) - } - - t := time.Unix(epoch, 0).In(loc) - return t.Format(time.RFC1123), nil -} -*/ diff --git a/terraformutils/conversion/conversion.go b/terraformutils/conversion/conversion.go new file mode 100644 index 0000000..f4f1e0c --- /dev/null +++ b/terraformutils/conversion/conversion.go @@ -0,0 +1,51 @@ +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package conversion + +import ( + "time" +) + +func EpochToRFC1123(epoch int64) string { + t := time.Unix(epoch, 0).UTC() + return t.Format(time.RFC1123) +} + +/* +// Converts an epoch time (in seconds, represented as a string) to a human-readable format with the specified timezone. +func EpochToRFC1123WithTimezone(epochStr string, timezone string) (string, error) { + epoch, err := strconv.ParseInt(epochStr, 10, 64) + if err != nil { + return "", fmt.Errorf("failed to parse epoch time: %s", err) + } + + loc, err := time.LoadLocation(timezone) + if err != nil { + return "", fmt.Errorf("failed to load location: %s", err) + } + + t := time.Unix(epoch, 0).In(loc) + return t.Format(time.RFC1123), nil +} +*/ diff --git a/teraformutils/helpers/helpers.go b/terraformutils/helpers/helpers.go similarity index 87% rename from teraformutils/helpers/helpers.go rename to terraformutils/helpers/helpers.go index 861150d..ba43fda 100644 --- a/teraformutils/helpers/helpers.go +++ b/terraformutils/helpers/helpers.go @@ -1,23 +1,25 @@ -// Copyright (c) 2023 Zscaler Inc, - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. +/* +Copyright (c) 2023 Zscaler Inc, + + MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ package helpers @@ -90,6 +92,16 @@ func GenerateOutputs(resourceType string, resourceID string, workingDir string) "zia_auth_settings_urls", "zia_sandbox_behavioral_analysis", "zia_security_settings", + "zia_advanced_settings", + "zia_atp_malicious_urls", + "zia_atp_security_exceptions", + "zia_advanced_threat_settings", + "zia_atp_malware_inspection", + "zia_atp_malware_protocols", + "zia_atp_malware_settings", + "zia_atp_malware_policy", + "zia_url_filtering_and_cloud_app_settings", + "zia_end_user_notification", } // Check if the resourceType is in the excluded list. diff --git a/teraformutils/nesting/nesting.go b/terraformutils/nesting/nesting.go similarity index 88% rename from teraformutils/nesting/nesting.go rename to terraformutils/nesting/nesting.go index 89b2204..b0dd0a2 100644 --- a/teraformutils/nesting/nesting.go +++ b/terraformutils/nesting/nesting.go @@ -32,12 +32,22 @@ import ( "github.com/iancoleman/strcase" "github.com/sirupsen/logrus" "github.com/zclconf/go-cty/cty" - "github.com/zscaler/zscaler-terraformer/teraformutils/conversion" - "github.com/zscaler/zscaler-terraformer/teraformutils/helpers" + "github.com/zscaler/zscaler-terraformer/terraformutils/conversion" + "github.com/zscaler/zscaler-terraformer/terraformutils/helpers" ) var log = logrus.New() +var noSkipIDBlocks = map[string]map[string]bool{ + "zia_firewall_dns_rule": { + "dns_gateway": true, + }, + "zia_ssl_inspection_rules": { + "ssl_interception_cert": true, + }, + // Add more resource->block combos here as needed +} + // nestBlocks takes a schema and generates all of the appropriate nesting of any. // top-level blocks as well as nested lists or sets. func NestBlocks(resourceType string, schemaBlock *tfjson.SchemaBlock, structData map[string]interface{}, parentID string, indexedNestedBlocks map[string][]string) string { @@ -94,7 +104,17 @@ func NestBlocks(resourceType string, schemaBlock *tfjson.SchemaBlock, structData continue } } - + if blockMap, ok := noSkipIDBlocks[resourceType]; ok { + if blockMap[block] { + // This block is one of the "edge cases" that need the ID. + blockData, ok := structData[MapTfFieldNameToAPI(resourceType, block)] + if ok { + output += writeBlockNoSkipID(resourceType, block, blockData, schemaBlock.NestedBlocks[block].Block) + } + // Skip the normal logic (don’t call WriteNestedBlock). + continue + } + } // special cases mapping. if resourceType == "zia_admin_users" && block == "admin_scope" { output += "admin_scope {\n" @@ -143,7 +163,7 @@ func NestBlocks(resourceType string, schemaBlock *tfjson.SchemaBlock, structData } output += "}\n" continue - } else if helpers.IsInList(resourceType, []string{"zia_firewall_filtering_network_service_groups", "zia_firewall_filtering_rule", "zia_url_filtering_rules", "zia_dlp_web_rules"}) && helpers.IsInList(block, []string{"departments", + } else if helpers.IsInList(resourceType, []string{"zia_firewall_filtering_network_service_groups", "zia_firewall_filtering_rule", "zia_url_filtering_rules", "zia_dlp_web_rules", "zia_ssl_inspection_rules", "zia_firewall_dns_rule", "zia_firewall_ips_rule", "zia_file_type_control_rules"}) && helpers.IsInList(block, []string{"departments", "groups", "locations", "dlp_engines", @@ -157,6 +177,7 @@ func NestBlocks(resourceType string, schemaBlock *tfjson.SchemaBlock, structData "override_users", "device_groups", "source_ip_groups", + "proxy_gateways", }) { output += helpers.ListIdsIntBlock(block, structData[MapTfFieldNameToAPI(resourceType, block)]) continue @@ -381,6 +402,7 @@ func WriteNestedBlock(resourceType string, attributes []string, schemaBlock *tfj // WriteAttrLine outputs a line of HCL configuration with a configurable depth. // for known types. func WriteAttrLine(key string, value interface{}, usedInBlock bool) string { + // General handling for attributes that are returned as nil. if value == nil { return "" @@ -536,17 +558,19 @@ func WriteAttrLine(key string, value interface{}, usedInBlock bool) string { return "" } -// Probably can be deprecated. Need to evaluate. func MapTfFieldNameToAPI(resourceType, fieldName string) string { - switch resourceType { - case "zia_admin_users": - switch fieldName { - case "username": - return "userName" - } + // Handle special cases for "TLS" + if fieldName == "min_client_tls_version" { + return "minClientTLSVersion" } - result := strcase.ToLowerCamel(fieldName) - return result + if fieldName == "min_server_tls_version" { + return "minServerTLSVersion" + } + if fieldName == "min_tls_version" { + return "minTLSVersion" + } + // Fallback + return strcase.ToLowerCamel(fieldName) } // Helper function to format a list of strings for HCL. @@ -557,3 +581,52 @@ func formatList(items []string) string { } return strings.Join(quotedItems, ", ") } + +func writeBlockNoSkipID(resourceType, blockName string, data interface{}, blockSchema *tfjson.SchemaBlock) string { + // If the API always returns a single map for this nested block, + // we cast to map[string]interface{} here. + mapData, ok := data.(map[string]interface{}) + if !ok || mapData == nil { + return "" + } + + // Gather the attributes from blockSchema (including "id"). + sortedAttrs := make([]string, 0, len(blockSchema.Attributes)) + for attr := range blockSchema.Attributes { + sortedAttrs = append(sortedAttrs, attr) + } + sort.Strings(sortedAttrs) + + nestedOutput := "" + for _, attrName := range sortedAttrs { + // We do NOT skip "id" or anything else here—no "if attrName == 'id' { continue }" + + // Convert the schema attribute name to snake_case for HCL + tfName := strcase.ToSnake(attrName) + + // Convert from Terraform field name -> actual key in map + // This is the same logic you had: + apiFieldName := MapTfFieldNameToAPI(resourceType, attrName) + + // Grab the value from the JSON data + value := mapData[apiFieldName] + if value == nil { + continue + } + + ty := blockSchema.Attributes[attrName].AttributeType + switch { + case ty.IsPrimitiveType(): + nestedOutput += WriteAttrLine(tfName, value, false) + case ty.IsListType(), ty.IsSetType(), ty.IsMapType(): + nestedOutput += WriteAttrLine(tfName, value, true) + } + } + + if nestedOutput == "" { + return "" + } + + // Return it in block form + return fmt.Sprintf("%s {\n%s}\n", blockName, nestedOutput) +}