Imagine a scenario where you might prefer to provision the resources manually in Cloud because it’s quick and easy. Maybe you needed a Cloud resource such as security group in a hurry, or someone on the team just created it without thinking about Terraform. Fast forward a few weeks, and now you’ve ended with a mix up of manual and Terraform-managed resources, which can quickly become disorganized.
Now question arises in such condition how do we manage those resources which were not provisioned via Terraform? Answer is Simple: we bring those manually created resources under Terraform’s control without destroying and recreating them.
Why Bother Managing Existing Resources with Terraform?
Managing Cloud resources manually seems fine at first, but the moment you need to make changes (or debug an issue), things get annoying:
- No tracking – You don’t know who changed what and when.
- Inconsistencies creep in – One server has port 22 open, another doesn’t. Now you’re scratching your head.
- Scaling issue – Imagine recreating the same thing across multiple environments manually. A Big Nightmare!
Terraform fixes all of this by treating infrastructure as code. But what about the resources we already created manually? That’s where importing comes in.
Note: In this guide, I have used AWS as an example cloud platform to demonstrate the process. Always take a backup of your Terraform state before importing. I have also included AWS CLI commands to retrieve resource information from AWS. Therefore, I recommend reading the official AWS CLI guide to learn how to install and configure AWS CLI on your system.
Procedure to Import Manually Created Resources Under Terraform’s Control
In the following steps, I will guide you through the process of bringing manually created resources under Terraform’s management without recreating them.
Step 1: Find the Resource in AWS
Let’s say we have a security group name RDSSecurityGroup that we created manually to allow access to RDS instance. First, we need to grab its ID. If you don’t remember the name, just list all security groups through AWS CLI command:
aws ec2 describe-security-groups
If you do remember the name, filter it like this:
aws ec2 describe-security-groups –filters “Name=group-name,Values=my-security-group”
This will spit out a bunch of details as below, but the most important thing is the Security Group ID (sg-xxxxxxxx). Hold on to that:
PS C:\Users> aws ec2 describe-security-groups –filters “Name=group-name,Values=RDSSecurityGroup”
{
“SecurityGroups”: [
{
“GroupId”: “sg-*************”,
“IpPermissionsEgress”: [
{
“IpProtocol”: “-1”,
“UserIdGroupPairs”: [],
“IpRanges”: [
{
“CidrIp”: “XXX.XXX.XXX.XXX/XX”
}
],
“Ipv6Ranges”: [],
“PrefixListIds”: []
}
],
“VpcId”: “vpc-********”,
“SecurityGroupArn”: “arn:aws:ec2:ap-south-1:************:security-group/sg-*************”,
“OwnerId”: “************”,
“GroupName”: “RDSSecurityGroup”,
“Description”: “RDS instance Access Security Group”,
“IpPermissions”: [
{
“IpProtocol”: “tcp”,
“FromPort”: 3306,
“ToPort”: 3306,
“UserIdGroupPairs”: [],
“IpRanges”: [
{
“CidrIp”: “XXX.XXX.XXX.XXX/XX”
}
],
“Ipv6Ranges”: [],
“PrefixListIds”: []
}
]
}
]
}
PS C:\Users>
Note: You can also get the same resource details as below from the AWS web console, but I find AWS CLI much more convenient:
Step 2: Write Terraform Code for It
Now, we need to define this security group in Terraform. Here’s a simple example:
provider “aws” {
region = ” ap-south-1″ # Change this to your region
}
resource “aws_security_group” “existing_sg” {
name = “RDSSecurityGroup”
description = “This security group was created manually, but now we’re managing it with Terraform.”
vpc_id = “vpc-xxxxxxxx” # Replace with your actual VPC ID
ingress {
from_port = 3306
to_port = 3306
protocol = “tcp”
cidr_blocks = [“192.168.0.254/32”] # Replace with your desired IP address
}
}
Now, here’s the thing Terraform doesn’t know this resource already exists in AWS. If we run terraform apply right now, it’ll try to create a new one, which we don’t want. So, we need to import the existing security group into Terraform’s state.
Step 3: Import the Resource
First, make sure Terraform is set up:
terraform init
Then, import the security group using its ID:
terraform import aws_security_group.existing_sg sg-xxxxxxxx
Voila! Terraform now knows this security group exists and will manage it going forward. Check out the terraform show command below, which displays the imported RDS Security Group information fetched from the state file:
Updating the Configuration of Imported Resources through Terraform
In the previous procedure, we successfully imported the RDS Security Group under Terraform control. Now, what if we want to update the configuration of this imported security group through Terraform? In that case, we need to make changes in the same Terraform configuration file and run the following command:
terraform plan
If it shows any differences and everything looks good, apply the changes using the command below. For example, I am updating the description of the RDS Security Group:
terraform apply
PS C:\Users\charanjit\terraform_import_aws_resource> terraform apply –auto-approve
aws_security_group.existing_sg: Refreshing state… [id=sg-**************]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_security_group.existing_sg must be replaced
-/+ resource “aws_security_group” “existing_sg” {
~ arn = “arn:aws:ec2:ap-south-1:***********:security-group/sg-**********” -> (known after apply)
~ description = “RDS instance Access Security Group” -> “This security group was created manually but now we are managing it with Terraform.” # forces replacement
~ egress = [
– {
– cidr_blocks = [
– “0.0.0.0/0”,
]
– from_port = 0
– ipv6_cidr_blocks = []
– prefix_list_ids = []
– protocol = “-1”
– security_groups = []
– self = false
– to_port = 0
# (1 unchanged attribute hidden)
},
] -> (known after apply)
~ id = “sg-*****************” -> (known after apply)
# Warning: this attribute value will be marked as sensitive and will not
# display in UI output after applying this change. The value is unchanged.
~ ingress = (sensitive value)
name = “RDSSecurityGroup”
+ name_prefix = (known after apply)
~ owner_id = “************” -> (known after apply)
+ revoke_rules_on_delete = false
– tags = {} -> null
~ tags_all = {} -> (known after apply)
# Warning: this attribute value will be marked as sensitive and will not
# display in UI output after applying this change. The value is unchanged.
~ vpc_id = (sensitive value)
}
Plan: 1 to add, 0 to change, 1 to destroy.
aws_security_group.existing_sg: Destroying… [id=sg-*****************]
aws_security_group.existing_sg: Destruction complete after 1s
aws_security_group.existing_sg: Creating…
aws_security_group.existing_sg: Creation complete after 2s [id=sg-*****************]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
PS C:\Users\charanjit\terraform_import_aws_resource>
And that’s it! Your security group is now updated through Terraform. You can also check from the AWS Console, as shown in the screenshot below, to see that the RDS Security Group description has been updated:
What About Other AWS Resources?
This procedure works for a lot of things: EC2 instances, S3 buckets, IAM roles, RDS databases, load balancers, you name it. The process is pretty much the same:
- Find the resource ID using AWS CLI or AWS Console.
- Write the Terraform code for it.
- Import it into Terraform state.
- Run terraform plan and terraform apply if a configuration update is required.
Wrap Up!
If you or your team have manually created AWS resources, now’s the time to bring them under Terraform’s management. It might feel like a hassle at first, but trust me, it’ll save you so much time down the road. No more wondering who changed what, no more manual mistakes, just clean, version-controlled infrastructure that’s compliant with DevSecOps processes.
You can check my Below GitHub link in which you can see how I have written code as per real-world examples:
https://github.com/cjcheema/terraform_import_aws_resource
Hope this helps! If you liked my blog, feel free to leave a comment and share it with your network.
- How to Bring and Manage Manually Created AWS Resources Under Terraform Management - January 31, 2025
- Iterating Cloud Resource Provisioning Using Terraform Count and For_Each Meta-Arguments - January 27, 2025
- Terraform and Ansible Collaboration for AWS Cloud Deployment - August 28, 2024