SQS policies are surprisingly complex because they’re not just about granting access, but about defining who can access what from where, and the most secure way to do it is to be as restrictive as possible.
Let’s watch Terraform create a queue and a policy for it, granting a specific Lambda function permission to send messages.
resource "aws_sqs_queue" "my_queue" {
name = "my-secure-processing-queue"
}
resource "aws_sqs_queue_policy" "my_queue_policy" {
queue_url = aws_sqs_queue.my_queue.id
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::123456789012:function:MyLambdaFunction"
},
Action = "sqs:SendMessage",
Resource = aws_sqs_queue.my_queue.arn
}
]
})
}
Here, aws_sqs_queue.my_queue.id gives us the SQS queue’s URL, and aws_sqs_queue.my_queue.arn provides its Amazon Resource Name (ARN). The jsonencode block constructs the IAM policy document. The Principal element is crucial: we’re explicitly stating that only the Lambda function with the ARN arn:aws:iam::123456789012:function:MyLambdaFunction is allowed to perform the sqs:SendMessage action on this specific queue.
This setup solves the problem of needing to grant cross-account or cross-service access to SQS queues without exposing them to the entire internet. The internal workings of SQS policies leverage IAM’s condition evaluation engine. When a request comes in to SendMessage to your queue, SQS checks the request against the attached policy. It verifies if the Principal making the request matches the Principal in the policy and if the Action requested is allowed. If the principal is an IAM role assumed by a Lambda function, SQS resolves that role to the specific account and service principal.
The key levers you control are:
- Principal: Who is allowed to act. This can be an AWS account, an IAM user, an IAM role, a service principal (like
lambda.amazonaws.com), or even federated users. For maximum security, be as specific as possible. Instead of{"AWS": "*"}(everyone), use"arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"or"arn:aws:iam::ACCOUNT_ID:user/USER_NAME". - Action: What operation is allowed. Common SQS actions include
sqs:SendMessage,sqs:ReceiveMessage,sqs:DeleteMessage,sqs:ListQueues, etc. - Resource: Which SQS queue the action applies to. This is typically the ARN of the queue.
- Condition: Optional constraints that must be met for the policy to apply. Examples include source IP addresses, VPC endpoints, or specific IAM conditions.
A common mistake is to grant broader permissions than necessary. For instance, allowing sqs:* instead of just sqs:SendMessage is a significant security risk. Another pitfall is using {"AWS": "*"} for the principal when you intend to grant access to a specific service or account. Always aim for the most granular permissions.
The most surprising thing about SQS policies is how Principal can be used to grant access to other AWS services, not just IAM principals. For example, you can grant lambda.amazonaws.com permission to sqs:SendMessage if you’re using Lambda to push messages into a queue, but you still need to specify the specific Lambda function ARN if you want to restrict it to only that function. This is done by specifying the Lambda function’s ARN in the AWS element of the Principal object, like {"AWS": "arn:aws:iam::123456789012:function:MyLambdaFunction"}.
The next concept you’ll likely encounter is managing dead-letter queues (DLQs) to handle messages that fail processing.