A secure-by-default, aggregation-flavored composite for Amazon QuickSight — data sources, data sets with row/column-level security, analyses, dashboards, templates, themes, folders, groups/users, VPC connections, and refresh schedules — the largest resource count in the AWS gap-closure catalog. Built for the AWS provider v6.x.
- 📊 Creates and wires QuickSight data sources across ~21 connector types (S3, Athena, Redshift, RDS, Aurora, Snowflake, and more) with deeply-typed, non-
anyconnection parameters. - 🗃️ Builds data sets with full physical/logical table transform pipelines, plus row-level and column-level security — the primary controls for restricting who sees which rows/columns of financial or member data.
- 📈 Promotes analyses → templates → dashboards via the
source_entitytemplate-promotion pattern (AWS's own recommended production path). - 🗂️ Organizes content into folders, manages groups/users/namespaces, and grants IAM policy assignments and custom-permissions profiles.
- 🔒 Wires private VPC connectivity for data sources instead of the public internet, and supports customer-managed KMS key registration and account-wide IP allow-listing.
- ⏱️ Schedules SPICE refreshes (recurring) and one-shot ingestions.
💡 Why it matters: QuickSight dashboards routinely surface member-identifiable financial data at — the difference between a data set with row-level security and one without is the difference between "each loan officer sees their own portfolio" and "every viewer sees every member's balance."
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- ⭐ Star this repository to help others discover this Terraform module.
- 🤝 Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
flowchart LR
VPC["tf-mod-aws-vpc"] --> VPCCONN["vpc_connections"]
SG["tf-mod-aws-security-group"] --> VPCCONN
IAMROLE["tf-mod-aws-iam-role"] --> VPCCONN
IAMROLE --> DS["data_sources"]
IAMPOLICY["tf-mod-aws-iam-policy"] --> IPA["iam_policy_assignments"]
KMS["tf-mod-aws-kms"] --> KEYREG["key_registration"]
SECRETS["tf-mod-aws-secrets-manager"] --> DS
RDS["tf-mod-aws-rds / tf-mod-aws-rds-aurora"] -.->|connection params| DS
REDSHIFT["tf-mod-aws-redshift"] -.->|connection params| DS
S3["tf-mod-aws-s3-bucket"] -.->|manifest / source| DS
subgraph QS["tf-mod-aws-quicksight"]
DS --> DSET["data_sets"]
VPCCONN --> DS
DSET --> ANALYSIS["analyses"]
DSET --> TEMPLATE["templates"]
ANALYSIS --> TEMPLATE
TEMPLATE --> DASH["dashboards"]
DASH --> FOLDER["folders"]
DSET --> REFRESH["refresh_schedules"]
end
style QS fill:#FF9900,color:#fff
flowchart TB
NS["aws_quicksight_namespace"] --> GRP["aws_quicksight_group"]
NS --> USR["aws_quicksight_user"]
GRP --> GRPM["aws_quicksight_group_membership"]
USR --> IPA2["aws_quicksight_iam_policy_assignment"]
VPCC["aws_quicksight_vpc_connection"] --> DSRC["aws_quicksight_data_source"]
DSRC --> DSET2["aws_quicksight_data_set"]
DSET2 --> ING["aws_quicksight_ingestion"]
DSET2 --> REFR["aws_quicksight_refresh_schedule"]
DSET2 --> ANL["aws_quicksight_analysis"]
THEME["aws_quicksight_theme"] --> ANL
ANL --> TMPL["aws_quicksight_template"]
TMPL --> TALIAS["aws_quicksight_template_alias"]
TMPL --> DASH2["aws_quicksight_dashboard"]
THEME --> DASH2
FLD["aws_quicksight_folder"] --> FMEM["aws_quicksight_folder_membership"]
DASH2 --> FMEM
ANL --> FMEM
DSET2 --> FMEM
CP["aws_quicksight_custom_permissions"] --> RCP["aws_quicksight_role_custom_permission"]
CP --> UCP["aws_quicksight_user_custom_permission"]
GRP --> RM["aws_quicksight_role_membership"]
ACCSUB["aws_quicksight_account_subscription"] --> ACCSET["aws_quicksight_account_settings"]
ACCSUB -.->|account-wide gate| DSRC
IPR["aws_quicksight_ip_restriction"]
KEYR["aws_quicksight_key_registration"]
style DSRC fill:#FF9900,color:#fff
ℹ️ Treatment depth is tiered.
data_source,data_set,analysis,dashboard,folder/folder_membership,group/group_membership,user,vpc_connection, andrefresh_scheduleare deeply-typed and schema-complete. The eight account-singleton/permission-profile resources (account_subscription,account_settings,ip_restriction,key_registration,custom_permissions,role_custom_permission,role_membership,user_custom_permission) get lighter-but-honest treatment — every required field is modeled, just with fewer worked examples. SeeSCOPE.mdfor the full transparency note, including why rawdefinitionauthoring on analyses/dashboards/templates is out of scope.
- Terraform
>= 1.12.0 hashicorp/aws >= 6.0, < 7.0- No
provider {}block inside this module — the caller's provider configuration is inherited.
| Action | Required for | Notes |
|---|---|---|
quicksight:CreateAccountSubscription, DescribeAccountSubscription, DeleteAccountSubscription |
account_subscription |
One-time, account-wide gate |
quicksight:CreateDataSource / Describe* / Update* / Delete* / *Permissions |
data_sources |
— |
quicksight:CreateDataSet / Describe* / Update* / Delete* / *Permissions |
data_sets |
Includes RLS/CLS grants |
quicksight:CreateIngestion, DescribeIngestion, CancelIngestion |
ingestions |
— |
quicksight:CreateRefreshSchedule / Describe* / Update* / Delete* |
refresh_schedules |
— |
quicksight:CreateAnalysis / Describe* / Update* / Delete* / *Permissions |
analyses |
— |
quicksight:CreateDashboard / Describe* / Update* / Delete* / *Permissions, UpdateDashboardPublishedVersion |
dashboards |
— |
quicksight:CreateTemplate / Describe* / Update* / Delete* / *Permissions |
templates |
— |
quicksight:CreateTemplateAlias / Describe* / Update* / Delete* |
template_aliases |
— |
quicksight:CreateTheme / Describe* / Update* / Delete* / *Permissions |
themes |
— |
quicksight:CreateFolder / Describe* / Update* / Delete* / *Permissions |
folders |
— |
quicksight:CreateFolderMembership, DeleteFolderMembership |
folder_memberships |
— |
quicksight:CreateGroup, DescribeGroup, DeleteGroup |
groups |
— |
quicksight:CreateGroupMembership, DeleteGroupMembership |
group_memberships |
— |
quicksight:RegisterUser, DescribeUser, DeleteUser |
users |
— |
quicksight:CreateNamespace, DescribeNamespace, DeleteNamespace |
namespaces |
— |
quicksight:CreateIAMPolicyAssignment / Describe* / Update* / Delete* |
iam_policy_assignments |
— |
iam:PassRole |
vpc_connections[*].role_arn, data_sources[*].parameters.{s3,athena}.role_arn |
Scope to each specific role ARN — never * |
quicksight:CreateVPCConnection / Describe* / Update* / Delete* |
vpc_connections |
— |
quicksight:UpdateIpRestriction, DescribeIpRestriction |
ip_restriction |
Account-wide singleton |
quicksight:UpdateKeyRegistration, DescribeKeyRegistration |
key_registration |
Account-wide singleton |
quicksight:CreateCustomPermissions / Describe* / Update* / Delete* |
custom_permissions |
— |
quicksight:UpdateRoleCustomPermission, DescribeRoleCustomPermission, DeleteRoleCustomPermission |
role_custom_permissions |
— |
quicksight:CreateRoleMembership, DeleteRoleMembership |
role_memberships |
— |
quicksight:UpdateUserCustomPermission, DescribeUserCustomPermission, DeleteUserCustomPermission |
user_custom_permissions |
— |
No service-linked role is auto-created by any resource in this module.
- Account subscription gates everything. Leave
var.account_subscription = nullif the account is already subscribed — re-subscribing an already-subscribed account fails. - Row-level and column-level security are the primary mechanisms for restricting who sees which rows/columns of financial/member data. This module does not enforce either automatically; data set owners must set
row_level_permission_data_set/column_level_permission_rulesexplicitly, and Compliance/Risk should review the design for any dashboard exposing member financial data. - QuickSight account subscriptions are Region-specific — no cross-Region replica model.
- SPICE capacity is a purchased, account-level quota distinct from Terraform's
applysuccess — exceeding it fails ingestion. - VPC connections require ENI quota headroom in the target subnets.
ip_restriction/key_registrationdestroy clears the ENTIRE account's restrictions/registrations, not just what this module manages — treat as a security-relevant event.
tf-mod-aws-quicksight/
├── providers.tf
├── variables.tf
├── main.tf
├── outputs.tf
├── README.md
└── SCOPE.md
module "quicksight" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-quicksight?ref=v1.0.0"
vpc_connections = {
core = {
name = "core-vpc-connection"
vpc_id = module.vpc.id
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.quicksight_sg.id]
role_arn = module.quicksight_vpc_role.arn
}
}
data_sources = {
member_redshift = {
name = "Member Redshift"
type = "REDSHIFT"
parameters = {
redshift = {
cluster_id = "member-analytics-cluster"
database = "memberdata"
}
}
vpc_connection_key = "core"
credentials = {
secret_arn = module.redshift_secret.arn
}
}
}
data_sets = {
loan_portfolio = {
name = "Loan Portfolio"
import_mode = "DIRECT_QUERY"
physical_table_map = {
loans = {
relational_table = {
data_source_arn = module.quicksight.data_source_arns["member_redshift"]
name = "loans"
input_columns = [
{ name = "loan_id", type = "STRING" },
{ name = "officer_id", type = "STRING" },
{ name = "balance", type = "DECIMAL" },
]
}
}
}
}
}
tags = {
Environment = "prod"
Owner = "analytics-platform"
}
}| Input | Type | Source module |
|---|---|---|
vpc_connections[*].subnet_ids |
list(string) |
tf-mod-aws-vpc |
vpc_connections[*].security_group_ids |
list(string) |
tf-mod-aws-security-group |
vpc_connections[*].role_arn |
string |
tf-mod-aws-iam-role |
data_sources[*].parameters.{s3,athena}.role_arn |
string |
tf-mod-aws-iam-role |
data_sources[*].credentials.secret_arn |
string |
tf-mod-aws-secrets-manager |
iam_policy_assignments[*].policy_arn |
string |
tf-mod-aws-iam-policy |
key_registration.key_registration[*].key_arn |
string |
tf-mod-aws-kms |
| Output | Description | Consumed by |
|---|---|---|
data_source_arns / data_source_ids |
Data source ARN/id maps | data_sets[*].physical_table_map |
data_set_arns / data_set_ids |
Data set ARN/id maps | templates/analyses/dashboards data set references |
analysis_arns |
Analysis ARN map | templates[*].source_entity.source_analysis.arn |
template_arns / template_version_numbers |
Template ARN/version maps | template_aliases, analyses/dashboards source_template |
dashboard_arns / dashboard_ids |
Dashboard ARN/id maps | folder_memberships |
theme_arns |
Theme ARN map | analyses[*].theme_arn, dashboards[*].theme_arn |
folder_arns / folder_ids |
Folder ARN/id maps | Nested folders, folder_memberships |
group_arns / user_arns |
Principal ARN maps | RLS/CLS principals, permissions[*].principal |
vpc_connection_arns |
VPC connection ARN map | data_sources[*].vpc_connection_key |
custom_permissions_arns |
Custom-permissions profile ARN map | role_custom_permissions, user_custom_permissions |
Full details, including the complete Consumes/Emits tables and every provider
gotcha, are in SCOPE.md.
1 · Minimal S3 data source + data set
module "quicksight" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-quicksight?ref=v1.0.0"
data_sources = {
csv_uploads = {
name = "CSV Uploads"
type = "S3"
parameters = {
s3 = {
manifest_file_location = {
bucket = module.reports_bucket.id
key = "manifests/quicksight-manifest.json"
}
}
}
}
}
data_sets = {
monthly_report = {
name = "Monthly Report"
import_mode = "SPICE"
physical_table_map = {
report = {
s3_source = {
data_source_arn = module.quicksight.data_source_arns["csv_uploads"]
input_columns = [
{ name = "period", type = "STRING" },
{ name = "amount", type = "DECIMAL" },
]
upload_settings = { format = "CSV", contains_header = true }
}
}
}
}
}
}2 · Row-level security data set (member-identifiable financial data)
data_sets = {
rls_rules = {
name = "RLS Rules"
import_mode = "SPICE"
use_as = "RLS_RULES"
physical_table_map = {
rules = {
s3_source = {
data_source_arn = module.quicksight.data_source_arns["csv_uploads"]
input_columns = [
{ name = "UserName", type = "STRING" },
{ name = "OfficerId", type = "STRING" },
]
upload_settings = { format = "CSV", contains_header = true }
}
}
}
}
loan_portfolio = {
name = "Loan Portfolio"
import_mode = "DIRECT_QUERY"
physical_table_map = {
loans = {
relational_table = {
data_source_arn = module.quicksight.data_source_arns["member_redshift"]
name = "loans"
input_columns = [
{ name = "loan_id", type = "STRING" },
{ name = "officer_id", type = "STRING" },
{ name = "balance", type = "DECIMAL" },
]
}
}
}
row_level_permission_data_set = {
arn = module.quicksight.data_set_arns["rls_rules"]
permission_policy = "GRANT_ACCESS"
}
column_level_permission_rules = [
{
column_names = ["balance"]
principals = [module.quicksight.group_arns["loan-officers"]]
}
]
}
}3 · Groups and users
groups = {
loan-officers = { description = "Loan officer viewers" }
analysts = { description = "Data analysts / authors" }
}
group_memberships = {
jsmith-officers = { group_name = "loan-officers", member_name = "jsmith" }
}
users = {
jsmith = {
email = "jsmith@financialpartners.com"
identity_type = "IAM"
user_role = "READER"
iam_arn = module.jsmith_role.arn
session_name = "jsmith"
}
}4 · VPC-connected private data source (secure-by-default)
vpc_connections = {
core = {
name = "core-vpc-connection"
vpc_id = module.vpc.id
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.quicksight_sg.id]
role_arn = module.quicksight_vpc_role.arn
}
}
data_sources = {
core_rds = {
name = "Core RDS"
type = "POSTGRESQL"
parameters = {
postgresql = {
host = module.rds.endpoint
port = 5432
database = "coredb"
}
}
vpc_connection_key = "core"
credentials = {
secret_arn = module.rds_secret.arn
}
ssl_properties = { disable_ssl = false }
}
}5 · Analysis promoted to a template, then to a dashboard
analyses = {
loan_analysis = {
name = "Loan Analysis (WIP)"
source_entity = {
source_template = {
arn = data.aws_quicksight_template.starter.arn
data_set_references = [{
data_set_arn = module.quicksight.data_set_arns["loan_portfolio"]
data_set_placeholder = "loans"
}]
}
}
recovery_window_in_days = 30
}
}
templates = {
loan_template = {
name = "Loan Template"
version_description = "v1"
source_entity = {
source_analysis = {
arn = module.quicksight.analysis_arns["loan_analysis"]
data_set_references = [{
data_set_arn = module.quicksight.data_set_arns["loan_portfolio"]
data_set_placeholder = "loans"
}]
}
}
}
}
dashboards = {
loan_dashboard = {
name = "Loan Portfolio Dashboard"
version_description = "Initial publish"
source_entity = {
source_template = {
arn = module.quicksight.template_arns["loan_template"]
data_set_references = [{
data_set_arn = module.quicksight.data_set_arns["loan_portfolio"]
data_set_placeholder = "loans"
}]
}
}
}
}6 · Folder organization
folders = {
finance = { name = "Finance" }
loans = { name = "Loans", parent_folder_key = "finance" }
}
folder_memberships = {
loan_dashboard_in_loans = {
folder_key = "loans"
member_type = "DASHBOARD"
member_id = module.quicksight.dashboard_ids["loan_dashboard"]
}
}7 · Refresh schedule (weekly incremental)
refresh_schedules = {
loan_portfolio_weekly = {
data_set_key = "loan_portfolio"
refresh_type = "INCREMENTAL_REFRESH"
schedule_frequency = {
interval = "WEEKLY"
time_of_the_day = "05:00"
timezone = "America/Chicago"
refresh_on_day = { day_of_week = "MONDAY" }
}
}
}8 · One-shot ingestion
ingestions = {
loan_portfolio_now = {
data_set_key = "loan_portfolio"
ingestion_type = "FULL_REFRESH"
}
}9 · `tags` example (merges with provider default_tags)
# Root provider block (outside this module):
# provider "aws" {
# default_tags { tags = { ManagedBy = "terraform", Team = "analytics-platform" } }
# }
module "quicksight" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-quicksight?ref=v1.0.0"
tags = {
Environment = "prod"
CostCenter = "1234"
# A key collision with default_tags (e.g. Team) is won by this module's value.
}
#... resource maps...
}10 · Customer-managed KMS key registration
key_registration = {
key_registration = [
{ key_arn = module.quicksight_kms.arn, default_key = true }
]
}11 · Account-wide IP restriction
ip_restriction = {
enabled = true
ip_restriction_rule_map = {
"203.0.113.0/24" = "Corporate network"
}
vpc_id_restriction_rule_map = {
(module.vpc.id) = "Core VPC endpoints"
}
}12 · Secure-by-default opt-out (public data source, no RLS — document the exception)
data_sources = {
public_demo = {
name = "Public Demo Source"
type = "ATHENA"
parameters = { athena = { work_group = "primary" } }
# No vpc_connection_key => public internet path. Documented exception:
# this data source is a non-sensitive public demo, not PII-bearing.
}
}
data_sets = {
demo_set = {
name = "Demo Set"
import_mode = "DIRECT_QUERY"
physical_table_map = {
demo = {
relational_table = {
data_source_arn = module.quicksight.data_source_arns["public_demo"]
name = "demo_table"
input_columns = [{ name = "col1", type = "STRING" }]
}
}
}
# No row_level_permission_data_set / column_level_permission_rules — the
# demo data set contains no member-identifiable data (documented exception).
}
}13 · `for_each` pattern (multiple analyses from a common template)
locals {
regional_analyses = {
midwest = { officer_group = "midwest-officers" }
south = { officer_group = "south-officers" }
}
}
analyses = {
for k, v in local.regional_analyses : k => {
name = "Loan Analysis - ${k}"
source_entity = {
source_template = {
arn = module.quicksight.template_arns["loan_template"]
data_set_references = [{
data_set_arn = module.quicksight.data_set_arns["loan_portfolio"]
data_set_placeholder = "loans"
}]
}
}
permissions = [{
actions = ["quicksight:DescribeAnalysis"]
principal = module.quicksight.group_arns[v.officer_group]
}]
}
}14 · Import an existing dashboard
import {
to = module.quicksight.aws_quicksight_dashboard.this["loan_dashboard"]
id = "123456789012,loan_dashboard"
}15 · End-to-end composition (VPC → IAM role → RDS → QuickSight)
module "vpc" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-vpc?ref=v1.0.0"
#...
}
module "quicksight_vpc_role" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-iam-role?ref=v1.0.0"
name = "quicksight-vpc-connection-role"
assume_role_policy = {
statements = [{
actions = ["sts:AssumeRole"]
principals = [{ type = "Service", identifiers = ["quicksight.amazonaws.com"] }]
}]
}
}
module "rds" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-rds?ref=v1.0.0"
#...
}
module "quicksight" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-quicksight?ref=v1.0.0"
vpc_connections = {
core = {
name = "core-vpc-connection"
vpc_id = module.vpc.id
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.quicksight_sg.id]
role_arn = module.quicksight_vpc_role.arn
}
}
data_sources = {
core_rds = {
name = "Core RDS"
type = "POSTGRESQL"
parameters = {
postgresql = {
host = module.rds.endpoint
port = 5432
database = "coredb"
}
}
vpc_connection_key = "core"
credentials = { secret_arn = module.rds_secret.arn }
}
}
data_sets = {
core_data = {
name = "Core Data"
import_mode = "DIRECT_QUERY"
physical_table_map = {
t = {
relational_table = {
data_source_arn = module.quicksight.data_source_arns["core_rds"]
name = "members"
input_columns = [{ name = "member_id", type = "STRING" }]
}
}
}
row_level_permission_data_set = {
arn = module.quicksight.data_set_arns["core_data_rls"]
permission_policy = "GRANT_ACCESS"
}
}
}
tags = { Environment = "prod" }
}- Identity/Access:
namespaces,groups,group_memberships,users,iam_policy_assignments,role_memberships,custom_permissions,role_custom_permissions,user_custom_permissions - Connectivity:
vpc_connections - Data layer:
data_sources,data_sets,ingestions,refresh_schedules - Presentation:
themes,templates,template_aliases,analyses,dashboards - Organization:
folders,folder_memberships - Account-wide:
account_subscription,account_settings,ip_restriction,key_registration - Universal:
tags,timeouts
Full per-field schemas with FORCE-NEW annotations are documented as heredoc
descriptions directly in variables.tf.
data_source_arns/data_source_ids,data_set_arns/data_set_idsanalysis_arns/analysis_ids,dashboard_arns/dashboard_idstemplate_arns/template_version_numbers,template_alias_arnstheme_arns/theme_version_numbersfolder_arns/folder_ids,folder_membership_idsgroup_arns,user_arns,user_ids,user_invitation_urls(sensitive — single-use registration links)namespace_arns/namespace_idsvpc_connection_arns/vpc_connection_ids/vpc_connection_availability_statusescustom_permissions_arnsingestion_arns/ingestion_statuses,refresh_schedule_arnsiam_policy_assignment_ids,group_membership_idsaccount_subscription_status,account_settings_managed,ip_restriction_enabled,key_registration_managed- Per-collection
*_tags_allmaps (namespace_tags_all,vpc_connection_tags_all,data_source_tags_all,data_set_tags_all,theme_tags_all,template_tags_all,analysis_tags_all,dashboard_tags_all,folder_tags_all,custom_permissions_tags_all)
This module has no single keystone, so there is no single id/arn/tags_all
output — every collection emits its own ARN/id/tags_all map instead.
- ARN format:
arn:aws:quicksight:<region>:<account-id>:<resource-type>/<resource-id>(e.g..../dashboard/loan_dashboard). - Most map keys ARE the AWS-side identifier (
data_source_id,data_set_id,analysis_id,dashboard_id,template_id,theme_id,folder_id,vpc_connection_id) and are therefore FORCE-NEW — renaming a map key destroys and recreates that resource. tags↔tags_all↔ providerdefault_tags: only a subset of the 25 resources accepttagsat all (seevariables.tf'stagsvariable description for the exact exclusion list — an AWS API limitation).aws_quicksight_usercannot be imported.aws_quicksight_refresh_schedule.schedule.schedule_frequencyis required by this module (not optional as the raw schema nominally allows) — see Troubleshooting.- Destroy ordering: destroy
folder_membershipsbefore the member asset; destroydata_sourcesbefore theirvpc_connection. - No
us-east-1global-resource requirement — QuickSight is a Region-scoped service (unlike CloudFront/ACM/WAFv2-CLOUDFRONT).
- SSL enforced by default on data source connections where
ssl_propertiesis set (disable_ssl = false). Opt out viadisable_ssl = trueonly for sources that cannot terminate TLS. - Private VPC connectivity recommended for any PII-bearing data source (
vpc_connection_key). Opt out by omitting it (public internet path). - Row-level security recommended on every data set with member-identifiable rows (
row_level_permission_data_set). Opt out by omitting it. - Column-level security recommended for financial/PII columns (
column_level_permission_rules). Opt out by omitting it. - Account IP allow-listing recommended (
ip_restriction.enabled = true). Opt out by leavingvar.ip_restriction = null. - Customer-managed KMS keys recommended (
key_registration). Opt out by leavingvar.key_registration = null(AWS-owned keys). - Account termination protection defaults to
true. Opt out only with an approved decommission plan. secret_arnrecommended overcredential_pairfor data-source credentials — acredential_pair.passwordis plaintext in state.- Analysis deletion recovery window defaults to 30 days (the AWS maximum). Opt out down to
0(immediate, unrecoverable) only with a documented exception.
cd tf-mod-aws-quicksight
terraform init -backend=false
terraform validate
terraform fmt -check
⚠️ Pin the module source with?ref=v1.0.0— never a branch.terraform plan/applyrequire valid AWS credentials (profile/SSO/OIDC) and a Region; this module's local validation gate never runsapply.
terraform validateandterraform fmt -checkpass with zero errors againsthashicorp/awsv6.54.0.- No live-account testing is part of this authoring workflow — plan/apply
against a non-production account with a least-privilege role is the
recommended next step before promoting to
v1.0.0in a real environment.
$ terraform apply
# module.quicksight.aws_quicksight_data_source.this["core_rds"] will be created
# module.quicksight.aws_quicksight_data_set.this["core_data"] will be created
# module.quicksight.aws_quicksight_dashboard.this["loan_dashboard"] will be created
Plan: 3 to add, 0 to change, 0 to destroy.
Outputs:
data_set_arns = {
"core_data" = "arn:aws:quicksight:us-east-1:123456789012:dataset/core_data"
}
dashboard_arns = {
"loan_dashboard" = "arn:aws:quicksight:us-east-1:123456789012:dashboard/loan_dashboard"
}
- Tag drift from
default_tagsoverlap — a resource-tag key duplicated in the provider'sdefault_tagsblock causes perpetual diffs; resource tags win, check the*_tags_alloutput to see the merged result. - Credential-chain failures —
AccessDenied/NoCredentialProvidersmean the executing identity's AWS credential chain (profile/SSO/OIDC) isn't resolving; this module never handles credentials itself. AccessDeniedExceptiononCreateDataSource/CreateVPCConnection— checkiam:PassRoleis scoped to the exactrole_arnpassed, not a wildcard.terraform validatefails with "Received null value, however the target type cannot handle null values" onaws_quicksight_refresh_schedule— this is whyschedule_frequencyis modeled as required in this module; always set it explicitly (see SCOPE.md provider gotchas).InvalidParameterValueExceptionon data source creation — usually atype/parametersmismatch (e.g.type = "S3"withparameters.athenaset). This module's lifecycle precondition and validation blocks catch the most common cases at plan time, but double-check the sub-object matchestypeexactly.- Dashboard shows no data / blank visuals — check
data_set_referencesplaceholders insource_entitymatch the placeholder names baked into the source template exactly (case-sensitive). - Destroy fails on
aws_quicksight_vpc_connectionwith ENIs still attached — destroy dependentdata_sourcesreferencing the connection first. aws_quicksight_account_subscriptiondestroy schedules deletion of the ENTIRE account — the single most destructive action available in this module; require explicit human sign-off before ever settingvar.account_subscription = nullon an existing subscription.
- Terraform Registry:
hashicorp/awsprovider — QuickSight resource family (data source, data set, analysis, dashboard, template, theme, folder, group, user, namespace, VPC connection, refresh schedule, and related resources) - AWS documentation: Amazon QuickSight User Guide — row-level security, column-level security, VPC connections, SPICE
- AWS documentation: Amazon QuickSight API Reference —
CreateDataSource,CreateDataSet,CreateAccountSubscription
🧡 "Infrastructure as Code should be standardized, consistent, and secure."