Back to home
sentinel-onboarding

Terraform Module

Provisions the least-privilege cross-account IAM role required to connect an AWS account to Vigilare. Idempotent — safe to run multiple times.

Sourcegithub.com/algofyczech/sentinel//terraform-modules/sentinel-onboarding
Terraform>= 1.5
AWS provider>= 5.0

Overview

What this module creates and how it fits into the Vigilare onboarding flow.

The sentinel-onboarding module creates a single IAM role in your AWS account. Vigilare assumes this role via sts:AssumeRole to collect billing, IAM, GuardDuty, SES, CloudTrail, service quota, and account-metadata signals — all read-only.

The trust policy is locked to the Vigilare platform AWS account ID and enforces an ExternalId condition (your unique token issued at registration) to prevent confused-deputy attacks.

Onboarding flow

  1. Sign up on the Vigilare dashboard and connect an AWS account.
  2. The dashboard returns your external_id and sentinel_account_id.
  3. Run this module with those values to create the IAM role.
  4. Copy the role_arn output into the dashboard to complete verification.
  5. Vigilare assumes the role and triggers the first scan immediately.

Requirements

Versions and providers this module depends on.

NameVersion
terraform>= 1.5
hashicorp/aws>= 5.0

Usage

Minimal configuration to provision the cross-account role.

module "sentinel_onboarding" {
  source = "github.com/algofyczech/sentinel//terraform-modules/sentinel-onboarding"

  # Issued by Vigilare during account registration — treat as a secret
  external_id         = var.sentinel_external_id

  # The Vigilare platform AWS account (provided by Vigilare)
  sentinel_account_id = "123456789012"

  # Optional: customise the role name
  role_name           = "sentinel-readonly"

  # Set to true if you have Business or Enterprise support
  enable_support_tier = false

  tags = {
    Environment = "production"
    CostCenter  = "security"
  }
}

# Pass the role ARN back to the Vigilare registration flow
output "sentinel_role_arn" {
  value = module.sentinel_onboarding.role_arn
}
Secret handling: Store external_id in a secret manager (AWS Secrets Manager, Terraform Cloud variables, or a .tfvars file excluded from source control). Do not commit it to your repository.

Inputs

Variables accepted by the module. * marks required inputs.

NameTypeDefaultDescription
external_id*stringrequiredExternal ID issued by Vigilare during account registration. Must be at least 32 characters. Used as a confused-deputy guard on sts:AssumeRole.
sentinel_account_id*stringrequiredAWS account ID of the Vigilare platform (12 digits). Only this account may assume the role.
role_namestring"sentinel-readonly"Name of the IAM role to create. Changing this after initial deployment forces role re-creation.
enable_support_tierboolfalseSet to true on Business or Enterprise support accounts to enable support:Describe* permissions used by the quota and account collectors.
tagsmap(string){}Additional tags to apply to the IAM role. The ManagedBy = "sentinel" tag is always added automatically.

Outputs

Values exported after a successful apply.

NameDescription
role_arnFull ARN of the created IAM role. Paste this into the Vigilare dashboard to complete account registration.
role_nameName of the created IAM role.

Permissions granted

Complete list of IAM actions added to the inline policy. All actions are read-only. No write, delete, or mutating actions are included.

Cost Explorer
ce:GetCostAndUsagece:GetAnomaliesce:GetAnomalySubscriptionsce:GetAnomalyMonitorsce:ListCostAllocationTags

Scope: Resource: *

Budgets
budgets:DescribeBudgetsbudgets:DescribeBudgetPerformanceHistorybudgets:ViewBudget

Scope: Resource: *

IAM
iam:GetAccountSummaryiam:GenerateCredentialReportiam:GetCredentialReportiam:ListUsersiam:ListVirtualMFADevicesiam:ListMFADevicesiam:ListAttachedUserPoliciesiam:ListUserPoliciesiam:GetUserPolicyiam:ListGroupsForUseriam:ListAttachedGroupPolicies

Scope: Account-level actions on *, user/group actions scoped to account ARNs

Access Analyzer
access-analyzer:ListAnalyzersaccess-analyzer:ListFindingsaccess-analyzer:GetFinding

Scope: ListAnalyzers on *, findings scoped to analyzer ARNs in account

GuardDuty
guardduty:ListDetectorsguardduty:GetDetectorguardduty:ListFindingsguardduty:GetFindingsguardduty:GetFindingsStatistics

Scope: ListDetectors on *, detector actions scoped to detector ARNs in account

SES / SESv2
ses:GetSendStatisticsses:GetSendQuotasesv2:GetAccountsesv2:ListEmailIdentitiessesv2:GetEmailIdentity

Scope: Account actions on *, identity reads scoped to identity ARNs

CloudTrail
cloudtrail:LookupEventscloudtrail:DescribeTrailscloudtrail:ListTrailscloudtrail:GetTrailStatus

Scope: List/lookup on *, GetTrailStatus scoped to trail ARNs in account

Service Quotas + CloudWatch
servicequotas:ListServiceQuotasservicequotas:GetServiceQuotaservicequotas:ListRequestedServiceQuotaChangescloudwatch:GetMetricDatacloudwatch:GetMetricStatisticscloudwatch:ListMetrics

Scope: Resource: *

Account / Organizations / Invoicing
account:GetContactInformationaccount:GetAlternateContactorganizations:DescribeOrganizationorganizations:DescribeAccountinvoicing:ListInvoiceSummaries

Scope: Resource: *

SupportOnly added when enable_support_tier = true
support:DescribeTrustedAdvisorCheckssupport:DescribeTrustedAdvisorCheckResultsupport:DescribeCases

Scope: Resource: *

Security notes

Design decisions that limit the blast radius of the granted access.

Least privilege

All permissions are read-only (Get*, List*, Describe*, Lookup*). No write, delete, or mutating actions are granted.

Confused-deputy protection

The trust policy requires a matching sts:ExternalId condition. The external_id value is unique per connected account and must be treated as a secret.

No data exfiltration surface

The policy grants no s3:GetObject, no secretsmanager:GetSecretValue, no kms:Decrypt, and no ec2:GetConsoleOutput. It cannot read application data.

Resource-level scoping where possible

IAM user/group actions, Access Analyzer findings, GuardDuty detector operations, SES identity reads, and CloudTrail trail reads are all scoped to ARNs within the customer account. Actions that AWS does not support resource-level restrictions for remain on Resource: *.

Support permissions are opt-in

support:Describe* is only added when enable_support_tier = true, because these APIs require Business or Enterprise support and will fail with an authorization error on Developer/Basic plans.

Idempotency

The inline policy is managed entirely by Terraform and is reconciled on every apply. Manual changes in the console will be overwritten. Running apply multiple times with the same inputs produces no changes.

Questions about the module?

Our team can help with custom role names, multi-account setups, and enterprise deployment patterns.

Contact support