How to Deploy Your Terraform Files
From download to deployed infrastructure. Follow these steps to apply the Terraform files generated by BaselineSentinel.
Printable — use Ctrl+P / Cmd+P to save as PDF.
Quick Start
Two Ways to Use This Guide
Beginner Path
Follow Steps 1 through 7 in order. Copy the commands exactly, then review Terraform plan output before apply.
Pro Path
Jump to Step 3 for auth profiles, Step 4 for structure, then run init, plan, and apply with named profiles per account.
Install Terraform
- Download and install Terraform from HashiCorp's official site.
- Follow the installer for your operating system (Windows, macOS, or Linux).
- Verify the installation by running:
terraform --version
You should see the installed version number. Any recent version (1.x+) works with BaselineSentinel files.
Install AWS CLI
- Download the AWS CLI from the official AWS guide.
- Terraform relies on AWS CLI credentials to authenticate with your AWS account.
- Verify the installation:
aws --version
Choose How Terraform Authenticates to AWS
Terraform needs AWS credentials. Choose your deployment style first, then pick the auth method.
Single Account
Use this if you deploy into one AWS account only. The cleanest path is one SSO profile or one IAM profile.
Single Account IAM
Best if you are using one IAM user or one long-lived automation credential.
aws configure --profile baseline-single
$env:AWS_PROFILE = "baseline-single" terraform plan
export AWS_PROFILE="baseline-single" terraform plan
[baseline-single] aws_access_key_id = AKIA... aws_secret_access_key = ...
Single Account SSO
Recommended if your organization already uses AWS IAM Identity Center / SSO.
aws configure sso --profile baseline-single aws sso login --profile baseline-single AWS_PROFILE=baseline-single terraform apply
[profile baseline-single] region = us-east-1 output = json # SSO example sso_start_url = https://your-org.awsapps.com/start sso_region = us-east-1 sso_account_id = 111122223333 sso_role_name = AdministratorAccess
CI/CD / automation
export AWS_ACCESS_KEY_ID=xxxx export AWS_SECRET_ACCESS_KEY=xxxx export AWS_DEFAULT_REGION=us-east-1
Recommended for ephemeral CI runners. For local machines, profiles are usually easier to maintain.
Multiple Accounts
Use this if you manage several AWS accounts. Best practice is one named profile per account and role, then choose the profile at runtime.
⚠ PowerShell vs Bash — profile syntax is different
AWS_PROFILE=MyProfile terraform plan is Bash/macOS/Linux only. It does not work in PowerShell (Windows Terminal, VS Code terminal on Windows).
In PowerShell you must set the variable first on its own line:
$env:AWS_PROFILE = "MyProfile" terraform plan
Or as a one-liner with a semicolon:
$env:AWS_PROFILE = "MyProfile"; terraform plan
Recommended path: keep BaselineSentinel-generated Terraform profile-agnostic and select the AWS profile when you run Terraform. That is simpler and more portable than hardcoding profile names into provider.tf.
Multiple Account SSO
This is the recommended enterprise flow. Define multiple SSO profiles in ~/.aws/config, authenticate once, then run Terraform against the profile you want.
[profile Account_A.AWSOrgAdmin] sso_start_url = https://your-org.awsapps.com/start sso_account_id = xxxxxxxxxxxxxxx sso_role_name = AWSOrgAdmin sso_region = us-east-1 [profile Account_B.AWSOrgAdmin] sso_start_url = https://your-org.awsapps.com/start sso_account_id = xxxxxxxxxxxxx sso_role_name = AWSOrgAdmin sso_region = us-east-1
PowerShell (Windows)
# Account A aws sso login --profile Account_A.AWSOrgAdmin $env:AWS_PROFILE = "Account_A.AWSOrgAdmin" # Optional: check currently selected profile $env:AWS_PROFILE # Optional: quick validation against AWS (returns Account, Arn, UserId) aws sts get-caller-identity terraform init terraform plan terraform apply # Account B aws sso login --profile Account_B.AWSOrgAdmin $env:AWS_PROFILE = "Account_B.AWSOrgAdmin" # Optional checks $env:AWS_PROFILE aws sts get-caller-identity terraform plan terraform apply
One-liner form: $env:AWS_PROFILE = "Account_A.AWSOrgAdmin"; terraform plan
Bash / macOS / Linux
# Account A aws sso login --profile Account_A.AWSOrgAdmin export AWS_PROFILE="Account_A.AWSOrgAdmin" # Optional: check currently selected profile echo "$AWS_PROFILE" # Optional: quick validation against AWS (returns Account, Arn, UserId) aws sts get-caller-identity terraform init terraform plan terraform apply # Account B aws sso login --profile Account_B.AWSOrgAdmin export AWS_PROFILE="Account_B.AWSOrgAdmin" # Optional checks echo "$AWS_PROFILE" aws sts get-caller-identity terraform plan terraform apply
Multiple Account IAM
Use one named IAM profile per account if SSO is not available.
[Account_A] aws_access_key_id = AKIA... aws_secret_access_key = ... [Account_B] aws_access_key_id = AKIA... aws_secret_access_key = ...
PowerShell (Windows)
# Account A $env:AWS_PROFILE = "Account_A" terraform plan terraform apply # Account B $env:AWS_PROFILE = "Account_B" terraform plan terraform apply
Bash / macOS / Linux
export AWS_PROFILE="Account_A" && terraform plan export AWS_PROFILE="Account_B" && terraform apply
CI/CD / automation
export AWS_ACCESS_KEY_ID=xxxx export AWS_SECRET_ACCESS_KEY=xxxx export AWS_DEFAULT_REGION=us-east-1
Recommended for ephemeral CI runners. For local machines, profiles are usually easier to maintain.
Profile files: Linux/macOS ~/.aws/config and ~/.aws/credentials; Windows %UserProfile%\\.aws\\config and %UserProfile%\\.aws\\credentials.
Best path forward
Do not hardcode profile names into generated provider.tf by default. If you have only one account, your default or single named profile is enough. If you have multiple accounts, run Terraform with the profile you want by setting AWS_PROFILE in your shell.
Organize Your Terraform Project
Create a folder structure that mirrors your AWS accounts and scan categories. Place the downloaded .tf files from BaselineSentinel into the appropriate folders:
terraform-project/ ├── AccountName/ │ ├── Monitoring/ │ │ ├── main.tf ← downloaded from BaselineSentinel │ │ ├── variables.tf │ │ └── outputs.tf │ └── Backups/ │ ├── main.tf ← downloaded from BaselineSentinel │ ├── variables.tf │ └── outputs.tf
Each folder is an independent Terraform workspace. You run terraform init and terraform apply inside each one separately.
Initialize & Plan
Navigate to the folder containing your downloaded .tf files, set your AWS profile, then initialize and plan.
PowerShell (Windows)
Set-Location "terraform-project\AccountName\Monitoring" $env:AWS_PROFILE = "YourProfile.AWSOrgAdmin" terraform init terraform plan
Bash / macOS / Linux
cd terraform-project/AccountName/Monitoring export AWS_PROFILE="YourProfile.AWSOrgAdmin" terraform init terraform plan
Review before applying
Terraform will show exactly what it plans to create. Review every line before applying. No resources are created at this stage.
Apply
PowerShell (Windows)
$env:AWS_PROFILE = "YourProfile.AWSOrgAdmin" terraform apply
Bash / macOS / Linux
export AWS_PROFILE="YourProfile.AWSOrgAdmin" terraform apply
Type yes to confirm. Terraform creates the resources defined in your .tf files.
Expected: Initial alert flood on first deploy
Every newly created CloudWatch alarm starts in INSUFFICIENT_DATA state. Within the first 5–10 minutes after apply, each alarm evaluates its first metric datapoints. If your resources are healthy (typical), all alarms transition INSUFFICIENT_DATA → OK simultaneously, triggering one notification per alarm through your configured channels. With many alarms this can produce a large burst of “OK” messages — this is normal and will not repeat unless you destroy and recreate the alarms.
Destroy (If Needed)
terraform destroy
Safe to use
This will only destroy resources that Terraform created within this specific workspace. It will not touch your existing AWS infrastructure, other Terraform workspaces, or anything you created manually. Your entire ecosystem is safe.
Prerequisites: Monitoring & Backup
To make Monitoring and Backup modules work correctly, your AWS resources need additional setup and permissions. This section explains what's required by each module.
📊 Monitoring Prerequisites
For CloudWatch alarms and metrics to work, your EC2 instances must have the Systems Manager (SSM) Agent installed, and the CloudWatch Agent installed and configured with a metrics JSON profile.
✓ EC2 Instance Requirements
- SSM Agent installed — Required for OS-level visibility and remote access. Available by default on Amazon Linux 2 and some Windows AMIs; install manually on Ubuntu/Debian.
- Port 443 outbound — SSM Agent communicates to AWS Systems Manager service on port 443 (HTTPS).
- IAM instance role — Attach policy
AmazonSSMManagedInstanceCore(or custom with ssm:UpdateInstanceInformation, ec2messages:*, ssmmessages:*). - CloudWatch Agent installed and configured — Installation alone is not enough; you must apply a config file (Linux/Windows JSON) and start the agent so metrics are published to
CWAgent. - CloudWatch Agent IAM policy — Attach
CloudWatchAgentServerPolicy(or equivalent custom permissions) to the EC2 instance role.
Important: Is this automatic?
No. BaselineSentinel does not automatically install CloudWatch Agent on your EC2 instances. The dashboard "Setup Guide" provides commands and JSON templates, and you must run/deploy them (manually or via SSM Run Command).
✓ Quick Check Commands
sudo systemctl status amazon-ssm-agent # or for SSM Agent process ps aux | grep amazon-ssm-agent
aws ssm describe-instance-information --region us-east-1 # Look for instances with "PingStatus": "Online"
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ -m ec2 -a status
⚠ Common Issue: "1 EC2 instance without SSM Agent"
This warning appears if an instance does not have SSM Agent running or connected. CloudWatch cannot collect OS-level metrics (Memory, Disk) without it. The monitoring dashboard shows this as a warning banner after you scan your inventory.
To fix: Install SSM Agent, then install and configure CloudWatch Agent on affected instances. See "Setup Guide" in the dashboard under Monitoring for per-instance instructions.
💾 Backup Prerequisites
AWS Backup requires proper IAM permissions and resource configuration. Setup takes 2–5 minutes for IAM propagation.
✓ For EC2 Instances & EBS Volumes
- EC2 instance role — Attach a role with backup creation permissions (or use default service role).
- Backup service role — The Terraform generated role attaches AWS managed policies
AWSBackupServiceRolePolicyForBackupandAWSBackupServiceRolePolicyForS3Backup. If using KMS-encrypted resources, ensure KMS key policy allows AWS Backup role access. - Resource availability — Instances must be in "running" or "stopped" state. Instances in "stopping" or "terminated" state cannot be backed up.
- Backup plan targeting — Backup plans use resource tags to identify targets. Ensure your resources have the tags specified in your backup plan config (e.g.,
Environment=prod).
✓ For RDS Databases
- RDS backup retention — Ensure your RDS instance has automatic backups enabled (backup retention > 0 days).
- Enhanced Monitoring IAM role — Recommended for visibility; attach
AmazonRDSEnhancedMonitoringRoleto RDS instances. - Backup vault access — RDS snapshots are stored in the backup vault created by Terraform.
✓ For S3 Buckets
💡 Why S3 backup is opt-in
S3 is designed for 99.999999999% (11 nines) durability and 99.99% availability — most businesses never need a backup of S3 itself. However, for special cases (compliance, accidental-deletion protection, critical data buckets) you can opt specific buckets in. Infrastructure buckets like access logs, Elastic Beanstalk, and ALB logs are automatically excluded.
- Tag the bucket — Only S3 buckets tagged
s3backup=trueare included in the backup plan. Add this tag to any bucket you want backed up, and remove it to opt out. - Versioning is required — AWS Backup for S3 cannot run without versioning enabled. Buckets without versioning will produce
EXPIRED/ failed jobs. - Backup service S3 role — The generated IAM role attaches
AWSBackupServiceRolePolicyForS3Backupautomatically.
Enable versioning — CLI:
aws s3api put-bucket-versioning --bucket BUCKET --versioning-configuration Status=Enabled
Enable versioning — AWS Console:
- Open the S3 console → click the bucket name.
- Go to Properties tab → scroll to Bucket Versioning.
- Click Edit → select Enable → click Save changes.
Add the opt-in tag — Console:
- In the S3 console, click the bucket → Properties tab → Tags → Edit.
- Add tag: Key =
s3backup· Value =true. - Click Save changes. The next scheduled backup job will pick it up automatically.
aws s3api put-bucket-tagging --bucket BUCKET --tagging 'TagSet=[{Key=s3backup,Value=true}]'⚠ Expected: Initial backup failures on first deploy
After first terraform apply, you may see AWS Backup Failures alert with many failed backup jobs. This is expected because:
- IAM role propagation takes 2–5 minutes — Backup jobs cannot run until the service role is fully active.
- Resource tags may not match — Backup plans target resources by tag (e.g.,
Environment=prod). Verify your resources have these tags. - Backup jobs have not yet run — The initial failure count reflects the alert threshold, not actual job runs. Once job runs complete successfully, failures will drop to 0.
To verify it's working: Check CloudWatch: aws backup list-backup-jobs --region us-east-1 --by-state COMPLETED — if you see successful backups, the failures were just initial state alerts.
✓ Quick Check Commands
aws backup list-backup-vaults --region us-east-1
aws backup list-backup-jobs --region us-east-1 --max-results 5
aws ec2 describe-instances --filters "Name=tag:Environment,Values=prod" --region us-east-1
🔄 Initial Alert Flood: Why? (Monitoring + Backup)
When you apply Monitoring or Backup Terraform for the first time, you may receive a large burst of alerts. Here's why:
Monitoring (CloudWatch Alarms)
Every newly created alarm starts in INSUFFICIENT_DATA state. Within 5–10 minutes, as metrics arrive, alarms evaluate and transition to OK (or ALARM if unhealthy). If all resources are healthy, every alarm fires an OK notification simultaneously — creating a large burst. This happens once and will not repeat.
Backup (AWS Backup Service)
Backup jobs have not yet run (no backup history), so the "failure count" shown in the alert is not real failures — it's the initial state. The alert threshold is met because Terraform just created the backup plan and no jobs have succeeded yet. Wait 5–10 minutes for IAM propagation and the first backup cycle. Failures will drop to zero once initial jobs complete.
→ Both are normal and expected. Do not destroy and reapply to make them go away — that will just restart the cycle.
What Production Looks Like
Developer laptop → Terraform → AWS SSO login → Assume role → Deploy resources
- •Remote state in S3
- •State locking with DynamoDB
- •CI/CD pipelines (GitHub Actions, GitLab, etc.)
The One Thing Beginners Miss (State)
Terraform tracks every resource it creates in a state file. For teams, store it remotely to avoid conflicts:
terraform {
backend "s3" {
bucket = "terraform-state-prod"
key = "network/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-lock"
}
}Drift detection with S3 state management is available with Base+Drift+Backup plan and above.