Terraform (TF) is a declarative language commonly used to create and maintain cloud infrastructure.   In Terraform you do not have to specify how to orchestrate and build the infrastructure, instead, using TF language, you describe the end state of the infrastructure.  TF will handle the hard work of building the infrastructure in a logical order.

TF supports many cloud providers including AlibabaCloud, Azure and AWS. We will walk through how to create an AWS RDS PostgresSQL database engine using TF. The same script can be applied to other cloud providers such as AlibabaCloud and Azure. (minus some provider-specific parts)

Using a Linux instance install Terraform and create a directory to place your TF files. Note that TF will evaluate all files that end int '.tf' and '.tfvars' files in a particular directory and subdirectory.

wget https://releases.hashicorp.com/terraform/0.12.0/terraform_0.12.0_linux_amd64.zip -O tf.zip

apt install unzip
unzip ./tf.zip -d "terraform"

# Create a directory to store .tf files
mkdir terraformTest
cd terraformTest/

Create 'example.tf' files and use the following TF scripts as an example, you will need to replace the xxxxx with a valid subnet and vpc-id.  Save and exit the script once you have done editing.

From within the "terraformTest" directory, initiate the TF and run a plan to see the planned changes that TF will perform if you run the apply command.

../terraform/terraform init
../terraform/terraform plan

# Run refresh to recreate the state file for existing infrastructure. 
../terraform/terraform refresh

You may need to set the credentials at the global variable level if you have not used the cloud-specific providers in the .tf file (source). In my example, I have used STS and set the  AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, and  AWS_SESSION_TOKEN at the global variable level.

provider "aws" {
  profile = "default"
  region = "us-east-1"
resource "aws_db_subnet_group" "subnet-mytest-db" {
  name = "subnet_mytest_db"
  subnet_ids = ["subnet-xxxxxx1","subnet-xxxxxx2"]
  tags = {
    Name        = "mytest-db-subnet-group"
resource "aws_security_group" "allow-mytest-db" {
  name        = "allow_mytest_db"
  description = "Allow all inbound traffic"
  vpc_id      = "vpc-xxxxxxxxxx"
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = [""]
resource "random_string" "postgres_password" {
  length = 16
  special = false
resource "aws_db_instance" "myTestDB" {
  identifier           = "mytest-db"
  allocated_storage    = "10"
  storage_type         = "gp2"
  engine               = "postgres"
  engine_version       = "10.6"
  allow_major_version_upgrade = true
  auto_minor_version_upgrade = false
  instance_class       = "db.t2.micro"
  name                 = "myTestDB"
  username             = "myTestDBUser"
  password             = "${random_string.postgres_password.result}"
  db_subnet_group_name = "${aws_db_subnet_group.subnet-mytest-db.name}"
  skip_final_snapshot  = true
  vpc_security_group_ids = ["${aws_security_group.allow-mytest-db.id}"]
  tags = {
    Name        = "mytest-db"
resource "aws_ssm_parameter" "mytest-db-connection-string" {
  name        = "/dev/mytest-db-connection-string"
  description = "The database connectionstring"
  type        = "SecureString"
  value       = "Server=${aws_db_instance.myTestDB.address};Database=${aws_db_instance.myTestDB.name};UserId=myTestDBUser;Password=${random_string.postgres_password.result}"
  tags = {
    Name        = "mytest-db-connection-string"