Deploy the Spotto AWS Role with Terraform
The terraform-aws-spotto module manages SpottoReadOnlyRole as code. It reads the same setup package as the Bash wizard and PowerShell wizard and produces the same results JSON. The module doesn't write any permissions of its own: the trust policy, External ID and policies all come from the package, unchanged.
Get The Module
terraform-aws-spotto on GitHub (public, with a README and examples in examples/single-account and examples/organization).
You don't need to download it. Terraform fetches it from GitHub through the source addresses in the examples below.
Prerequisites
- Terraform 1.12.0 or later, with the
hashicorp/awsprovider 5.x or 6.x. - The setup package
spotto-aws-setup.json. In the Portal, select Download Setup Package. Preview customers receive it from Spotto. Enter any billing export details first (find them in AWS), so one run sets up every role. If you use Discover Exports instead, see discover a Data Export instead. - An AWS provider configuration for each account.
- In each account, these permissions for the identity running Terraform:
sts:GetCallerIdentityiam:GetRole,iam:CreateRole,iam:UpdateAssumeRolePolicy,iam:TagRole,iam:UntagRole,iam:ListRoleTags,iam:UpdateRole,iam:DeleteRoleiam:GetRolePolicy,iam:PutRolePolicy,iam:DeleteRolePolicy,iam:ListRolePoliciesiam:ListAttachedRolePolicies,iam:AttachRolePolicy,iam:DetachRolePolicy,iam:ListInstanceProfilesForRoleiam:PutRolePermissionsBoundary, only if you set a permissions boundary
Modules
| Module | Purpose |
|---|---|
modules/account-role | Creates or updates the role in one account. Use one instance per account. Optional inputs: permissions_boundary_arn and tags. The role is tagged SpottoCompanyId and SpottoManagedBy=Terraform. |
modules/handoff | Combines every account's result into the results JSON for Spotto. The plan fails if an account in the package has no module instance. |
Single Account Example
provider "aws" {
region = "us-east-1" # IAM is global; this only selects the STS endpoint.
}
locals {
setup_package = file("./spotto-aws-setup.json")
}
module "spotto_role" {
source = "git::https://github.com/spottoai/terraform-aws-spotto.git//modules/account-role"
setup_package = local.setup_package
account_id = "111122223333"
}
module "spotto_handoff" {
source = "git::https://github.com/spottoai/terraform-aws-spotto.git//modules/handoff"
setup_package = local.setup_package
account_results = [module.spotto_role.handoff_result]
}
output "aws_manual_onboarding_json" {
value = module.spotto_handoff.aws_manual_onboarding_json
}
The examples use the latest version of the module. To pick up updates later, run terraform init -upgrade. Reference modules from the repository with the //modules/... path as shown. They share an internal module by relative path, so you can't copy a single module folder.
Organizations
Terraform needs a provider per account, so add one account-role instance per account, each with providers = { aws = aws.<alias> }. The repository's examples/organization example signs in to the management account and assumes OrganizationAccountAccessRole or AWSControlTowerExecution in member accounts. IAM Identity Center profiles per account work too.
The example refers to the modules by relative path. If you copy it out of the repository, change each source to the git::https://github.com/spottoai/terraform-aws-spotto.git//modules/... form shown above.
The package covers exactly the accounts listed in Spotto. Terraform doesn't discover organization accounts.
Apply And Return The Results
- Run
terraform initandterraform plan. The plan stops if a provider signs in to the wrong account, an account is missing, or the package isn't a valid, consistent Spotto AWS setup package. - Run
terraform apply. - Run
terraform output -raw aws_manual_onboarding_json. - Return the output:
- Preview customers: send it to your Spotto contact.
- Portal users: paste it into Setup Results JSON, select Read Setup Results, then Create or Update.
The output contains your Spotto company ID, the account and billing configuration from the package, and each account's role ARN and outcome. It contains no credentials and no External ID. configured means the IAM changes were applied. Spotto checks effective access when it validates the connection.
Change Or Remove Access
- Permissions change: download a new package and apply. Terraform changes only what differs.
- Legacy managed policies: applying a current package automatically detaches
ReadOnlyAccessandSecurityAuditattachments tracked in Terraform state. Review those removals in the plan; attachments outside the module's state remain unchanged. - Billing or commitments removed in Spotto: the matching inline policy is deleted on the next apply.
- Account removed: removing its module instance, or running
terraform destroy, deletes the role. Remove the account from Spotto first.
Existing Roles
If SpottoReadOnlyRole already exists, the apply fails with EntityAlreadyExists. To manage it with Terraform, add import blocks for the role, its Spotto inline policies and any managed policy attachments, then review the plan. The module README has the exact blocks. Don't import a role that a CloudFormation stack owns.
Bash and PowerShell treat SpottoManagedBy=Terraform as verify-only: they can report a configuration match, but refuse changes even with their repair option. Keep maintaining this role through Terraform.
State Contains The External ID
The setup package and Terraform state contain your company's External ID inside the trust policy. AWS doesn't treat External IDs as secrets, but the value ties the role to your Spotto company. Use a remote backend with encryption and access controls.