In this article, I will show how you can import pre-existing ZPA cloud resources into Terraform so that you can take advanced and continue to develop the IaC in Terraform.
Existing ZPA resources can be imported and brought under Terraform management by using the Unofficial ZPA Terraform Provider, via the built-in Terraform command called
Important: It is important to emphasize that this Terraform provider is not in any way supported or maintained by Zscaler engineering, product or support teams; hence, it is provided “AS IS” as a community effort.
Terraform is able to import existing infrastructure. This allows you take resources you’ve created by some other means and bring it under Terraform management.
This is a great way to slowly transition your ZPA resources or any other supported cloud infrastructure resources to Terraform, or to be able to be confident that you can use Terraform in the future if it potentially doesn’t support every feature you need today.
Terraform is a relatively new technology and fully adopting it to manage an organization’s cloud resources may take some time and effort. The lack of human resources and the learning curve in using Terraform may cause teams to start using cloud infrastructure outside of the formal IaC process and go directly via their respective Web Consoles.
For that matter, any kind of IaC method such as (CloudFormation, Azure ARM templates, Pulumi, etc,) requires some training and real-time scenario handling experience. Things may get especially complicated when dealing with concepts like states and remote backends. In a worst case scenario, if you lose the
terraform.tfstate file you can use this import functionality to rebuild your configuration.
Getting the pre-existing cloud resources under the Terraform management is facilitated by Terraform import.
import is a Terraform CLI command which is used to read real-world infrastructure and update the state, so that future updates to the same set of infrastructure can be applied via IaC.
The import functionality helps update the state locally and it does not create the corresponding configuration automatically. However, the Terraform team is working hard to improve this function in upcoming releases.
Now that we understand of why we need to import cloud resources, let’s begin by importing a simple ZPA App Connector Group resource. To import a simple resource like this into Terraform, follow the below step-by-step guide or watch the video tutorial at the end of this article.
Assuming that the Terraform installation, ZPA provider binary are installed locally on your machine, and that the ZPA API credentials have already been done locally, we can begin by importing a simple resource - App Connector Group. For sake of this tutorial, I manually created the resource to be imported ahead of time.
The purpose of this step is to import this app connector group into our Terraform configuration. In your desired path, create
main.tf and configure the ZPA Provider parameters. The file should look like the one below:
terraform init to initialize the Terraform modules. Below is the output of a successful initialization.
As discussed earlier, the
terraform import command does not generate the configuration file by itself. Thus, you need to create the corresponding configuration for the App Connector Group manually. This can be an empty resource with no arguments, as the import command will provide all the necessary parameters for us at the end and we can simply add those into the empty resource block. Here is how to my main.tf file looks like with the empty resource block.
Think of it as if the ZPA cloud resource (App Connector Group) and its corresponding configuration were available in our files. All that is left to do is to map the two into our state file. We do that by running the import command as follows:
terraform import zpa_app_connector_group.example <ConnectorGroup ID>
As you may know, every single resource ID in ZPA is numeric, and that would be an extremely daunting task to identify all of that. For this reason and to make things easier, I added in the provider code, the ability to retrieve this information via either its numeric ID or via its name.
terraform import zpa_app_connector_group.example <ConnectorGroup Name>
A successfully output should look like this:
The above command essentially maps the zpa_app_connector_group.example configuration to the App Connector Group using the ID. By mapping, I mean that the state file now “knows” about the existence of the App Connector Group with the given ID. The state file also contains information about each attribute of this resource, as it has fetched the same using the import command.
Our project directory now also contains the terraform.tfstate file, which was generated after the import command was successfully executed. We now can run the command
terraform show and that will give us an output of the configuration in HCL format that we can use in our main.tf file. I can now simply copy this output and place into my main.tf file.
Note: There are few arguments which are not required in this configuration such as the id, city_country and version_profile_name. These information is determined by the upstream API and are not configurable; hence, we can remove it from the actual main.tf file. By doing that, our file .tf file will look like this:
At this point if we go ahead and run the command
terraform plan, Terraform will check the upstream resource in the ZPA cloud, to ensure there is no difference with the state file. Because there is no difference it will tell us that the upstream ZPA tenant configuration, matches the configuration in our state file.
Although this article provides a comprehensive overview of possible approaches towards importing ZPA resources into Terraform, there is always room for some particular and unusual scenarios. If you have any questions or would like to share your thoughts on common practices, feel free to leave your comments below.