EC2 instances should have IAM profile attached
EC2 instances without an IAM instance profile cannot use temporary credentials from STS. This pushes developers toward embedding long-lived access keys in application config, environment variables, or user data scripts. Leaked keys mean emergency rotation, and rotating hardcoded credentials across distributed config files is its own operational incident.
Instance profiles grant short-lived credentials that rotate automatically and are scoped by an IAM role's policy. AWS Systems Manager Agent, CloudWatch Agent, and CodeDeploy Agent all depend on instance profile credentials to function. Missing profiles also block SSM Session Manager access, forcing teams to open SSH over port 22.
Retrofit consideration
Existing instances do not require a stop/start or reboot to attach an instance profile via the AWS API. Terraform can associate a profile on a running instance, but the EC2 metadata service may need a short propagation window before new credentials appear. After migration, audit each instance for hardcoded credentials and remove them.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "ec2_instance" {
source = "pcidss.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "hipaa.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "nist80053.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "cisv80ig1.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "nist800171.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "ffiec.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "acscessentialeight.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "cfrpart11.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "rbiitfnbfc.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
module "ec2_instance" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
If you use terraform-aws-modules/ec2-instance/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "ec2_instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = ">=6.0.0"
ami_ssm_parameter = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64"
instance_type = "t4g.nano"
subnet_id = "subnet-abc123"
}
Use AWS provider resources directly. See docs for the resources involved: aws_instance.
resource "aws_instance" "this" {
ami = "ami-abc12345"
instance_type = "t4g.nano"
subnet_id = element(["subnet-abc123", "subnet-def456"], 0)
vpc_security_group_ids = ["sg-abc12345"]
iam_instance_profile = "example-instance-profile"
}
What this control checks
The aws_instance resource must include the iam_instance_profile argument pointing to a valid aws_iam_instance_profile resource. A configuration passes when iam_instance_profile is set to a non-empty value (name or ARN). It fails when the argument is omitted or unset. The referenced aws_iam_instance_profile must itself reference an aws_iam_role via the role argument. For launch templates, aws_launch_template should include an iam_instance_profile block with either name or arn specified. For Auto Scaling groups using launch templates, the profile must be defined in the template rather than on individual instances.
Common pitfalls
Instance profile vs. IAM role confusion
An IAM role alone does not satisfy this control. You must create an
aws_iam_instance_profileresource that wraps the role, then reference the profile inaws_instance.iam_instance_profile. Having a role defined without the instance profile wrapper is one of the most common Terraform mistakes on this control.Empty or placeholder profiles
Attaching an instance profile with a role that has no policies technically passes this control but provides no actual permissions. Some teams create stub profiles to clear compliance checks without granting the permissions applications need. The result is runtime failures that are easy to misattribute until someone checks the role.
Launch template override gaps
When an Auto Scaling group uses mixed instance types with launch template overrides, the
iam_instance_profileblock must be defined in the baseaws_launch_template, not in an override. Overrides do not support changing the instance profile, so if it's missing from the base template, launched instances have no profile attached.Spot and on-demand fleet instances
Instances launched via
aws_spot_instance_requestor EC2 Fleet require the sameiam_instance_profileargument. These resource types get overlooked when teams apply this control toaws_instanceresources and assume coverage is complete.
Audit evidence
Auditors look for AWS Config rule evaluation results from the managed rule ec2-instance-profile-attached showing all EC2 instances as compliant. A supplementary report listing all running instances alongside their attached instance profile ARN (from aws ec2 describe-instances filtered on IamInstanceProfile) is direct supporting evidence. CloudTrail events for AssociateIamInstanceProfile and RunInstances confirm when profiles were attached and whether new launches include them.
For continuous compliance, AWS Security Hub findings mapped to this control, or external CSPM scan results showing zero non-compliant EC2 instances, work as ongoing evidence.
Framework-specific interpretation
PCI DSS v4.0: Requirements 7 and 8 together cover what access should exist and how it's authenticated. Instance profiles satisfy both: each instance gets a distinct identity tied to a scoped role, eliminating shared long-lived credentials and producing a per-instance authentication record that examiners can trace.
HIPAA Omnibus Rule 2013: The Security Rule at 164.312(a)(1) requires access controls that restrict system access to authorized users and processes. For EC2 workloads touching ePHI in other AWS services, an instance profile replaces shared static credentials with temporary, role-scoped keys, meeting the minimum necessary standard for that machine-level access.
NIST SP 800-53 Rev 5: Maps to AC-2 (Account Management), AC-3 (Access Enforcement), and IA-2 (Identification and Authentication). Instance profiles give each EC2 instance a managed identity within IAM, with credentials that rotate automatically and a role policy enforcing the access boundaries AC-3 requires. Under AC-2, there's no static key lifecycle to manage.
Related controls
EC2 launch templates should not assign public IPs to network interfaces
Neptune DB clusters should have IAM database authentication enabled
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
ec2_instance_iam_profile_attachedAWS Config Managed Rule:
EC2_INSTANCE_PROFILE_ATTACHEDCheckov Check:
CKV2_AWS_41Powerpipe Controls:
aws_compliance.control.ec2_instance_iam_profile_attached,aws_compliance.control.ec2_instance_publicly_accessible_iam_profile_attachedProwler Check:
ec2_instance_profile_attached
Last reviewed: 2026-03-09