Account Baseline
The Account Baseline feature in NTC Account Factory allows you to establish and maintain consistent configurations, guardrails, and security controls across multiple AWS accounts through comprehensive infrastructure as code.
Overview
Account Baselines ensure that all AWS accounts in your organization follow a consistent set of standards and configurations. Unlike the event-driven Account Lifecycle Management, baselines provide ongoing governance through CI/CD pipelines that apply Terraform code to maintain the desired state of your accounts.
How It Works
- Terraform code defines the desired configuration for accounts
- S3 buckets store the Terraform code and state files
- CodePipeline orchestrates the deployment process
- CodeBuild executes Terraform to apply the configuration
- DynamoDB tables protect against accidental deletion
Key Features
Feature | Description | Benefits |
---|---|---|
Scoped Deployment | Apply different baselines to specific accounts based on OU paths, names, or tags | Target configurations to the right environments |
Multi-Region Support | Deploy resources across multiple AWS regions | Maintain consistency across global infrastructure |
Modular Design | Build baselines from reusable templates or custom code | Reduce duplication and maintenance overhead |
Cross-Account Orchestration | Configure resources that require access to multiple accounts | Simplify complex cross-account dependencies |
Scheduled Updates | Automatically reapply baselines at scheduled intervals | Enforce compliance and correct drift |
State Management | Maintain Terraform state to detect and correct configuration drift | Ensure resources stay properly configured |
NTC Account Baseline Templates
The NTC Account Baseline Templates module provides a set of pre-built, reusable templates for common account configurations. These templates can be easily integrated into your Account Factory configuration to standardize governance across accounts.
Available Templates
The following templates are available in the NTC Account Baseline Templates module:
Template | Description | When to Use |
---|---|---|
iam_role | Creates a standardized IAM role with configurable trust policy, principal access, and permission settings | When you need consistent IAM roles across accounts for cross-account access, service roles, or EC2 instance profiles |
aws_config | Sets up AWS Config recorders and rules | For continuous compliance monitoring |
openid_connect | Configures OIDC integration | For setting up an OpenID Connect (OIDC) identity provider in IAM with assumable role and permissions |
Implementation
The NTC Account Baseline Templates can be implemented in your NTC Account Factory configuration through a few simple steps:
- Include the Templates Module
First, include the NTC Account Baseline Templates module in your Terraform configuration:
module "account_baseline_templates" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-account-baseline-templates?ref=X.X.X"
account_baseline_templates = [
{
file_name = "iam_monitoring_reader"
template_name = "iam_role"
iam_role_inputs = {
role_name = "CloudWatch-CrossAccountSharingRole"
# policy can be submitted directly as JSON or via data source aws_iam_policy_document
policy_json = data.aws_iam_policy_document.monitoring_reader.json
role_principal_type = "AWS"
# grant account (org management) permission to assume role in member account
role_principal_identifiers = [123456789102] # monitoring account id
}
},
{
file_name = "iam_instance_profile"
template_name = "iam_role"
iam_role_inputs = {
role_name = "ntc-ssm-instance-profile"
# use 'policy_arn' to reference an aws managed policy
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
role_principal_type = "Service"
# grant account (org management) permission to assume role in member account
role_principal_identifiers = ["ec2.amazonaws.com"]
# (optional) set to true to create an instance profile
role_is_instance_profile = true
}
},
{
file_name = "aws_config"
template_name = "aws_config"
aws_config_inputs = {
config_log_archive_bucket_arn = "CONFIG_LOG_ARCHIVE_BUCKET_ARN"
config_log_archive_kms_key_arn = "CONFIG_LOG_ARCHIVE_KMS_KEY_ARN"
# optional inputs
config_recorder_name = "ntc-config-recorder"
config_delivery_channel_name = "ntc-config-delivery"
config_iam_role_name = "ntc-config-role"
config_iam_path = "/"
config_delivery_frequency = "One_Hour"
# (optional) override account baseline main region with main region of security tooling
# this is necessary when security tooling uses a different main region
# omit to use the main region of the account baseline
config_security_main_region = ""
}
},
{
file_name = "oidc_spacelift"
template_name = "openid_connect"
openid_connect_inputs = {
provider = "example.app.spacelift.io"
audience = "example.app.spacelift.io"
role_name = "ntc-oidc-spacelift-role"
role_path = "/"
role_max_session_in_hours = 1
permission_boundary_arn = ""
permission_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
subject_list_encoded = <<EOT
flatten([
[
"space:REPLACE_ME_WITH_SPACELIFT_SPACE_ID:stack:$${var.current_account_name}:*"
],
[
for subject in try(var.current_account_customer_values.additional_oidc_subjects, []) : "space:REPLACE_ME_WITH_SPACELIFT_SPACE_ID:stack:$${subject}:*"
]
])
EOT
}
}
]
}
- Reference Templates in Account Factory Configuration
After defining the templates, reference them in your NTC Account Factory configuration:
module "ntc_account_factory" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-account-factory?ref=X.X.X"
account_factory_baseline_bucket_name = "ntc-account-factory-baseline"
# Other account factory configuration...
account_baseline_scopes = [
{
scope_name = "workloads"
terraform_binary = "opentofu"
terraform_version = "1.8.5"
aws_provider_version = "5.76.0"
provider_default_tags = {
ManagedBy = "ntc-account-factory",
BaselineScope = "workloads",
BaselineVersion = "1.3"
}
baseline_execution_role_name = "OrganizationAccountAccessRole"
# Reference pre-defined templates
baseline_terraform_files = [
module.account_baseline_templates.account_baseline_terraform_files["iam_monitoring_reader"],
module.account_baseline_templates.account_baseline_terraform_files["iam_instance_profile"],
module.account_baseline_templates.account_baseline_terraform_files["aws_config"],
]
baseline_regions = ["us-east-1", "eu-central-1"]
baseline_main_region = "eu-central-1"
# Target specific accounts
include_accounts_all = false
include_accounts_by_ou_paths = [
"/root/workloads/prod",
"/root/workloads/dev"
]
}
]
}
- Testing and Monitoring
Once your baseline configuration is deployed, you can monitor its status through:
- AWS CodePipeline console to track deployment
- CodeBuild logs for detailed execution information
- Account-level resources to verify proper configuration
Custom Baseline Templates
Injected Variables
The NTC Account Factory automatically injects several predefined variables into your baseline Terraform templates, providing essential account and deployment context information. These variables are automatically injected by the NTC Account Factory CodePipeline and are available in all baseline templates without any additional configuration.
Variable | Type | Description |
---|---|---|
var.current_region | string | The AWS region where the baseline is currently being deployed |
var.main_region | string | The primary region designated for the account baseline |
var.is_current_region_main_region | bool | Boolean flag indicating whether the current deployment region is the main region |
var.current_account_id | string | The AWS account ID where the baseline is being applied |
var.current_account_name | string | The name of the AWS account |
var.current_account_email | string | The email address associated with the AWS account |
var.current_account_ou_path | string | The organizational unit path where the account is located |
var.current_account_tags | map | Key-value pairs of tags assigned to the account |
var.current_account_alternate_contacts | list | List of alternate contact information for the account |
var.current_account_customer_values | any | Custom values provided during account creation or configuration |
var.baseline_parameters | any | Configuration parameters specific to the baseline scope |
While the pre-built templates cover many common scenarios, you may need to create custom baseline templates for unique requirements. The injected variables provide powerful capabilities for creating dynamic, account-aware configurations that adapt based on the deployment context.
Leveraging Injected Variables in Custom Templates
The injected variables enable you to create sophisticated baseline templates that automatically adapt to different accounts, regions, and organizational contexts:
# Example: Account-aware resource naming
resource "aws_s3_bucket" "logs" {
bucket = "${var.current_account_id}-${var.current_region}-audit-logs"
tags = merge(var.current_account_tags, {
Purpose = "Audit Logging"
Region = var.current_region
})
}
# Example: Region-specific deployments
resource "aws_iam_role" "cross_account_role" {
# Only create in main region since IAM is global
count = var.is_current_region_main_region ? 1 : 0
name = "${var.current_account_name}-CrossAccountRole"
# ... role configuration
}
# Example: Environment-based configurations
locals {
environment = can(regex("/prod", var.current_account_ou_path)) ? "production" : "non-production"
# Different settings based on environment
backup_retention_days = local.environment == "production" ? 30 : 7
monitoring_enabled = local.environment == "production" ? true : false
# Use customer values for additional customization
custom_budget_limit = try(var.current_account_customer_values.budget_limit, "100")
}
Simple Account Budget Template
For straightforward budget management, here's a simple template that uses injected variables for basic account-aware budgeting:
# files/simple_account_budget.tf
resource "aws_budgets_budget" "account_budget" {
# Only create in main region to avoid duplicates
count = var.is_current_region_main_region ? 1 : 0
name = "${var.current_account_name}-monthly-budget"
budget_type = "COST"
limit_amount = var.baseline_parameters["budget_limit"]
limit_unit = "USD"
time_unit = "MONTHLY"
time_period_start = "2024-01-01_00:00"
# 80% threshold notification
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = [var.current_account_email]
}
# 100% threshold notification
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = [var.current_account_email]
}
tags = merge(var.current_account_tags, {
ManagedBy = "ntc-account-factory"
Purpose = "cost-monitoring"
})
}
This simple template:
- Uses
var.current_account_name
for unique budget naming - Gets budget limit from
var.baseline_parameters["budget_limit"]
- Sends notifications to
var.current_account_email
- Only deploys in the main region using
var.is_current_region_main_region
- Merges account tags using
var.current_account_tags
You can configure different budget limits per scope in your Account Factory configuration:
account_baseline_scopes = [
{
scope_name = "production_accounts"
baseline_parameters = {
budget_limit = "1000"
}
baseline_terraform_files = [
{
file_name = "simple_account_budget.tf"
content = file("${path.module}/files/simple_account_budget.tf")
}
]
include_accounts_by_ou_paths = ["/root/workloads/prod"]
},
{
scope_name = "development_accounts"
baseline_parameters = {
budget_limit = "200"
}
baseline_terraform_files = [
{
file_name = "simple_account_budget.tf"
content = file("${path.module}/files/simple_account_budget.tf")
}
]
include_accounts_by_ou_paths = ["/root/workloads/dev"]
}
]
Account Budget Template with Dynamic Configuration
Here's an enhanced budget template that leverages injected variables for dynamic configuration based on account context:
-
Create the Enhanced Terraform File
# files/account_budget_dynamic.tf
locals {
# Determine environment from OU path
environment = can(regex("/prod", var.current_account_ou_path)) ? "production" : (
can(regex("/dev", var.current_account_ou_path)) ? "development" : "sandbox"
)
# Set budget limits based on environment and custom values
default_budget_limits = {
production = "1000"
development = "200"
sandbox = "50"
}
# Allow override via customer values, fallback to environment defaults
budget_limit = try(
var.current_account_customer_values.budget_limit,
local.default_budget_limits[local.environment],
"100"
)
# Environment-specific notification settings
notification_emails = {
production = ["finance-prod@company.com", "team-lead@company.com"]
development = ["finance-dev@company.com"]
sandbox = ["admin@company.com"]
}
# Use account email as fallback if environment-specific emails not defined
budget_emails = try(
var.current_account_customer_values.budget_notification_emails,
local.notification_emails[local.environment],
[var.current_account_email]
)
}
resource "aws_budgets_budget" "account_budget" {
# Only create in main region to avoid duplicates
count = var.is_current_region_main_region ? 1 : 0
name = "${var.current_account_name}-monthly-budget"
budget_type = "COST"
limit_amount = local.budget_limit
limit_unit = "USD"
time_unit = "MONTHLY"
time_period_start = "2024-01-01_00:00"
# 80% threshold notification
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = local.budget_emails
}
# 100% threshold notification
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = local.budget_emails
}
# Forecasted 100% threshold
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = local.budget_emails
}
tags = merge(var.current_account_tags, {
ManagedBy = "ntc-account-factory"
Environment = local.environment
BudgetLimit = local.budget_limit
})
}
# Optional: Create CloudWatch alarm for budget exceeded
resource "aws_cloudwatch_metric_alarm" "budget_alarm" {
count = var.is_current_region_main_region ? 1 : 0
alarm_name = "${var.current_account_name}-budget-exceeded"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "ActualSpend"
namespace = "AWS/Billing"
period = "86400"
statistic = "Maximum"
threshold = local.budget_limit
alarm_description = "Budget exceeded for account ${var.current_account_name}"
dimensions = {
Currency = "USD"
}
tags = var.current_account_tags
} -
Use in Account Factory Configuration
account_baseline_scopes = [
{
scope_name = "all_workloads"
baseline_terraform_files = [
{
file_name = "account_budget_dynamic.tf"
content = file("${path.module}/files/account_budget_dynamic.tf")
}
]
# Apply to all workload accounts
include_accounts_by_ou_paths = [
"/root/workloads/prod",
"/root/workloads/dev",
"/root/workloads/sandbox"
]
baseline_regions = ["us-east-1", "eu-central-1"]
baseline_main_region = "us-east-1"
}
]
Multi-Region Resource Template
This template demonstrates how to use injected variables for region-aware deployments:
# files/multi_region_resources.tf
# Global resources (created only in main region)
resource "aws_iam_role" "application_role" {
count = var.is_current_region_main_region ? 1 : 0
name = "${var.current_account_name}-ApplicationRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = merge(var.current_account_tags, {
CreatedIn = var.main_region
})
}
# Regional resources (created in each region)
resource "aws_s3_bucket" "regional_bucket" {
bucket = "${var.current_account_id}-${var.current_region}-regional-data"
tags = merge(var.current_account_tags, {
Region = var.current_region
IsMainRegion = var.is_current_region_main_region
})
}
# Cross-region reference (data source in non-main regions)
data "aws_iam_role" "application_role" {
count = var.is_current_region_main_region ? 0 : 1
name = "${var.current_account_name}-ApplicationRole"
}
locals {
# Get role ARN whether created locally or referenced from main region
application_role_arn = var.is_current_region_main_region ?
aws_iam_role.application_role[0].arn :
data.aws_iam_role.application_role[0].arn
}
# Use the role ARN in regional resources
resource "aws_instance_profile" "application_profile" {
name = "${var.current_account_name}-${var.current_region}-profile"
role = local.application_role_arn
tags = var.current_account_tags
}
These enhanced examples demonstrate how the injected variables enable you to create dynamic, intelligent baseline templates that automatically adapt to different deployment contexts while maintaining consistency across your AWS organization.
Cross-Account Orchestration
Account Baseline supports configuring resources that require access to multiple accounts. This is particularly useful when your baseline needs to manage networking resources (like Transit Gateway attachments) or DNS delegations that are centrally managed in dedicated accounts.
baseline_assume_role_providers = [
{
configuration_alias = "connectivity"
role_arn = "REPLACE_WITH_THE_ROLE_ARN_THAT_ALLOWS_BASELINE_TO_MANAGE_NETWORKING_RESOURCES" # local.ntc_parameters["connectivity"]["baseline_assume_role_arn"]
session_name = "ntc-account-factory"
}
]
This configuration creates additional AWS provider aliases that can be used in your baseline Terraform code. For example, you might use the connectivity provider to:
- Attach VPCs to Transit Gateways: Automatically connect new account VPCs to your central networking infrastructure
- Create DNS subdomain delegations: Set up Route53 hosted zones and delegate subdomains from a central DNS account
- Configure cross-account security group rules: Allow traffic between accounts through centrally managed security groups
Setting Up Cross-Account Access
To use cross-account orchestration, you need to create a role in the target account (e.g., connectivity account) that can be assumed by the baseline pipeline:
- Create the cross-account role in the target account:
# In your connectivity account
resource "aws_iam_role" "ntc_baseline" {
name = "ntc-baseline-role"
assume_role_policy = data.aws_iam_policy_document.ntc_baseline_trust.json
}
data "aws_iam_policy_document" "ntc_baseline_trust" {
statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = ["ACCOUNT_FACTORY_BASELINE_ROLE_ARN"] # local.ntc_parameters["mgmt-account-factory"]["baseline_role_arns"]
}
actions = ["sts:AssumeRole"]
}
}
# Grant specific permissions needed for baseline operations
resource "aws_iam_role_policy" "ntc_baseline" {
name = "ntc-baseline-permissions"
role = aws_iam_role.ntc_baseline.id
policy = data.aws_iam_policy_document.ntc_baseline_permissions.json
}
data "aws_iam_policy_document" "ntc_baseline_permissions" {
# permission required to manage transit gateway attachments
statement {
sid = "ManageTransitGatewayAttachments"
effect = "Allow"
actions = [
"ec2:CreateTags",
"ec2:DescribeTransitGatewayAttachments",
"ec2:AssociateTransitGatewayRouteTable",
"ec2:EnableTransitGatewayRouteTablePropagation",
"ec2:GetTransitGatewayAttachmentPropagations",
]
resources = [
module.ntc_core_network_frankfurt.transit_gateway_arn,
module.ntc_core_network_zurich.transit_gateway_arn
]
}
# permissions required to manage subdomain delegations
statement {
sid = "ManageRoute53SubdomainDelegations"
effect = "Allow"
actions = [
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets",
"route53:ListTagsForResource",
"route53:GetHostedZone",
]
resources = ["*"] # Use "*" to allow access to all Route53 hosted zones, or specify specific ARNs if needed like module.ntc_route53_dev.zone_arn
}
statement {
effect = "Allow"
actions = [
"route53:ListHostedZones",
"route53:GetChange",
]
resources = ["*"]
}
# add additional permissions as needed
}
- Use the provider in your baseline templates:
# Example: Attach new account VPC to Transit Gateway
resource "aws_ec2_transit_gateway_vpc_attachment" "workload_attachment" {
provider = aws.connectivity
subnet_ids = [aws_subnet.workload_subnet.id]
transit_gateway_id = var.transit_gateway_id
vpc_id = aws_vpc.workload_vpc.id
tags = {
Name = "${var.current_account_name}-attachment"
}
}
# Example: Create subdomain delegation
resource "aws_route53_record" "subdomain_delegation" {
provider = aws.connectivity
zone_id = var.parent_zone_id
name = "${var.current_account_name}.example.com"
type = "NS"
ttl = 300
records = aws_route53_zone.account_zone.name_servers
}
Account Scoping
You can precisely target which accounts receive specific baselines using several scoping mechanisms:
-
Include by OU Path
include_accounts_by_ou_paths = [
"/root/workloads/prod",
"/root/workloads/dev"
] -
Include by Account Name
include_accounts_by_names = ["exclusive-account"]
-
Include by Tags
include_accounts_by_tags = [
{
key = "AccountType"
value = "workload"
}
] -
Exclusion Options
exclude_accounts_by_ou_paths = ["/root/workloads/sandbox"]
exclude_accounts_by_names = ["test-account"]
exclude_accounts_by_tags = [
{
key = "ExcludeFromBaseline"
value = "true"
}
]
Scheduled Updates
To ensure your baselines remain consistently applied, you can schedule automatic reapplication:
schedule_rerun_every_x_hours = 24 # Rerun daily
This feature is particularly useful for:
- Correcting manual changes that cause drift
- Ensuring new resources meet compliance requirements
- Applying updates to resources that might be modified by other processes
Decommissioning
When you need to remove resources created by a baseline:
decommission_accounts_all = false
decommission_accounts_by_tags = [
{
key = "AccountDecommission"
value = true
}
]
Pipeline Delay Options
To handle dependencies and ensure resources are available before applying the baseline:
pipeline_delay_options = {
wait_for_seconds = 120 # Sets a fixed delay before starting baseline deployment, useful for allowing AWS operations to propagate
wait_retry_count = 5 # Number of times to retry dependency checks before failing, combined with wait_for_seconds can wait up to 10 minutes
wait_for_execution_role = true # Ensures the IAM execution role exists and is accessible in target accounts before proceeding
wait_for_regions = false # If true, checks that all AWS regions specified in baseline_regions are enabled in the account
wait_for_securityhub = false # If true, verifies AWS Security Hub is properly configured before proceeding with the baseline
wait_for_guardduty = false # If true, checks if AWS GuardDuty is properly configured before applying the baseline
}
Differences from Account Lifecycle Management
While Account Lifecycle Management provides event-driven, reactive automation for specific moments, Account Baseline is focused on:
Account Baseline | Account Lifecycle Management |
---|---|
Deployment Method | CI/CD pipeline-based execution |
Operational Mode | Comprehensive, ongoing governance |
State Management | State-based approach that maintains and reconciles desired state |
Execution Timing | Scheduled or on-demand execution |
Account Targeting | Precision targeting based on OU paths, tags, or account names |
Typical Use Cases | Standardized security controls, compliance requirements, organizational policies |
Best Practices
- Start Simple: Begin with a few essential resources and gradually expand
- Test Thoroughly: Test baseline changes in a development environment first
- Version Control: Store baseline templates in version control
- Modular Design: Break down complex baselines into modular components
- Documentation: Document the purpose and requirements of each baseline component
- Tagging Strategy: Develop a consistent tagging strategy for resources
- Error Handling: Include proper error handling in your baseline code
- Idempotency: Ensure your Terraform code is idempotent to avoid issues with repeated applications
- Scoping: Use precise account targeting to avoid applying baselines to the wrong accounts
- Dependency Management: Consider dependencies between resources and baseline components
FAQ
How do Account Baselines differ from traditional Terraform deployments?
Account Baselines provide several advantages over traditional Terraform deployments:
- Centralized management through a single point of configuration
- Multiple account targeting without managing separate state files for each account
- Automated deployment pipelines that eliminate manual terraform apply steps
- Consistent provider configuration across all targeted accounts
- Scheduled reapplication to maintain compliance and prevent drift
- Coordinated multi-region deployments from a single baseline definition
Can I use both pre-defined and custom templates together?
Yes, you can combine pre-defined templates from the NTC Account Baseline Templates module with your custom templates in the same baseline. This allows you to leverage existing solutions for common tasks while still implementing custom logic for your specific requirements.
How do I handle secrets in my baseline templates?
For handling secrets in your baseline templates, you have several options:
- AWS Secrets Manager: Store secrets in Secrets Manager and retrieve them at runtime
- AWS Parameter Store: Use Parameter Store for configuration values, especially with SecureString parameters
- IAM Role Assumption: Use IAM roles with specific permissions rather than hardcoded credentials
- Environment Variables: Pass sensitive values as environment variables to CodeBuild jobs
Never hardcode secrets in your Terraform files. Instead, use:
data "aws_secretsmanager_secret_version" "example" {
secret_id = "arn:aws:secretsmanager:region:account:secret:name"
}
locals {
secret_value = jsondecode(data.aws_secretsmanager_secret_version.example.secret_string)
}
How do I debug issues with my baseline deployment?
When troubleshooting baseline deployment issues:
- Check the CodeBuild logs for detailed error messages
- Verify that the execution role has the necessary permissions
- Inspect the Terraform plan output for expected changes
- Check for timeouts or connectivity issues in cross-account operations
- Verify that the resources defined in your baseline are valid for all targeted regions
- Test your Terraform templates locally before adding them to your baseline
Can I use OpenTofu instead of Terraform for my baselines?
Yes, NTC Account Factory supports both Terraform and OpenTofu. To use OpenTofu:
account_baseline_scopes = [
{
scope_name = "workloads"
terraform_binary = "opentofu" # Specify OpenTofu instead of Terraform
terraform_version = "1.8.5" # Specify OpenTofu version
# ...other configuration...
}
]