Data Source: aws_iam_principal_policy_simulation

Runs a simulation of the IAM policies of a particular principal against a given hypothetical request.

You can use this data source in conjunction with Preconditions and Postconditions so that your configuration can test either whether it should have sufficient access to do its own work, or whether policies your configuration declares itself are sufficient for their intended use elsewhere.

Example Usage

Self Access-checking Example

The following example raises an error if the credentials passed to the AWS provider do not have access to perform the three actions s3:GetObject, s3:PutObject, and s3:DeleteObject on the S3 bucket with the given ARN. It combines aws_iam_principal_policy_simulation with the core Terraform postconditions feature.

data "aws_caller_identity" "current" {}

data "aws_iam_principal_policy_simulation" "s3_object_access" {
  action_names = [
    "s3:GetObject",
    "s3:PutObject",
    "s3:DeleteObject",
  ]
  policy_source_arn = data.aws_caller_identity.current.arn
  resource_arns     = ["arn:aws:s3:::my-test-bucket"]

  # The "lifecycle" and "postcondition" block types are part of
  # the main Terraform language, not part of this data source.
  lifecycle {
    postcondition {
      condition     = self.all_allowed
      error_message = <<EOT
        Given AWS credentials do not have sufficient access to manage ${join(", ", self.resource_arns)}.
      EOT
    }
  }
}
        Given AWS credentials do not have sufficient access to manage ${join(", ", self.resource_arns)}.
      EOT
    }
  }
}

If you intend to use this data source to quickly raise an error when the given credentials are insufficient then you must use depends_on inside any resource which would require those credentials, to ensure that the policy check will run first:

resource "aws_s3_bucket_object" "example" {
  bucket = "my-test-bucket"
  # ...

  depends_on = [data.aws_iam_principal_policy_simulation.s3_object_access]
}

Testing the Effect of a Declared Policy

The following example declares an S3 bucket and a user that should have access to the bucket, and then uses aws_iam_principal_policy_simulation to verify that the user does indeed have access to perform needed operations against the bucket.

data "aws_caller_identity" "current" {}

resource "aws_iam_user" "example" {
  name = "example"
}

resource "aws_s3_bucket" "example" {
  bucket = "my-test-bucket"
}

resource "aws_iam_user_policy" "s3_access" {
  name = "example_s3_access"
  user = aws_iam_user.example.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action   = "s3:GetObject"
        Effect   = "Allow"
        Resource = aws_s3_bucket.example.arn
      },
    ]
  })
}

resource "aws_s3_bucket_policy" "account_access" {
  bucket = aws_s3_bucket.example.bucket
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "s3:*"
        Effect = "Allow"
        Principal = {
          # Any caller belonging to the current AWS account
          # is allowed full access to this S3 bucket.
          AWS = data.aws_caller_identity.current.account_id
        }
        Resource = [
          aws_s3_bucket.example.arn,
          "${aws_s3_bucket.example.arn}/*",
        ]
      },
    ]
  })
}

data "aws_iam_principal_policy_simulation" "s3_object_access" {
  action_names = [
    "s3:GetObject",
  ]
  policy_source_arn = aws_iam_user.example.arn
  resource_arns     = [aws_s3_bucket.example.arn]

  # The IAM policy simulator automatically imports the policies associated
  # with the user (policy_source_arn) but cannot automatically import the
  # policies from the S3 bucket, because in a real request those would be
  # provided by the S3 service itself. Therefore we need to provide the
  # same policy that we associated with the S3 bucket above.
  resource_policy_json = aws_s3_bucket_policy.account_access.policy

  # The policy simulation won't be valid until the user's policy
  # has been created or updated.
  depends_on = [aws_iam_user_policy.s3_access]

  # The "lifecycle" and "postcondition" block types are part of
  # the main Terraform language, not part of this data source.
  lifecycle {
    postcondition {
      condition     = self.all_allowed
      error_message = <<EOT
        ${self.policy_source_arn} does not have the expected access to ${join(", ", self.resource_arns)}.
      EOT
    }
  }
}
        ${self.policy_source_arn} does not have the expected access to ${join(", ", self.resource_arns)}.
      EOT
    }
  }
}

When using aws_iam_principal_policy_simulation to test the effect of a policy declared elsewhere in the same configuration, it's important to use depends_on to make sure that the needed policy has been fully created or updated before running the simulation.

Argument Reference

The following arguments are required for any principal policy simulation:

You must closely match the form of the real service request you are simulating in order to achieve a realistic result. You can use the following additional arguments to specify other characteristics of the simulated requests:

context block arguments

The following arguments are all required in each context block:

Attribute Reference

This data source exports the following attributes in addition to the arguments above: