Terraform Tutorial - Import
In this post, we'll import resources (IAM user, Access key, IAM Group, etc.)
Unfortunately, the current version of Terraform imports resources into their internal state only. It does not create a configuration file.
We need to add the initial configuration in our main.tf file:
resource "aws_iam_user" "existing_iam_user" { name = "test_user" } resource "aws_iam_access_key" "existing_iam_access_key" { user = aws_iam_user.existing_iam_user.name }
variables.tf:
variable "region" { default = "us-east-1" }
Before anything else, we need to do terraform init
to Initializing the backend and provider plugins.
$ terraform init Initializing the backend... Initializing provider plugins... - Reusing previous version of hashicorp/aws from the dependency lock file - Using previously-installed hashicorp/aws v3.42.0 Terraform has been successfully initialized ...
Now, import the resources using terraform import
command.
$ terraform import aws_iam_user.existing_iam_user test_user aws_iam_user.existing_iam_user: Importing from ID "test_user"... aws_iam_user.existing_iam_user: Import prepared! Prepared aws_iam_user for import aws_iam_user.existing_iam_user: Refreshing state... [id=test_user] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform. $ terraform import aws_iam_access_key.existing_iam_access_key AKIA...SJUP aws_iam_access_key.existing_iam_access_key: Importing from ID "AKIA...SJUP"... aws_iam_access_key.existing_iam_access_key: Import prepared! Prepared aws_iam_access_key for import aws_iam_access_key.existing_iam_access_key: Refreshing state... [id=AKIA...SJUP] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
Once the import is done, we may want to check the new state located in terraform.tfstate file:
{ "version": 4, "terraform_version": "0.15.3", "serial": 3, "lineage": "c6c904f7-3851-ee2e-4574-83f03e30c409", "outputs": {}, "resources": [ { "mode": "managed", "type": "aws_iam_access_key", "name": "existing_iam_access_key", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "schema_version": 0, "attributes": { "create_date": "2021-05-26T04:51:33Z", "encrypted_secret": null, "id": "AKIA...SJUP", "key_fingerprint": null, "pgp_key": null, "secret": null, "ses_smtp_password_v4": null, "status": "Active", "user": "test_user" }, "sensitive_attributes": [], "private": "ey...=", "dependencies": [ "aws_iam_user.existing_iam_user" ] } ] }, { "mode": "managed", "type": "aws_iam_user", "name": "existing_iam_user", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "schema_version": 0, "attributes": { "arn": "arn:aws:iam::047109936880:user/test_user", "force_destroy": null, "id": "test_user", "name": "test_user", "path": "/", "permissions_boundary": null, "tags": {}, "tags_all": {}, "unique_id": "AIDA...CJ26" }, "sensitive_attributes": [], "private": "eyJ...=" } ] } ] }
Starting from here, we can manually add some of the properties into our main.tf template file. Then, the new resource definition can be used to recreate it or to create new infrastructures.
In our case, because everything is up-to-date, the follwing runs won't make any changes to our infrastructure.
$ terraform plan aws_iam_user.existing_iam_user: Refreshing state... [id=test_user] aws_iam_access_key.existing_iam_access_key: Refreshing state... [id=AKIAQV57YS3YLEJYSJUP] No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take. $ terraform apply aws_iam_user.existing_iam_user: Refreshing state... [id=test_user] aws_iam_access_key.existing_iam_access_key: Refreshing state... [id=AKIAQV57YS3YLEJYSJUP] Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
To import a group, we need to modify the main.tf:
resource "aws_iam_user" "existing_iam_user" { name = "test_user" } resource "aws_iam_access_key" "existing_iam_access_key" { user = aws_iam_user.existing_iam_user.name } resource "aws_iam_group" "existing_iam_group" { name = "Testers" }
Import it:
$ terraform import aws_iam_group.existing_iam_group Testers aws_iam_group.existing_iam_group: Importing from ID "Testers"... aws_iam_group.existing_iam_group: Import prepared! Prepared aws_iam_group for import aws_iam_group.existing_iam_group: Refreshing state... [id=Testers] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
The test_user is a member of the Testers group. But import
does not supprt aws_iam_group_membership. So, we can just create it using terraform apply
:
resource "aws_iam_group_membership" "tf_aws_iam_group_membership" { name = "terraform-testing-group-membership" users = [ aws_iam_user.existing_iam_user.name ] group = aws_iam_group.existing_iam_group.name }
Refs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs
Refs:
Setup env variables:
export ZPA_CLIENT_ID = "xxxxxxxxxxxxxxxx" export ZPA_CLIENT_SECRET = "xxxxxxxxxxxxxxxx" export ZPA_CUSTOMER_ID = "xxxxxxxxxxxxxxxx"
We can get the credentials from Zscaler UI.
Client_ID and Client_Secret:
We can get the Customer_ID from TenantID:
Using Terraformer
If we use Terraformer to get the all existing resopurces for "zpa_policy_access_rule", it will not only create a state file but also a content file for us:
/usr/local/bin/zscaler-terraformer import --resource-type "zpa_policy_access_rule" .... terraform import zpa_policy_access_rule.resource_khong_954129033976677216 954129033976677216 terraform import zpa_policy_access_rule.resource_khongtest_954129033976677219 954129033976677219
Terraformer, once importing is done, we can see it created files under zpa folder:
terraform.tfstate files has a current zpa policy rule state and the zpa_policy_access_rule.tf has the current terraform configuration file:
... resource "zpa_policy_access_rule" "resource_khongtest_954129033976677219" { action = "ALLOW" bypass_default_rule = false lss_default_rule = false name = "khong-test" operator = "AND" policy_type = "1" priority = "1" reauth_default_rule = false rule_order = "194" app_connector_groups { id = [] } app_server_groups { id = [] } conditions { negated = false operator = "OR" operands { lhs = "id" name = "jenkins.bogo.xyz" object_type = "APP" rhs = "954129033976676919" } } conditions { negated = false operator = "OR" operands { lhs = "954129033976676547" name = "Email" object_type = "SAML" rhs = "khong@bogotobogo.com" } operands { lhs = "954129033976676702" name = "GroupName" object_type = "SAML" rhs = "zscalerus" } } conditions { negated = false operator = "OR" operands { lhs = "id" name = "zpn_client_type_zapp" object_type = "CLIENT_TYPE" rhs = "zpn_client_type_zapp" } } }
Note that if we want to import partial resources, we can take the console outputs for those resourses, for example:
terraform import zpa_policy_access_rule.resource_khongtest_954129033976677219 954129033976677219
The import command tells Zscaler that terraform will handle only the resource that's being imported not others. So, once imported, we can play with the resource not worrying about other resources.
Terraform
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization