Category Archives: Terraform

Learn Terraform with DevOpsRoles.com. Access detailed guides and tutorials to master infrastructure as code and automate your DevOps workflows using Terraform.

Resolve No Valid Credential Sources Found for AWS Provider Error in Terraform: A Deep Guide

Introduction

Terraform is a powerful tool for managing infrastructure as code, especially when working with AWS. However, you may occasionally encounter the dreaded error: Error: No valid credential sources found for AWS Provider. This issue can disrupt your workflow and delay your deployment processes. This deep guide aims to provide you with a comprehensive understanding of the possible causes and solutions for this error. We’ll cover everything from basic configurations to advanced troubleshooting techniques, ensuring that you have the knowledge to resolve this error quickly and effectively.

Understanding the AWS Provider Error in Terraform

The error message Error: No valid credential sources found for AWS Provider typically occurs when Terraform cannot locate valid AWS credentials to authenticate API requests. AWS credentials are essential for Terraform to manage your AWS resources, and without them, Terraform cannot perform any actions on your AWS account.

How Terraform Authenticates with AWS

Terraform uses the AWS provider plugin to interact with AWS services. To authenticate, Terraform relies on a variety of credential sources, including environment variables, AWS credentials files, and IAM roles. If none of these sources are properly configured or accessible, Terraform throws the “No valid credential sources found” error.

Key Credential Sources

Terraform looks for AWS credentials in the following order:

  1. Environment Variables: The most straightforward method for setting AWS credentials.
  2. Shared Credentials File: Typically located at ~/.aws/credentials.
  3. AWS Config File: Located at ~/.aws/config, used for profile settings.
  4. IAM Role for EC2: Used when Terraform is run from an EC2 instance with an attached IAM role.
  5. Assume Role with MFA: Requires temporary credentials generated using MFA.

Basic Troubleshooting Steps

Let’s start with the basics. These initial steps often resolve the issue quickly without delving into more complex solutions.

1. Verifying Environment Variables

Environment variables are a primary method for setting AWS credentials. Terraform specifically looks for the following:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN (optional, for temporary credentials)

You can check whether these variables are set using the command line:

echo $AWS_ACCESS_KEY_ID
echo $AWS_SECRET_ACCESS_KEY

If these commands return empty values, it means the environment variables are not set, and you need to configure them:

export AWS_ACCESS_KEY_ID=your_access_key_id
export AWS_SECRET_ACCESS_KEY=your_secret_access_key
export AWS_SESSION_TOKEN=your_session_token  # Optional

2. Validating the AWS CLI Configuration

If you have the AWS CLI installed, a simple way to test your credentials is to run:

aws sts get-caller-identity

This command returns details about the AWS account and identity that the credentials belong to. If the command fails, you may need to reconfigure the AWS CLI:

aws configure

During configuration, you’ll be prompted to enter your AWS access key ID, secret access key, region, and output format.

3. Checking the Shared Credentials File

Terraform also looks for credentials in the shared credentials file, typically located at ~/.aws/credentials. Open this file to ensure it’s properly configured:

[default]
aws_access_key_id = your_access_key_id
aws_secret_access_key = your_secret_access_key

[profile_name]
aws_access_key_id = your_profile_access_key_id
aws_secret_access_key = your_profile_secret_access_key

Make sure that the profile specified in your Terraform configuration matches the profile name in the credentials file.

4. Ensuring the AWS Profile is Correctly Configured

If you’re using a specific AWS profile in Terraform, confirm that it’s correctly configured in both your credentials file and your Terraform provider block:

provider "aws" {
  profile = "your_profile_name"
  region  = "us-west-2"
}

You can list all available profiles using:

aws configure list-profiles

Advanced Troubleshooting Techniques

If the basic steps above don’t resolve the issue, you may need to employ more advanced troubleshooting techniques. These techniques help diagnose and fix more complex issues that might be causing the error.

1. Using IAM Roles in Terraform

When deploying Terraform configurations on EC2 instances or using IAM roles, the setup might involve assuming a role. Here’s how you can ensure this is configured correctly:

provider "aws" {
  assume_role {
    role_arn     = "arn:aws:iam::account-id:role/role-name"
    session_name = "session_name"
  }
  region = "us-west-2"
}

If your IAM role requires MFA, you’ll need to configure Terraform to handle this by obtaining temporary credentials.

2. Debugging Terraform Commands

Sometimes, understanding what Terraform is attempting to do can help in diagnosing the problem. Terraform provides a debugging option to output detailed logs:

export TF_LOG=DEBUG
terraform plan

The output will include detailed information on the actions Terraform is attempting to perform and where it might be failing.

3. Handling Temporary Security Credentials

If you are using temporary security credentials (like those obtained from STS), ensure they are valid and not expired. Temporary credentials are often used in environments that require additional security measures, such as roles that assume MFA.

To verify the validity of temporary credentials:

aws sts get-session-token

Ensure your Terraform configuration is using these credentials correctly by setting them in the environment variables or directly in the provider block.

4. IAM Permissions and Policy Checks

Even if your credentials are correct, you might encounter issues if the IAM user or role doesn’t have the necessary permissions to execute the Terraform operations. Verify the permissions attached to your IAM user or role:

aws iam list-attached-user-policies --user-name your_user_name

Ensure the policies attached grant sufficient permissions for the AWS services you’re trying to manage with Terraform.

5. Leveraging Instance Metadata Service (IMDS)

For EC2 instances, Terraform can automatically use credentials from the instance metadata if the instance has an attached IAM role with the necessary permissions. To troubleshoot IMDS-related issues, run:

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

This will return the IAM role attached to the instance and the corresponding credentials.

Handling Edge Cases

Edge cases can occur in more complex environments or configurations. Below are some less common scenarios and how to address them.

Using Multiple AWS Accounts

If you’re working across multiple AWS accounts, ensure that the correct account is being used in your Terraform configuration. It’s important to specify the correct role or credentials for each account.

provider "aws" {
  alias  = "account1"
  region = "us-west-2"
  assume_role {
    role_arn     = "arn:aws:iam::account-id:role/role-name"
    session_name = "session_name"
  }
}

provider "aws" {
  alias  = "account2"
  region = "us-east-1"
  assume_role {
    role_arn     = "arn:aws:iam::another-account-id:role/role-name"
    session_name = "session_name"
  }
}

Configuring Terraform with MFA

Using MFA with Terraform can add an extra layer of security but requires additional configuration. You need to generate temporary credentials using the aws sts get-session-token command and configure Terraform to use them.

aws sts get-session-token --serial-number arn-of-the-mfa-device --token-code code-from-mfa-device

Set the session token in your environment variables:

export AWS_SESSION_TOKEN=your_session_token

Common Mistakes and Misconfigurations

Some common mistakes that lead to the No valid credential sources found for AWS Provider error include:

  • Incorrect file paths: Make sure your .aws/credentials and .aws/config files are in the correct location.
  • Typo in profile names: Ensure that profile names are correctly spelled in both Terraform and AWS CLI configurations.
  • Expired credentials: Regularly rotate credentials and ensure temporary credentials are renewed before they expire.

Frequently Asked Questions (FAQs)

Q: What does “No valid credential sources found for AWS Provider” mean?

A: This error occurs when Terraform is unable to find valid AWS credentials needed to authenticate API requests. It usually points to misconfigured environment variables, incorrect AWS profiles, or missing credentials files.

Q: How can I check if my AWS credentials are working?

A: You can verify your AWS credentials by running aws sts get-caller-identity in the command line. If it returns valid information, your credentials are correctly configured.

Q: Can I use IAM roles with Terraform?

A: Yes, Terraform supports IAM roles. You can configure Terraform to assume a role by using the assume_role block in the AWS provider configuration.

Q: How do I set temporary credentials in Terraform?

A: Temporary credentials can be set in Terraform using environment variables such as AWS_SESSION_TOKEN. These credentials are typically obtained using the AWS STS service.

Q: What should I do if my Terraform deployment is on an EC2 instance?

A: Ensure that the EC2 instance has an IAM role attached with the necessary permissions. Terraform will automatically use credentials from the instance metadata service.

Conclusion

Resolving the No valid credential sources found for AWS Provider error in Terraform requires careful examination of how your AWS credentials are configured. By following the steps outlined in this guide—from basic checks of environment variables to more advanced IAM role configurations—you can troubleshoot and resolve this error efficiently. As always, ensure that your credentials are up-to-date and that your IAM roles have the necessary permissions to avoid encountering this issue in the future. Thank you for reading the DevopsRoles page!

How to Fix Instance Not Found Error in Terraform: A Deep Guide

Introduction

Terraform has revolutionized the way infrastructure is managed, allowing for the efficient and automated deployment of resources. However, like any tool, it is not immune to errors. One particularly frustrating error that many users encounter is the Instance not found error. This error can arise due to a variety of reasons, from simple configuration issues to more complex state management problems. In this deep guide, we will explore the causes of this error and provide a comprehensive approach to resolving it.

What is the Instance Not Found Error in Terraform?

Understanding the Error

The “Instance not found” error typically occurs when Terraform is unable to locate a resource that it expects to manage. This can happen for various reasons, including:

  • The resource was manually deleted outside of Terraform.
  • The resource was moved or renamed.
  • The Terraform state file is out of sync with the actual infrastructure.

When this error occurs, Terraform may fail to apply further changes, leaving your infrastructure in an inconsistent state.

Why Does This Error Matter?

This error can halt your Terraform workflows, preventing you from deploying, updating, or destroying resources as needed. It can also lead to unexpected behavior in your infrastructure, such as resources not being properly managed or updated.

Basic Troubleshooting Steps

1. Check for Manual Deletion

One of the most common causes of the “Instance not found” error is that the resource was manually deleted outside of Terraform, such as directly through the cloud provider’s console.

  • Step 1: Log in to your cloud provider’s management console (e.g., AWS, Azure, Google Cloud).
  • Step 2: Navigate to the resource type in question (e.g., EC2 instances, S3 buckets).
  • Step 3: Verify whether the resource still exists.

If the resource has been deleted, Terraform will no longer be able to manage it, resulting in the “Instance not found” error.

2. Review Terraform Configuration

Another possible cause is a mismatch between your Terraform configuration files and the actual state of your infrastructure.

  • Step 1: Open your Terraform configuration files and review the resource block that corresponds to the missing instance.
  • Step 2: Ensure that all resource names, IDs, and other parameters are correct and match the actual infrastructure.
  • Step 3: Run terraform plan to see what changes Terraform plans to make.

3. Check the Terraform State File

Terraform uses a state file (terraform.tfstate) to keep track of the resources it manages. If the state file is out of sync with the actual infrastructure, Terraform might fail to find the resource.

  • Step 1: Run terraform state list to list all resources currently tracked by Terraform.
  • Step 2: Identify the resource that is causing the error.
  • Step 3: Check if the resource still exists in the state file.

If the resource is missing from the state file but still exists in your cloud provider, you might need to import it back into Terraform (more on that later).

Intermediate Troubleshooting Techniques

4. Use terraform state rm to Remove the Resource

If a resource is no longer needed or cannot be found, you can remove it from Terraform’s state file using the following command:

terraform state rm <resource_address>
  • Example: terraform state rm aws_instance.my_instance

This command removes the resource from the state file, allowing Terraform to proceed without managing the missing resource.

5. Refresh the State File

Sometimes, the state file might become outdated, especially if changes were made outside of Terraform. Refreshing the state file updates it to reflect the current state of your infrastructure.

terraform refresh
  • Step 1: Run terraform refresh to update the state file with the latest information from your cloud provider.
  • Step 2: Rerun terraform plan to verify that the error is resolved.

6. Inspecting the State File Manually

For more advanced users, manually inspecting the state file can provide insights into why Terraform cannot find the resource.

  • Step 1: Open the terraform.tfstate file in a text editor.
  • Step 2: Search for the resource in question and review its details.
  • Step 3: Ensure that the resource information matches the actual infrastructure.

If discrepancies are found, consider manually correcting the state file or re-importing the resource.

Advanced Troubleshooting Techniques

7. Recreate the Missing Resource

If the resource is crucial to your infrastructure and has been deleted, you can recreate it using Terraform.

  • Step 1: Use the -replace flag to force Terraform to recreate the resource:
terraform apply -replace=<resource>
  • Step 2: Confirm that the resource has been recreated successfully.

This technique is particularly useful when the resource is critical and must be present for the infrastructure to function correctly.

8. Importing an Existing Resource into Terraform

If the resource still exists but Terraform has lost track of it, you can import it back into Terraform’s state file using the terraform import command.

terraform import <resource_address> <resource_id>
  • Example: terraform import aws_instance.my_instance i-1234567890abcdef
  • Step 1: Identify the resource’s address in your Terraform configuration.
  • Step 2: Use the terraform import command to import the resource into the state file.

This allows Terraform to resume management of the resource without needing to recreate it.

9. State File Surgery

For complex scenarios, you might need to manually edit the state file to resolve inconsistencies. This process, often referred to as “state file surgery,” should be done with caution.

  • Step 1: Back up your current state file before making any changes.
  • Step 2: Use a text editor to carefully modify the state file, ensuring that all references to the missing resource are accurate.
  • Step 3: Save the state file and run terraform plan to verify that the changes are correct.

State file surgery is an advanced technique and should only be used when other methods have failed.

Preventing Future “Instance Not Found” Errors

10. Use Remote State Storage

One of the best practices to prevent state file issues is to use remote state storage, such as AWS S3, Azure Blob Storage, or Terraform Cloud.

  • Benefit 1: Remote state storage ensures that your state file is always accessible and can be easily shared among team members.
  • Benefit 2: It reduces the risk of state file corruption or loss.

11. Avoid Manual Changes Outside Terraform

To prevent discrepancies between your Terraform state file and actual infrastructure, avoid making manual changes outside of Terraform.

  • Best Practice: Implement a policy that all infrastructure changes must go through Terraform.
  • Benefit: This ensures that the state file is always in sync with the actual infrastructure.

12. Regularly Backup Your State File

Regularly backing up your state file can save you from a lot of headaches if things go wrong.

  • Tip: Automate state file backups using your cloud provider’s tools or a CI/CD pipeline.
  • Benefit: In case of an issue, you can restore the state file from a backup, minimizing downtime.

FAQs

Q1: What is the most common cause of the Instance not found error in Terraform?

The most common cause is the manual deletion of a resource outside of Terraform, leading to a mismatch between the state file and the actual infrastructure.

Q2: How can I prevent Terraform from trying to manage a resource that no longer exists?

You can use the terraform state rm command to remove the resource from the state file, allowing Terraform to proceed without managing it.

Q3: Can I manually edit the Terraform state file to fix the Instance not found error?

Yes, but it should be done with caution. Manually editing the state file is an advanced technique and should only be attempted if other troubleshooting methods have failed.

Q4: How can I ensure that my Terraform state file is always up-to-date?

Regularly running terraform refresh can help keep your state file in sync with the actual infrastructure. Additionally, avoid making manual changes outside of Terraform.

Q5: What should I do if I encounter the Instance not found error but the resource still exists?

You can use the terraform import command to re-import the resource into the state file, allowing Terraform to manage it again.

Conclusion

The Instance not found error in Terraform can be a complex issue to resolve, but with the right approach, it is manageable. By following the steps outlined in this guide, you can identify the root cause of the error and apply the appropriate solution. Remember to implement best practices, such as using remote state storage and avoiding manual changes, to prevent this error from occurring in the future. Thank you for reading the DevopsRoles page!

This deep guide has provided a thorough examination of the Instance not found error in Terraform, from basic troubleshooting steps to advanced techniques. By understanding and applying these solutions, you can maintain the integrity of your Terraform-managed infrastructure and avoid disruptions in your workflows.

Fix Provider Configuration Not Present Error in Terraform: A Deep Guide

Introduction

Terraform is an open-source infrastructure-as-code software tool that enables users to define and provision data center infrastructure using a high-level configuration language. However, despite its power and flexibility, users sometimes encounter issues that can disrupt their workflows. One common issue is the Provider Configuration Not Present Error, which can be frustrating and confusing, especially for those new to Terraform.

This comprehensive guide will delve into the causes and solutions for this error, providing a deep dive into the mechanics of Terraform provider configuration. We’ll cover both basic and advanced troubleshooting techniques, offering a path to resolution regardless of your familiarity with Terraform.

Understanding Terraform Provider Configuration

What Is a Terraform Provider?

Before diving into the error, it’s essential to understand what a Terraform provider is. A provider in Terraform is a plugin that allows Terraform to manage and interact with resources on a particular platform or service. Each provider is responsible for understanding API interactions and exposing resources for Terraform to manage. For example, the AWS provider allows Terraform to create and manage AWS resources like EC2 instances, S3 buckets, and more.

How Does Terraform Handle Provider Configuration?

Terraform requires users to specify provider configurations in their configuration files. This configuration tells Terraform which provider to use and how to authenticate and connect to it. The provider configuration typically includes credentials, regions, and other parameters required to interact with the provider’s API.

provider "aws" {
  region  = "us-west-2"
  version = "~> 3.0"
}

Why Is Provider Configuration Important?

Without a proper provider configuration, Terraform cannot interact with the desired resources, leading to errors during the plan or apply stages. The “Provider Configuration Not Present” error occurs when Terraform cannot find the necessary configuration to proceed.

The Root Causes of the Provider Configuration Not Present Error

Missing Provider Block

The most straightforward cause of this error is the absence of a provider block in your Terraform configuration file. Without this block, Terraform has no way of knowing how to connect to the provider.

Incorrect Provider Version

Terraform providers are versioned, and using an incompatible or outdated version can cause configuration issues. Specifying the correct version ensures that Terraform uses the appropriate provider features and API endpoints.

Module Dependency Issues

When using modules, Terraform may have difficulty locating the provider configuration if it’s not explicitly passed down. Modules are isolated and do not inherit provider configurations automatically, leading to potential errors.

Incorrect or Corrupted State File

Terraform maintains a state file that keeps track of the resources it manages. If this state file becomes corrupted or misaligned with your configuration, Terraform may not be able to find the necessary provider configuration.

Misconfigured Workspaces

Terraform workspaces allow users to manage multiple environments with the same configuration. However, if the provider configuration is not correctly set for each workspace, it can lead to the Provider Configuration Not Present error.

Step-by-Step Guide to Fixing the Error

Step 1: Verify the Provider Block

The first and most crucial step is to ensure that your Terraform configuration includes a valid provider block. For instance, if you’re working with AWS, your provider block should look like this:

provider "aws" {
  region  = "us-west-2"
  version = "~> 3.0"
}

Make sure this block is present in your configuration file and that it specifies the correct region and version.

Step 2: Initialize Terraform

If the provider block is correctly configured, the next step is to initialize Terraform. The initialization process downloads the necessary provider plugins and prepares the environment for further operations.

terraform init

Step 3: Upgrade the Provider

If the error persists, you may need to upgrade the provider to the latest version. This ensures that you are using a version of the provider that is compatible with your Terraform configuration.

terraform init -upgrade

Step 4: Validate the Configuration

Validation is a critical step to ensure that your Terraform configuration is syntactically correct and that all required providers are properly configured. The terraform validate the command checks your configuration for errors:

terraform validate

Step 5: Explicitly Define Provider Source in Modules

If you are using modules, you might need to pass the provider configuration explicitly to the module. This ensures that the module uses the correct provider settings.

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "2.77.0"

  providers = {
    aws = aws.primary
  }
}

provider "aws" {
  alias  = "primary"
  region = "us-east-1"
}

Step 6: Review and Rebuild the State File

The state file is a critical component of Terraform’s operation. If the state file is corrupted or outdated, you may need to refresh or rebuild it:

terraform refresh

This command refreshes the state file with the actual state of your infrastructure, aligning it with your configuration.

Step 7: Use Terraform Workspaces Correctly

If you are managing multiple environments using workspaces, ensure that each workspace has the correct provider configuration. Switch between workspaces using the following commands:

terraform workspace new dev
terraform workspace select dev

Step 8: Inspect Dependency Graphs

Terraform provides a way to inspect the dependency graph of your configuration. This can help you identify issues related to provider configuration:

terraform graph

By analyzing the graph, you can see how providers and modules are connected, which may help identify where the configuration is missing or incorrect.

Step 9: Upgrade Terraform

If you have tried all the above steps and the error persists, consider upgrading Terraform to the latest version:

terraform version
terraform upgrade

Ensure that the Terraform version you are using is compatible with your provider versions and other plugins.

Advanced Techniques for Troubleshooting

Using Terraform Debug Logs

Terraform provides a way to enable debug logs, which can give you more insight into what’s happening during the plan or apply processes. To enable debugging, set the following environment variable:

export TF_LOG=DEBUG

Run your Terraform commands again, and you’ll see detailed logs that may help identify the source of the error.

Managing Multiple Providers

If your configuration involves multiple providers, you may need to manage them more explicitly to avoid conflicts. For example, using provider aliases can help Terraform distinguish between different providers:

provider "aws" {
  alias  = "primary"
  region = "us-west-2"
}

provider "aws" {
  alias  = "secondary"
  region = "us-east-1"
}

module "my_module" {
  source   = "./module"
  providers = {
    aws = aws.primary
  }
}

Automating Provider Configuration Management

In larger infrastructure deployments, managing provider configurations manually can become cumbersome. Consider using automation tools like Terraform Cloud or Terraform Enterprise to manage provider configurations at scale. These tools provide centralized management of provider settings, reducing the likelihood of errors.

Custom Terraform Modules with Provider Configurations

If you are developing custom Terraform modules, ensure that your module explicitly defines which providers it depends on and passes these configurations from the root module. This can prevent the Provider Configuration Not Present error when others use your module.

FAQs

What does the Provider Configuration Not Present error mean?

This error indicates that Terraform cannot find the necessary provider configuration to execute the plan or apply command. It usually occurs when the provider block is missing, the provider version is incorrect, or there are issues with the state file or module dependencies.

How can I prevent the Provider Configuration Not Present error in the future?

To prevent this error, always ensure that your provider blocks are correctly configured and validated. Use Terraform workspaces and modules appropriately, and regularly upgrade your Terraform and provider versions.

What should I do if upgrading Terraform does not fix the error?

If upgrading Terraform doesn’t resolve the error, consider inspecting the state file, using debug logs, or explicitly managing multiple providers. You may also need to consult Terraform’s documentation or community forums for specific cases.

Can this error occur with any Terraform provider?

Yes, the “Provider Configuration Not Present” error can occur with any provider if the configuration is not properly set up. Whether you’re using AWS, Azure, GCP, or any other provider, the steps to resolve the error are generally similar.

Conclusion

The Provider Configuration Not Present error in Terraform can be a challenging issue to resolve, especially in complex infrastructure environments. However, by following the steps outlined in this guide, you can troubleshoot and fix the error, ensuring that your Terraform configurations run smoothly.

From verifying provider blocks to advanced techniques like using debug logs and managing multiple providers, this guide provides a comprehensive approach to resolving the error. Remember to validate your configurations, upgrade your tools, and keep your Terraform setup aligned with best practices to avoid encountering this error in the future. Thank you for reading the DevopsRoles page!

By mastering these techniques, you’ll be well-equipped to handle not only the Provider Configuration Not Present error but any other challenges that come your way in Terraform infrastructure management.

Terraform 1.9 features: Explore Enhanced Input Validation and Advanced String Template Functionality

Introduction

Terraform, the popular open-source infrastructure as code (IaC) tool, continues to evolve with its latest release, Terraform 1.9 features. This update brings significant enhancements, particularly in input validation and the introduction of a new string template function. Whether you’re a beginner or an advanced user, understanding these new features can significantly improve your infrastructure management and automation processes.

In this article, we will explore the key features of Terraform 1.9, focusing on enhanced input validation and the new string template function. We’ll provide examples that range from basic to advanced to ensure you can implement these features effectively in your projects.

What’s New in Terraform 1.9 features?

Enhanced Input Validation

What is Input Validation in Terraform?

Input validation in Terraform ensures that the values provided to variables or resources conform to the expected format and constraints. This feature is crucial for maintaining the integrity and reliability of your infrastructure configurations.

The Importance of Enhanced Input Validation

In previous versions of Terraform, input validation was somewhat limited, often requiring external tools or scripts to enforce complex validation rules. With Terraform 1.9, input validation has been significantly improved, allowing for more sophisticated checks directly within your Terraform configuration files.

Key Improvements in Input Validation

  • Complex Conditional Logic: You can now define complex conditional logic within your validation rules, ensuring that only valid combinations of inputs are accepted.
  • Detailed Error Messages: Terraform 1.9 allows you to provide more detailed error messages, making it easier for users to understand what went wrong when a validation fails.
  • Regex Support: Enhanced regex support enables more precise validation of string inputs, which is particularly useful for enforcing naming conventions or ensuring valid formats for URLs, emails, etc.

Example of Enhanced Input Validation

Let’s look at a basic example:

variable "instance_type" {
  type    = string
  default = "t2.micro"

  validation {
    condition     = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
    error_message = "Instance type must be one of t2.micro, t2.small, or t2.medium."
  }
}

In this example, Terraform checks that the provided instance_type is one of the allowed values. If an invalid value is provided, Terraform will output the specified error message.

New String Template Function

Understanding String Templates in Terraform

String templates in Terraform allow you to create dynamic strings by embedding expressions within ${}. This functionality is essential for creating flexible and reusable infrastructure configurations.

What’s New in Terraform 1.9?

Terraform 1.9 features introduce a new string template function that significantly expands the capabilities of string manipulation. This function provides more control over how strings are formatted and allows for more complex string operations.

Key Features of the New String Template Function

  • Advanced String Formatting: You can now format strings with more precision, including padding, alignment, and case conversion.
  • Conditional Expressions: Embed conditional logic within your string templates to create dynamic outputs based on variable values.
  • Enhanced Looping Constructs: Loop through lists or maps within your templates to generate complex configurations dynamically.

Example of the New String Template Function

Here’s an example demonstrating the new string template function:

output "formatted_message" {
  value = format("Hello, %s! Your server count is %d.", var.username, length(var.servers))
}

In this example, the format function dynamically creates a message that includes the username and the number of servers in the servers list.

Practical Use Cases of Terraform 1.9

Implementing Enhanced Input Validation in Real Projects

Scenario 1: Validating IP Addresses

Suppose you have a variable that accepts an IP address. With Terraform 1.9, you can validate that the input is a valid IP address using regex:

variable "ip_address" {
  type = string

  validation {
    condition     = can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}$", var.ip_address))
    error_message = "The IP address must be a valid IPv4 address."
  }
}

This validation ensures that only valid IPv4 addresses are accepted, preventing misconfigurations in your network resources.

Advanced String Template Functions for Complex Configurations

Scenario 2: Dynamic Naming Conventions

In large-scale deployments, maintaining consistent naming conventions is crucial. Terraform 1.9 features a new string template function that allows you to enforce and automate naming conventions dynamically:

variable "environment" {
  type = string
  default = "production"
}

variable "component" {
  type = string
  default = "web"
}

output "resource_name" {
  value = format("%s-%s-%s", var.environment, var.component, timestamp())
}

This configuration automatically generates resource names based on the environment, component, and current timestamp, ensuring consistency across your infrastructure.

Frequently Asked Questions

How does enhanced input validation improve Terraform configurations?

Enhanced input validation in Terraform 1.9 improves the accuracy and reliability of your configurations by ensuring that only valid inputs are accepted. This reduces the risk of deployment errors and simplifies troubleshooting by providing clear and specific error messages.

Can I use the new string template function in previous Terraform versions?

No, the new string template function introduced in Terraform 1.9 is not available in earlier versions. To take advantage of this feature, you will need to upgrade to Terraform 1.9.

How do I upgrade to Terraform 1.9?

To upgrade to Terraform 1.9, you can follow the official Terraform upgrade guide. Ensure that you test your configurations in a staging environment before deploying them to production.

What are the benefits of using string templates in Terraform?

String templates in Terraform allow you to create dynamic, reusable configurations. They enable you to embed expressions within strings, which can be used to generate resource names, tags, and other configuration elements based on variable inputs.

Conclusion

Terraform 1.9 features a significant release that enhances the flexibility and robustness of infrastructure as code. The improved input validation and new string template function provide powerful tools for ensuring that your configurations are both accurate and maintainable. Thank you for reading the DevopsRoles page!

By incorporating these features into your projects, you can streamline your deployment processes, reduce errors, and maintain consistent, high-quality infrastructure across your environments. Whether you’re just starting with Terraform or are a seasoned user, the enhancements in Terraform 1.9 are sure to improve your workflow and infrastructure management. Thank you for reading the DevopsRoles page!

Creating a Terraform variable file from an Excel

Introduction

How to Create a Terraform variable file from an Excel. In the world of infrastructure as code (IaC), Terraform stands out as a powerful tool for provisioning and managing infrastructure resources. Often, managing variables for your Terraform scripts can become challenging, especially when dealing with a large number of variables or when collaborating with others.

This blog post will guide you through the process of creating a Terraform variable file from an Excel spreadsheet using Python. By automating this process, you can streamline your infrastructure management workflow and improve collaboration.

Prerequisites

Before we begin, make sure you have the following installed:

Steps to Create a Terraform Variable File from Excel

  • Step 1: Excel Setup
  • Step 2: Python Script to create Terraform variable file from an Excel
  • Step 3: Execute the Script

Step 1: Excel Setup

Start by organizing your variables in an Excel spreadsheet. Create columns for variable names, descriptions, default values, setting value, and any other relevant information.

Setting_value and Variable_name columns will be written to the output file.

In the lab, I only created a sample Excel file for the Terraform VPC variable

Folder structure

  • env.xlsx: Excel file

Step 2: Python Script to create Terraform variable file from an Excel

Write a Python script to read the Excel spreadsheet and generate a Terraform variable file (e.g., terraform2.tfvars).

import pandas as pd
from pathlib import Path
import traceback
from lib.header import get_header

parent = Path(__file__).resolve().parent

# Specify the path to your Excel file
excel_file_path = 'env.xlsx'
var_file_name = 'terraform2.tfvars'

def main():
    try:
        env = get_header()
        sheet_name = env["SHEET_NAME"]

        # Read all sheets into a dictionary of DataFrames
        excel_data = pd.read_excel(parent.joinpath(excel_file_path),sheet_name=None, header=6, dtype=str)
        
        # Access data from a specific sheet
        extracted_data = excel_data[sheet_name]
        col_map = {
            "setting_value": env["SETTING_VALUE"],
            "variable_name": env["VARIABLE_NAME"],
            "auto_gen": env["AUTO_GEN"]
        }
        sheet_data = extracted_data[[col_map[key] for key in col_map if key in col_map]]
        sheet_name_ft = sheet_data.query('Auto_gen == "○"')

        # Display the data from the selected sheet
        print(f"\nData from [{sheet_name}] sheet:\n{sheet_name_ft}")

        # Open and clear content of file
        with open(f"{var_file_name}", "w", encoding="utf-8") as file:
            print(f"{var_file_name} create finish")

        # Write content of excel file to file
        for index, row in sheet_name_ft.iterrows():
            with open(f"{var_file_name}", "a", encoding="utf-8") as file:
                file.write(row['Variable_name'] + ' = ' + '"' + row['Setting_value'] + '"' + '\n')
        print(f"{var_file_name} write finish")
        
    except Exception:
        print(f"Error:")
        traceback.print_exc()

if __name__ == "__main__":
    main()
 

You can change the input Excel file name and output file name at these variables

excel_file_path = 'env.xlsx' 
var_file_name = 'terraform2.tfvars'

Depending on the contents of your Excel file, you can change the variables in the header.py file below

import os

def get_header():
    # Description
    os.environ["DESCRIPTION"] = os.environ.get("DESCRIPTION", "Description")
    # Description
    os.environ["DATA_TYPE"] = os.environ.get("DATA_TYPE", "Data_type")
    # setting value
    os.environ["SETTING_VALUE"] = os.environ.get("SETTING_VALUE", "Setting_value")
    # variablename
    os.environ["VARIABLE_NAME"] = os.environ.get("VARIABLE_NAME", "Variable_name")
    # genaration
    os.environ["AUTO_GEN"] = os.environ.get("AUTO_GEN", "Auto_gen")
    # variable file name location
    os.environ["FILE_NAME_LOCATION"] = os.environ.get("FILE_NAME_LOCATION", "4")

    return os.environ

Step 3: Execute the Script

python3 excel/main.py 

Output

Conclusion

By following these steps, you’ve automated the process of creating a Terraform variable file from an Excel spreadsheet. This not only saves time but also enhances collaboration by providing a standardized way to manage and document your Terraform variables.

Feel free to customize the script based on your specific needs and scale it for more complex variable structures. Thank you for reading the DevopsRoles page!

Terraform deploy cluster web servers in ASG with ELB

#Introduction

In this tutorial, How to deploy cluster web servers in Auto Scaling Group with ELB use Terraform.

What does Elastic Load Balancer mean?

Elastic Load Balancer allows balancing the load across the nodes ASG cluster.ELB also helps to manage SSL cert if your project requires HTTPS access.

Three types of ELB: Classic Load Balancer, Network Load Balancer, and Application Load Balancer.

Auto Scaling Group: allow us to scale up and scaling down the resources based on usage.

Auto Scaling Policy: the key feature of Auto Scaling Group is to scale up or scale down resources based on Auto Scaling Policy we attach.

  • AWS auto scaling Group: Min = 2, Max = 10 and desired_capacity =3
  • User user_data and create a script to install Nginx webserver on amazon linux 2.
  • Auto Scaling Group: Scaling Policy – Target Tracking policy
  • Security group ingress rule to allow access web server from my laptop ? and ELB security group.
  • Elastic load balancer
  • Elastic load balancer security group: ingress rule to allow access web server from my laptop ?

Structure folder and files

Created Cluster_WebServer_ASG_ELB folder contains files as below:

asg_config.tf
auto_scale_group.tf
output.tf
provider.tf
securitygroups.tf
variables.tf
elastic_load_balancer.tf
elb_security_group.tf

On AWS

we created key pair terraform-demo as the picture below

Deploy cluster web servers in ASG with ELB

Create a new file asg_config.tf with the content as below

resource aws_launch_configuration "my_config" {
image_id = var.ami
instance_type = var.instance_type
security_groups=["${aws_security_group.web_sg.id}"]
key_name = "terraform-demo"
 user_data = <<EOF
#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
sudo yum update -y
sudo amazon-linux-extras install nginx1 -y
sudo su -c "/bin/echo 'My Site: DevopsRoles.com' >/usr/share/nginx/html/index.html"
instance_ip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sudo su -c "echo $instance_ip >>/usr/share/nginx/html/index.html"
sudo systemctl start nginx
sudo systemctl enable  nginx
EOF
lifecycle {
create_before_destroy = true
}
}

Create a new file auto_scale_group.tf with the content as below

resource "aws_autoscaling_group" "first_asg" {
	launch_configuration = aws_launch_configuration.my_config.id
	availability_zones = "${var.azs}"
    
	min_size = 2
	max_size = 10
	desired_capacity = 3
	tag {
		key = "Name"
		value = "terraform-asg"
		propagate_at_launch = true  
	}
}

New file elastic_load_balancer.tf with the content as below

resource "aws_elb" "first_elb" {
    name = "terraform-elb"
    availability_zones = var.azs
    security_groups=[ aws_security_group.elb_sg.id ]
    listener {
        lb_port=80
        lb_protocol ="http"
        instance_port = var.server_port
        instance_protocol= "http"
    }
    health_check {
        healthy_threshold = 2
        unhealthy_threshold = 2
        timeout=3
        interval = 30
        target = "HTTP:${var.server_port}/"
    }
}

Create a new file elb_security_group.tf with the content as below

resource "aws_security_group" "elb_sg" { 

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }

egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }    
}

Create a new file output.tf with the content as below

output "elb_endpoint" {
    value = [ "${aws_elb.first_elb.arn}"]
}

provider.tf file

provider "aws" {
        region = var.region
}

securitygroups.tf file

resource "aws_security_group" "web_sg" { 

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }

    ingress {
        from_port = var.ssh_port
        to_port = var.ssh_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        security_groups = [ aws_security_group.elb_sg.id ]
    } 
       
egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }    
}

variables.tf file

variable "region" {
	description = " Define the AWS region "
	default = "us-west-2"
}
variable "server_port" {
	description = "http service listen"
	default = "80"
}

variable "ssh_port" {
	description = "ssh to server"
	default = "22"
}
variable "instance_type" { 
	description = "AWS ec2 instance type"
	default="t2.micro"
}
variable "my_public_ip" {
	description = "My laptop public IP ..." 
        default = "116.110.26.150/32"
}
variable "ami" {
    description = "amazon machine image"
        default = "ami-0c2d06d50ce30b442"
}

variable "azs" {
    default = [ "us-west-2a", "us-west-2b", "us-west-2c" ]
}

First, we run below to initialize, download the plugins and validate the terraform syntax…

terraform init
terraform validate

Applying a template

$ terraform apply

Conclusion

You have to deploy cluster web servers in ASG with ELB use Terraform. I hope will this your helpful. Thank you for reading the DevopsRoles page!

Terraform deploy cluster web servers in Auto Scaling Group

#Introduction

In this tutorial, How to deploy cluster web servers use Terraform. Now, let’s go Terraform deploy cluster web servers in Auto Scaling Group

  • AWS auto scaling Group: Min = 2, Max = 10 and desired_capacity =3
  • User user_data and create a script to install Nginx webserver on amazon linux 2.
  • Auto Scaling Group: Scaling Policy – Target Tracking policy
  • Security group ingress rule to allow access web server from my laptop ?

Structure folder and files Terraform deploy cluster web servers in Auto Scaling Group

Created Cluster_WebServer_ASG folder contains files as below:

asg_config.tf
auto_scale_group.tf
auto_scale_policy.tf
output.tf
provider.tf
securitygroups.tf
variables.tf

On AWS

we created key pair terraform-demo as the picture below

Deploy cluster web servers in Auto Scaling Group

Create a new file asg_config.tf with the content as below

resource aws_launch_configuration "my_config" {
name = "webserver-launch"
image_id = var.ami
instance_type = var.instance_type
security_groups=["${aws_security_group.web_sg.id}"]
key_name = "terraform-demo"
 user_data = <<EOF
#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
sudo yum update -y
sudo amazon-linux-extras install nginx1 -y
sudo su -c "/bin/echo 'My Site: DevopsRoles.com' >/usr/share/nginx/html/index.html"
instance_ip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sudo su -c "echo $instance_ip >>/usr/share/nginx/html/index.html"
sudo systemctl start nginx
sudo systemctl enable  nginx
EOF
}

Create a new file auto_scale_group.tf with the content as below

resource "aws_autoscaling_group" "first_asg" {
	launch_configuration = aws_launch_configuration.my_config.id
	availability_zones = "${var.azs}"
    
	min_size = 2
	max_size = 10
	desired_capacity = 3
	tag {
		key = "Name"
		value = "terraform-asg"
		propagate_at_launch = true  
	}
}

New file auto_scale_policy.tf with the content as below

resource "aws_autoscaling_policy" "my_asg_policy" {
  name = "webservers_autoscale_policy"
  policy_type = "TargetTrackingScaling"
  autoscaling_group_name = aws_autoscaling_group.first_asg.name

  target_tracking_configuration {
  predefined_metric_specification {
    predefined_metric_type = "ASGAverageCPUUtilization"
  }
  target_value = "75"
  }

}

Create new a provider.tf the content as below

provider "aws" {
        region = var.region
}

Create a new file output.tf with the content as below

output "asg_arn" {
    value = [ "${aws_autoscaling_group.first_asg.arn}"]
}

Create new file variables.tf with the content as below

variable "region" {
	description = " Define the AWS region "
	default = "us-west-2"
}
variable "server_port" {
	description = "http service listen"
	default = "80"
}

variable "ssh_port" {
	description = "ssh to server"
	default = "22"
}
variable "instance_type" { 
	description = "AWS ec2 instance type"
	default="t2.micro"
}
variable "my_public_ip" {
	description = "My laptop public IP ..." 
        default = "116.110.26.150/32"
}
variable "ami" {
    description = "amazon machine image"
        default = "ami-0c2d06d50ce30b442"
}

variable "azs" {
    default = [ "us-west-2a", "us-west-2b", "us-west-2c" ]
}

new file securitygroups.tf with the content as below

resource "aws_security_group" "web_sg" { 

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }

    ingress {
        from_port = var.ssh_port
        to_port = var.ssh_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }
egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }    
}

First, we run below to initialize, download the plugins and validate the terraform syntax…

terraform init
terraform validate

The output terminal is as follows

Applying a template

$ terraform apply

The output terminal is as below

C:\Users\HuuPV\Desktop\Terraform\Cluster_WebServer_ASG>terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_autoscaling_group.first_asg will be created
  + resource "aws_autoscaling_group" "first_asg" {
      + arn                       = (known after apply)
      + availability_zones        = [
          + "us-west-2a",
          + "us-west-2b",
          + "us-west-2c",
        ]
      + default_cooldown          = (known after apply)
      + desired_capacity          = 3
      + force_delete              = false
      + force_delete_warm_pool    = false
      + health_check_grace_period = 300
      + health_check_type         = (known after apply)
      + id                        = (known after apply)
      + launch_configuration      = (known after apply)
      + max_size                  = 10
      + metrics_granularity       = "1Minute"
      + min_size                  = 2
      + name                      = (known after apply)
      + name_prefix               = (known after apply)
      + protect_from_scale_in     = false
      + service_linked_role_arn   = (known after apply)
      + vpc_zone_identifier       = (known after apply)
      + wait_for_capacity_timeout = "10m"

      + tag {
          + key                 = "Name"
          + propagate_at_launch = true
          + value               = "terraform-asg"
        }
    }

  # aws_autoscaling_policy.my_asg_policy will be created
  + resource "aws_autoscaling_policy" "my_asg_policy" {
      + arn                     = (known after apply)
      + autoscaling_group_name  = (known after apply)
      + id                      = (known after apply)
      + metric_aggregation_type = (known after apply)
      + name                    = "webservers_autoscale_policy"
      + policy_type             = "TargetTrackingScaling"

      + target_tracking_configuration {
          + disable_scale_in = false
          + target_value     = 60

          + predefined_metric_specification {
              + predefined_metric_type = "ASGAverageCPUUtilization"
            }
        }
    }

  # aws_launch_configuration.my_config will be created
  + resource "aws_launch_configuration" "my_config" {
      + arn                         = (known after apply)
      + associate_public_ip_address = false
      + ebs_optimized               = (known after apply)
      + enable_monitoring           = true
      + id                          = (known after apply)
      + image_id                    = "ami-0c2d06d50ce30b442"
      + instance_type               = "t2.micro"
      + key_name                    = "terraform-demo"
      + name                        = "webserver-launch"
      + name_prefix                 = (known after apply)
      + security_groups             = (known after apply)
      + user_data                   = "e210837ad2017cf0971bc0ed4af86edab9d8a10d"

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + no_device             = (known after apply)
          + snapshot_id           = (known after apply)
          + throughput            = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + throughput            = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_security_group.web_sg will be created
  + resource "aws_security_group" "web_sg" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
        ]
      + name                   = (known after apply)
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + asg_arn = [
      + (known after apply),
    ]

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.web_sg: Creating...
aws_security_group.web_sg: Creation complete after 8s [id=sg-083d582a5691c56d9]
aws_launch_configuration.my_config: Creating...
aws_launch_configuration.my_config: Creation complete after 2s [id=webserver-launch]
aws_autoscaling_group.first_asg: Creating...
aws_autoscaling_group.first_asg: Still creating... [10s elapsed]
aws_autoscaling_group.first_asg: Still creating... [21s elapsed]
aws_autoscaling_group.first_asg: Still creating... [31s elapsed]
aws_autoscaling_group.first_asg: Still creating... [41s elapsed]
aws_autoscaling_group.first_asg: Creation complete after 45s [id=terraform-20211010125900499300000002]
aws_autoscaling_policy.my_asg_policy: Creating...
aws_autoscaling_policy.my_asg_policy: Creation complete after 2s [id=webservers_autoscale_policy]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

asg_arn = [
  "arn:aws:autoscaling:us-west-2:633602660500:autoScalingGroup:2b023a9d-a66c-464e-9cb0-80d9eef00e33:autoScalingGroupName/terraform-20211010125900499300000002",
]

Result on EC2 AWS

3 Instance EC2

Auto Scaling: Launch configurations

Auto Scaling groups

Conclusion

You have to deploy cluster web servers in the Auto Scaling Group. I hope will this your helpful. Thank you for reading the DevopsRoles page!

Terraform deploy cluster web servers

#Introduction

In this tutorial, How to deploy cluster web servers use Terraform. For example, I will use Terraform deploy cluster web servers.

  • Create EC2 instance
  • Terraform parameter Count: I will create three EC2 instance use Count parameter
  • User user_data and create a script to install Nginx webserver on amazon linux 2.
  • Security group ingress rule to allow access web server from my laptop ?

Structure folder and files of Terraform deploy cluster web servers

Created Cluster-WebServer folder contains files as below:

main.tf
output.tf
provider.tf
securitygroups.tf
variables.tf

Deploy cluster Web Servers with Terraform

Create new file main.tf with the content as below

resource "aws_instance" "devopsroles-lab01" {
 count = 3
 ami = var.ami
 instance_type = var.instance_type
 vpc_security_group_ids = ["${aws_security_group.webserver_security_group.id}"]
 tags = {
	 Name = "DevopsRoles-${count.index}"
 }
 key_name = "terraform-demo"
 user_data = <<EOF
#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
sudo yum update -y
sudo amazon-linux-extras install nginx1 -y
sudo su -c "/bin/echo 'My Site: DevopsRoles.com' >/usr/share/nginx/html/index.html"
instance_ip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sudo su -c "echo $instance_ip >>/usr/share/nginx/html/index.html"
sudo systemctl start nginx
sudo systemctl enable  nginx
EOF
}

On AWS, we created key pair terraform-demo as the picture below

Create a new file provider.tf with the content as below

provider "aws" {
        region = var.region
}

New file securitygroups.tf with the content as below

resource "aws_security_group" "webserver_security_group" { 

    ingress {
        from_port = var.server_port
        to_port = var.server_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }

    ingress {
        from_port = var.ssh_port
        to_port = var.ssh_port
        protocol = "tcp"
        cidr_blocks = [ var.my_public_ip ]
    }
egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }    
}

Create a new file output.tf with the content as below

output "public_ip" {
    value = [ "${aws_instance.devopsroles-lab01.*.public_ip}"]
}

Create new file variables.tf with the content as below

variable "region" {
	description = " Define the AWS region "
	default = "us-west-2"
}
variable "server_port" {
	description = "http service listen"
	default = "80"
}

variable "ssh_port" {
	description = "ssh to server"
	default = "22"
}
variable "instance_type" { 
	description = "AWS ec2 instance type"
	default="t2.micro"
}
variable "my_public_ip" {
	description = "My laptop public IP ..." 
        default = "116.110.26.150/32"
}
variable "ami" {
description = "amazon machine image"
default = "ami-0c2d06d50ce30b442"
}

variable "azs" {
default = [ "us-east-2a", "us-east-2b", "us-east-2c"]
}

First, we run below to initialize, download the plugins and validate the terraform syntax…

terraform init
terraform validate

The output terminal is as follows

Applying a template

$ terraform apply

The output terminal is as below

C:\Users\HuuPV\Desktop\Terraform\Cluster_WebServer>set AWS_PROFILE=devopsroles-demo
C:\Users\HuuPV\Desktop\Terraform\Cluster_WebServer>terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.devopsroles-lab01[0] will be created
  + resource "aws_instance" "devopsroles-lab01" {
      + ami                                  = "ami-0c2d06d50ce30b442"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "terraform-demo"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "DevopsRoles-0"
        }
      + tags_all                             = {
          + "Name" = "DevopsRoles-0"
        }
      + tenancy                              = (known after apply)
      + user_data                            = "e210837ad2017cf0971bc0ed4af86edab9d8a10d"
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_instance.devopsroles-lab01[1] will be created
  + resource "aws_instance" "devopsroles-lab01" {
      + ami                                  = "ami-0c2d06d50ce30b442"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "terraform-demo"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "DevopsRoles-1"
        }
      + tags_all                             = {
          + "Name" = "DevopsRoles-1"
        }
      + tenancy                              = (known after apply)
      + user_data                            = "e210837ad2017cf0971bc0ed4af86edab9d8a10d"
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_instance.devopsroles-lab01[2] will be created
  + resource "aws_instance" "devopsroles-lab01" {
      + ami                                  = "ami-0c2d06d50ce30b442"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "terraform-demo"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "DevopsRoles-2"
        }
      + tags_all                             = {
          + "Name" = "DevopsRoles-2"
        }
      + tenancy                              = (known after apply)
      + user_data                            = "e210837ad2017cf0971bc0ed4af86edab9d8a10d"
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_security_group.webserver_security_group will be created
  + resource "aws_security_group" "webserver_security_group" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
        ]
      + name                   = (known after apply)
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + public_ip = [
      + [
          + (known after apply),
          + (known after apply),
          + (known after apply),
        ],
    ]

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.webserver_security_group: Creating...
aws_security_group.webserver_security_group: Creation complete after 8s [id=sg-05fe82255b21a2c8b]
aws_instance.devopsroles-lab01[1]: Creating...
aws_instance.devopsroles-lab01[0]: Creating...
aws_instance.devopsroles-lab01[2]: Creating...
aws_instance.devopsroles-lab01[0]: Still creating... [10s elapsed]
aws_instance.devopsroles-lab01[2]: Still creating... [10s elapsed]
aws_instance.devopsroles-lab01[1]: Still creating... [10s elapsed]
aws_instance.devopsroles-lab01[2]: Still creating... [20s elapsed]
aws_instance.devopsroles-lab01[0]: Still creating... [20s elapsed]
aws_instance.devopsroles-lab01[1]: Still creating... [20s elapsed]
aws_instance.devopsroles-lab01[1]: Still creating... [30s elapsed]
aws_instance.devopsroles-lab01[2]: Still creating... [30s elapsed]
aws_instance.devopsroles-lab01[0]: Still creating... [30s elapsed]
aws_instance.devopsroles-lab01[0]: Still creating... [40s elapsed]
aws_instance.devopsroles-lab01[1]: Still creating... [40s elapsed]
aws_instance.devopsroles-lab01[2]: Still creating... [40s elapsed]
aws_instance.devopsroles-lab01[0]: Creation complete after 42s [id=i-0ade24acdfd72944c]
aws_instance.devopsroles-lab01[1]: Creation complete after 42s [id=i-00daeeee26f9dcd20]
aws_instance.devopsroles-lab01[2]: Creation complete after 43s [id=i-0b77263afa926a374]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

public_ip = [
  [
    "54.189.128.5",
    "52.27.217.183",
    "54.201.82.158",
  ],
]

C:\Users\HuuPV\Desktop\Terraform\Cluster_WebServer>

The result, on EC2 AWS

Open Browser, type http://IP_Public_EC2 as below

Webserver 1

Webserver 2

Webserver 3

Conclusion

You have use Terraform deploy cluster web servers. I hope will this your helpful. Thank you for reading the DevopsRoles page!

Deploy a Web Server with Terraform

#Introduction

In this tutorial, How to deploy a Web Server with Terraform.

  • Create EC2 instance
  • User user_data and create a script to install Nginx webserver on amazon linux 2.
  • Security group ingress rule to allow access web server from my laptop ?

Structure folder and files deploy a Web Server with Terraform

Created Single-WebServer folder contains files as below:

main.tf
output.tf
provider.tf
securitygroups.tf

Deploy a Web Server with Terraform

Create new file main.tf with the content as below

resource "aws_instance" "devopsroles-lab01" {
 ami = "ami-0c2d06d50ce30b442"
 instance_type = "t2.micro"
 vpc_security_group_ids = ["${aws_security_group.webserver_security_group.id}"]
 tags = {
	 Name = "DevopsRoles-Webserver"
 }
 key_name = "terraform-demo"
 user_data = <<EOF
#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
sudo yum update -y
sudo amazon-linux-extras install nginx1 -y
sudo su -c "/bin/echo 'My Site: DevopsRoles.com' >/usr/share/nginx/html/index.html"
instance_ip=`curl http://169.254.169.254/latest/meta-data/local-ipv4`
sudo su -c "echo $instance_ip >>/usr/share/nginx/html/index.html"
sudo systemctl start nginx
sudo systemctl enable  nginx
EOF
}

On AWS, we created key pair terraform-demo as the picture below

Create a new file provider.tf with the content as below

provider "aws" {
	region = "us-west-2"
}

New file securitygroups.tf with the content as below

resource "aws_security_group" "webserver_security_group" { 

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = [ "116.110.26.150/32"]
    }
    ingress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = [ "116.110.26.150/32"]
    }
    
egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }    
}

Create a new file output.tf with the content as below

output "public_ip" {
    value = "${aws_instance.devopsroles-lab01.public_ip}"
}

First, we run below to initialize, download the plugins and validate the terraform syntax…

terraform init
terraform validate

The output terminal is as follows

Applying a template

$ terraform apply

The output terminal is as below

C:\Users\HuuPV\Desktop\Terraform\Single-WebServer>terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.devopsroles-lab01 will be created
  + resource "aws_instance" "devopsroles-lab01" {
      + ami                                  = "ami-0c2d06d50ce30b442"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "terraform-demo"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "DevopsRoles-Webserver"
        }
      + tags_all                             = {
          + "Name" = "DevopsRoles-Webserver"
        }
      + tenancy                              = (known after apply)
      + user_data                            = "e210837ad2017cf0971bc0ed4af86edab9d8a10d"
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_security_group.webserver_security_group will be created
  + resource "aws_security_group" "webserver_security_group" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
          + {
              + cidr_blocks      = [
                  + "116.110.26.150/32",
                ]
              + description      = ""
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
        ]
      + name                   = (known after apply)
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + public_ip = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.webserver_security_group: Creating...
aws_security_group.webserver_security_group: Still creating... [10s elapsed]
aws_security_group.webserver_security_group: Creation complete after 15s [id=sg-08ad09dbfd038f567]
aws_instance.devopsroles-lab01: Creating...
aws_instance.devopsroles-lab01: Still creating... [10s elapsed]
aws_instance.devopsroles-lab01: Still creating... [20s elapsed]
aws_instance.devopsroles-lab01: Still creating... [30s elapsed]
aws_instance.devopsroles-lab01: Still creating... [40s elapsed]
aws_instance.devopsroles-lab01: Creation complete after 48s [id=i-085b38b93b9e04090]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "34.221.162.57"

C:\Users\HuuPV\Desktop\Terraform\Single-WebServer>

The result, on EC2 AWS

Open Browser, type http://IP_Public_EC2 as below

Conclusion

You have to Deploy a Web Server with Terraform. I hope will this your helpful. Thank you for reading the DevopsRoles page!

Terraform deploy a single server

#Introduction

In this tutorial, We will create an EC2 server. How to use Terraform deploy a single server.

Structure folder and files for Terraform deploy a single server

Created Deploy-EC2-Server folder contains files as below:

main.tf
output.tf
provider.tf
securitygroups.tf

Terraform deploy a single server

Create new file main.tf with the content as below

resource "aws_instance" "devopsroles-lab01" {

 ami = "ami-0c2d06d50ce30b442" 
 instance_type = "t2.micro"
 vpc_security_group_ids = ["${aws_security_group.webserver_security_group.id}"]
 key_name = "terraform-demo"
 tags = {
	 Name = "Devops Roles"
 }

}

On AWS, we created key pair terraform-demo as the picture below

Create a new file provider.tf with the content as below

provider "aws" {
	region = "us-west-2"
}

New file securitygroups.tf with the content as below

resource "aws_security_group" "webserver_security_group" { 

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = [ "116.110.26.100/32"]
    }
    
    egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }
    
}

Create a new file output.tf with the content as below

output "public_ip" {
    value = "${aws_instance.devopsroles-lab01.public_ip}"
}

First, we run below to initialize, download the plugins and validate the terraform syntax…

terraform init
terraform validate

The output terminal is as follows

Applying a template

$ terraform apply

The output terminal is as below

C:\Users\HuuPV\Desktop\Terraform\EC2>set AWS_PROFILE=devopsroles-demo

C:\Users\HuuPV\Desktop\Terraform\EC2>terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.devopsroles-lab01 will be created
  + resource "aws_instance" "devopsroles-lab01" {
      + ami                                  = "ami-0c2d06d50ce30b442"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = "terraform-demo"
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "Devops Roles"
        }
      + tags_all                             = {
          + "Name" = "Devops Roles"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_security_group.webserver_security_group will be created
  + resource "aws_security_group" "webserver_security_group" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "116.110.26.100/32",
                ]
              + description      = ""
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
        ]
      + name                   = (known after apply)
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + public_ip = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.webserver_security_group: Creating...
aws_security_group.webserver_security_group: Still creating... [10s elapsed]
aws_security_group.webserver_security_group: Creation complete after 13s [id=sg-0fc57ba0b0d8f51d8]
aws_instance.devopsroles-lab01: Creating...
aws_instance.devopsroles-lab01: Still creating... [10s elapsed]
aws_instance.devopsroles-lab01: Still creating... [20s elapsed]
aws_instance.devopsroles-lab01: Still creating... [30s elapsed]
aws_instance.devopsroles-lab01: Still creating... [40s elapsed]
aws_instance.devopsroles-lab01: Creation complete after 45s [id=i-051ce096e6d8cad8a]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "34.212.176.200"

C:\Users\HuuPV\Desktop\Terraform\EC2>

The result, on EC2 AWS

Conclusion

You have Terraform deploy a single server. I hope will this your helpful. Thank you for reading the DevopsRoles page!