Secure IaC Deployment: CloudFormation, StackSets, and Service Catalog
A secure deployment strategy on AWS is one where every account receives the same reviewed infrastructure-as-code definition, the same guardrails, and the same shared resources, so security does not depend on who clicked what in which console. CloudFormation templates (or third-party IaC such as Terraform) become versioned, peer-reviewed security artifacts, validated before deployment with cfn-lint and CloudFormation Guard, protected in production by stack policies and DeletionPolicy, and pushed organization-wide with StackSets. Around that core, this lesson covers what Task 6.2 of the exam guide names: dynamic references that keep secrets out of templates, securing Terraform state, tag policies, SCP tag conditions and Resource Groups for organizing resources, Service Catalog launch roles, AWS RAM sharing, Firewall Manager for centrally enforced network policies, and least-privilege CI/CD pipelines with OIDC federation. It closes by troubleshooting StackSet failures in a single account and drift on security stacks.
On this page10 sections
- IaC as the security baseline: templates are security artifacts
- Protecting security resources: stack policies, DeletionPolicy, and service roles
- StackSets: the security baseline in every account, automatically
- Shift-left validation: cfn-lint, CloudFormation Guard, and change sets
- Third-party IaC: securing Terraform state and plans
- Secrets in templates: NoEcho is masking, dynamic references are the answer
- Service Catalog, AWS RAM, and Firewall Manager: sharing and central enforcement
- Choosing the mechanism — and tagging at deploy time
- Tags for management: standardize, require at creation, and group
- Least-privilege pipelines and a troubleshooting walkthrough
- Treat templates as security artifacts: kept in source control with pull-request review, drift-detected within its limits (explicitly declared properties only), and protected by stack policies, DeletionPolicy, and SCPs against out-of-band edits.
- Deploy a baseline to every current and future account with service-managed StackSets, and explain trusted access, delegated administrators, operation preferences, and the management-account exclusion.
- Place cfn-lint, CloudFormation Guard, CloudFormation Hooks, change sets, and AWS Config correctly before, during, and after deployment — including gating a Terraform plan with Guard.
- Keep secrets out of templates and Terraform state with dynamic references, KMS-encrypted and access-restricted remote state, and service-managed passwords.
- Standardize, require, and group tags with tag policies, SCP tag conditions, Resource Groups, and cost allocation tags.
- Share and enforce across accounts with Service Catalog constraints (including LocalRoleName), AWS RAM subnet, transit gateway and Resolver rule sharing, and Firewall Manager policy types.
- Design least-privilege pipelines: scoped iam:PassRole, per-environment deploy roles isolated by OIDC trust conditions, and approval gates — and diagnose single-account StackSet failures.
IaC as the security baseline: templates are security artifacts
Infrastructure as code makes security consistent because every account receives the same reviewed definition instead of hand-built configuration. A control that exists in most accounts is a finding waiting to happen in the rest, and console-built infrastructure guarantees that kind of inconsistency. When every resource comes from a CloudFormation template, the template — not the console — is where security is defined, reviewed, and versioned.
The change-control properties come from where the template lives, so it is worth being precise:
| Template store | Revert to a known-good version | Review before the change lands | Who changed what, and why |
|---|---|---|---|
| Source control with pull requests (Git) | Yes — any earlier commit | Yes — required approvals on the merge | Yes — commit author, reviewer, and message |
| S3 bucket with Versioning | Yes — any earlier object version | No — an upload is live immediately | Only the uploading principal, and only if CloudTrail S3 data events are enabled; no review record |
| Console edits | No | No | CloudTrail only |
So "peer reviewed, attributable, revertible" requires a repository with pull requests feeding the deployment pipeline; versioned object storage gives only the revert.
The counterpart to declared state is detecting departure from it. Drift detection compares a stack's actual resource configuration against its template and reports each resource as IN_SYNC, MODIFIED, or DELETED, or NOT_CHECKED for resource types that drift detection does not support. Its scope is narrower than it looks: CloudFormation compares only the property values explicitly set in the template or its parameters. A property the template never declares — say, a bucket's public-access settings left at their defaults — is not compared, so someone can change it out-of-band and the resource still reports IN_SYNC. If a setting matters for security, declare it explicitly so drift detection can see it; continuous evaluation of undeclared settings is AWS Config's job.
Drift detection tells you what changed, not who: for attribution you pivot to the CloudTrail management event for the modifying API call, which records the principal, the time, and the source IP address. Drift on an ordinary stack is a hygiene problem; drift on a security stack — an IAM trust policy widened, a security group rule added — is a potential incident.
Note the boundary: CloudFormation drift and template review govern deployment consistency. Continuously evaluating whether deployed resources comply with rules is AWS Config's job, which belongs to Task 6.3. And where accounts, OUs, and SCPs come from is Task 6.1 — this lesson assumes that structure exists and deploys into it.
Protecting security resources: stack policies, DeletionPolicy, and service roles
CloudFormation gives a deployed security stack three distinct protections — stack policies, deletion/replacement policies, and termination protection — plus service roles that control who deploys it. Each covers a different event:
| Control | What it protects against | What it does not cover |
|---|---|---|
| Stack policy | A CloudFormation stack update modifying, replacing, or deleting protected resources | Changes made directly through the service APIs or console; stack deletion |
DeletionPolicy: Retain | Losing the resource when the stack is deleted | Updates that modify the resource |
UpdateReplacePolicy: Retain | Losing the old physical resource when an update forces replacement | Stack deletion |
| Termination protection | Deleting the whole stack | Updates; individual resource changes |
A stack policy is a JSON document that controls which resources can be modified during stack updates. The pattern: deny Update:* on critical logical resources — the trail, the central logging bucket, the audit roles — so a routine update cannot replace or delete them. Updating a protected resource requires an explicit, temporary policy override, which turns a silent mistake into a deliberate act. A stack policy is evaluated only by CloudFormation, though: an administrator calling the EC2 or IAM API directly is not subject to it. Preventing out-of-band edits needs an IAM-layer control that account administrators cannot remove — typically an SCP that denies the modifying actions on baseline resources unless aws:PrincipalArn matches the pipeline's deploy role. Drift detection then becomes confirmation rather than the only line of defence.
DeletionPolicy: Retain keeps a resource when its stack is deleted (Snapshot captures a final snapshot for supported types). Evidence stores — log buckets, KMS keys, audit tables — should carry it so evidence outlives the stack, with UpdateReplacePolicy: Retain covering replacement during updates.
A service role answers who deploys what. Instead of acting with the caller's credentials, CloudFormation assumes a dedicated IAM role scoped to the resource types a template legitimately creates. The deploying human or pipeline needs only CloudFormation permissions plus iam:PassRole — and the PassRole statement must name that specific role's ARN. iam:PassedToService only limits which service receives the role; on a wildcard resource such as role/* it still lets the caller hand CloudFormation an administrator role and deploy anything. That separation — users hold the right to use a scoped role, never the underlying permissions — is the load-bearing idea of this task, and it returns in Service Catalog launch roles and pipeline design below.
StackSets: the security baseline in every account, automatically
CloudFormation StackSets deploy one template as stack instances across many accounts and Regions from a single administration point, which makes them the standard mechanism for giving every account the same security stack. The baseline StackSet typically carries what Task 6.1 decided every account needs: GuardDuty detector settings, the AWS Config recorder and delivery channel, standard security-tooling roles, and log-forwarding plumbing.
The two permission models behave differently in ways that decide most StackSet scenarios:
| Self-managed permissions | Service-managed permissions | |
|---|---|---|
| Roles | You create an administration role in the administrator account and an execution role (default AWSCloudFormationStackSetExecutionRole) in every target account, trusting the administration role | StackSets creates the roles it needs through Organizations |
| Prerequisite | None beyond the roles; works outside an organization | Trusted access for StackSets activated in AWS Organizations (this applies only to this model) |
| Targets | Lists of account IDs | OUs or the organization root |
| New accounts | Must be added by hand | Automatic deployment: accounts joining a targeted OU get the stack; leaving accounts can have instances removed |
| Management account | Can be a target like any other account | Never receives stack instances, even when the root or its OU is targeted |
| Run from | Any account holding the administration role | The management account or a registered delegated administrator (a member account — the management account cannot be one) |
Two consequences follow. A requirement covering "current and future accounts" with no manual step is met only by service-managed permissions with automatic deployment turned on. And when the management account also needs the baseline, deploy it there separately — as an ordinary stack or through a self-managed stack set — because adding its ID as a target does not override the exclusion.
Operation preferences control rollout, not permissions: maximum concurrent accounts (how many accounts deploy at once), failure tolerance (how many account failures per Region stop the operation), Region concurrency (sequential or parallel), and Region order. A low concurrency or zero failure tolerance slows or halts an operation; it does not by itself cause a particular account's instance to fail. When one account fails, look at that account's roles and guardrails (see the walkthrough below).
Shift-left validation: cfn-lint, CloudFormation Guard, and change sets
Shift-left validation means rejecting a misconfiguration before it reaches an account: caught in CI it costs a failed build, caught after deployment it costs an incident. The tools differ in what they check and when they run:
| Tool | Checks | Runs | Input |
|---|---|---|---|
| cfn-lint | Correctness against the CloudFormation resource specification and best-practice rules | Locally / CI, before deployment | CloudFormation templates only |
| CloudFormation Guard (cfn-guard) | Your policy: rules in a declarative DSL stating what must and must not appear | Locally / CI, before deployment | Any JSON or YAML document — templates, Terraform plan JSON, Kubernetes manifests |
| CloudFormation Hooks | Your policy, evaluated by CloudFormation itself | In the account, at deploy time, before resources (or the stack/change set) are provisioned; can fail the operation or warn | The resource properties CloudFormation is about to apply |
| Change set review | A human reading exactly what an update will add, modify (with or without replacement), or delete | Before executing a production update | The proposed stack update |
| AWS Config rules (detective) | Compliance of resources that already exist | After deployment, continuously | Recorded configuration items |
cfn-lint answers "is this valid CloudFormation?" — malformed properties, invalid values, structural mistakes. It has no notion of your organization's security policy. CloudFormation Guard answers "is this allowed here?": every S3 bucket encrypted, no security group open to 0.0.0.0/0 on SSH, no IAM policy with "Action": "*". Run in the pipeline, a Guard violation fails the build — this is how a security team's requirements become machine-enforced instead of living in a wiki. Because Guard evaluates any structured document, the same rule logic can gate other IaC tools (see the Terraform section below).
CloudFormation Hooks move a policy check into the account itself: a hook activated in the account runs every time CloudFormation provisions matching resources, so it catches deployments that bypass the CI pipeline. Hooks can be written as Guard rules or as Lambda functions, and Control Tower's proactive controls are delivered as hooks. AWS Config also has a proactive evaluation mode for supported rules, which evaluates proposed resource properties before deployment — but on its own it only reports a result and never blocks or remediates; the blocking comes from a hook or pipeline that acts on that result. Config rules in their usual detective mode, including custom policy rules written in Guard, evaluate resources that already exist, so they detect and remediate after the fact rather than prevent.
For CDK projects, cdk-nag applies rule packs to the synthesized output; CDK apps synthesize to CloudFormation templates, so every template-level control here still applies. Order the gates: lint and Guard on every commit in CI, hooks as the in-account backstop, change-set review guarding the production apply, Config watching what exists.
Third-party IaC: securing Terraform state and plans
Third-party IaC tools such as Terraform are secured with the same principles as CloudFormation — reviewed code, least-privilege deploy roles, pre-deployment policy checks — plus one risk CloudFormation does not have: the state file. Terraform records every managed resource's attributes in state, and that includes secrets such as a database master password, stored in plaintext. Marking a variable sensitive only redacts it from plan and apply output on screen; the value is still written to state.
Protect remote state as the secret store it effectively is:
- Confidentiality — store state in an S3 backend encrypted with a KMS key, and limit both the bucket policy and the key policy to the pipeline's deploy role (plus break-glass). Developers who can read the state can read every secret in it.
- Integrity and concurrency — state locking (S3 lock files, or a DynamoDB table in older setups) stops two runs writing state at once, and bucket Versioning lets you recover a corrupted or overwritten state. Neither does anything for confidentiality.
- Keep secrets out of state where possible — let the service own the secret (for example, RDS managing the master password in Secrets Manager), or use Terraform's ephemeral values and write-only arguments, which are not persisted to state.
Gating a Terraform apply uses the plan, not the HCL source: export the saved plan as JSON (terraform show -json) and evaluate it with CloudFormation Guard rules in the build, failing the stage before apply runs. This lets a team reuse its existing Guard rule logic across both tools. cfn-lint cannot do this — it understands only CloudFormation templates. Service Catalog also supports Terraform-based products, so a governed self-service path does not require converting everything to CloudFormation.
Secrets in templates: NoEcho is masking, dynamic references are the answer
Secrets belong in a secret store and are pulled into a deployment by reference; a template or stack parameter should never carry the value. The classic finding is a database password visible in a CloudFormation template or in the stack's parameter history, and the mitigations rank clearly.
Plaintext secrets in templates are always wrong. Templates live in Git, flow through pipelines, and are readable via cloudformation:GetTemplate — a secret in a template is published to everyone with repository or stack read access and preserved in version history.
The NoEcho parameter attribute masks a parameter's value (as asterisks) in console and Describe API output. It does not encrypt the value or remove it from what CloudFormation stores. If the parameter has a default, the default is masked in output too — but the default is written in the template itself, so anyone who can read the template has the secret. NoEcho also does not mask anything you place in the template's Metadata section, a resource's Metadata attribute, or Outputs, so a NoEcho value referenced there is exposed. Treat NoEcho as display hygiene, never as secrets management.
The correct pattern is a dynamic reference: the template stores a pointer, and CloudFormation resolves the value at deployment time. The three forms differ in ways that matter:
| Reference | Resolves | Encrypted at rest | Built-in rotation |
|---|---|---|---|
{{resolve:secretsmanager:MySecret:SecretString:password}} | A Secrets Manager secret (or one JSON key of it) | Yes (KMS) | Yes — scheduled rotation |
{{resolve:ssm-secure:ParameterName}} | A Parameter Store SecureString, for supported resource properties only | Yes (KMS) | No — Parameter Store has no rotation; you build your own |
{{resolve:ssm:ParameterName}} | A plain String/StringList parameter | No — the value is unencrypted | No |
With either secure form, the secret never appears in the template, the repository, or the stack's stored parameters, and access to it is governed separately by IAM, the key policy, and (for Secrets Manager) the secret's resource policy. When the requirement includes scheduled rotation without a custom process, Secrets Manager is the store that provides it. Better still, resources such as AWS::RDS::DBInstance can have RDS manage the master password in Secrets Manager, so no human ever supplies one.
Service Catalog, AWS RAM, and Firewall Manager: sharing and central enforcement
Three services handle cross-account sharing and central enforcement, and each shares a different thing: Service Catalog shares approved templates that others launch, AWS RAM shares live resources that others use, and Firewall Manager pushes and maintains network protections everywhere.
AWS Service Catalog
Service Catalog packages approved templates (CloudFormation or Terraform) as products in portfolios, shared across the organization so one security-owned catalog serves every account. Constraints attached to a product in a portfolio shape how it launches:
| Constraint | What it controls |
|---|---|
| Launch | The IAM role Service Catalog assumes to provision the product, so end users need only Service Catalog permissions, never the underlying EC2/IAM/RDS permissions |
| Template | Which parameter values users may choose (instance types, CIDR ranges) |
| StackSet | Deploys the product as a stack set: target accounts, Regions, and the StackSet administration/execution roles. It does not set the role end users launch with |
| Tag update | Whether users may change tags on resources after provisioning |
| Notification | An SNS topic that receives stack events for provisioned products |
A launch constraint can name a role ARN, but an ARN pins one account. For a portfolio shared to many accounts, set LocalRoleName instead: Service Catalog uses the role with that name in the account where the user launches, so a role of the same name deployed to every spoke (typically by a stack set) makes one constraint work everywhere. Control Tower's Account Factory is itself built on Service Catalog.
AWS Resource Access Manager (RAM)
RAM shares live resources with specific accounts, OUs, or the whole organization, so one owner manages a resource that many accounts use instead of each account running a copy. Sharing with the whole organization or with OUs requires turning on RAM sharing with AWS Organizations (which also removes the invitation step); individual accounts can be shared with by invitation without it; some resource types can also be shared with external accounts, but subnets only within the organization. The owner always keeps control:
| Shared resource | What participants get | What stays with the owner |
|---|---|---|
| VPC subnets | Launch their own instances, databases, and other resources into the shared subnets (one VPC, no copy per account); manage their own resources and security groups | The VPC, its route tables, network ACLs, gateways, and other accounts' resources — participants cannot modify them |
| Transit gateway | Each account keeps its own VPCs and route tables and creates attachments to the shared gateway | The transit gateway's route tables — which attachments can reach each other, and whether traffic passes a central inspection VPC |
| Route 53 Resolver rules | Associate the rules with their VPCs and use them | The rules themselves — one owner, one copy, changes apply everywhere |
That last row is the general point: when a resource must stay single-owner, share it with RAM; a stack set or Service Catalog product would instead create an independent copy in each account that drifts on its own.
AWS Firewall Manager
Firewall Manager is central enforcement for network protections. Its prerequisites are an organization with all features enabled, a Firewall Manager administrator account (set from the management account; can be a delegated member account), and AWS Config enabled in every in-scope account and Region, because Firewall Manager detects resources and noncompliance through Config. Policies are scoped to accounts/OUs and resource types, automatically cover new accounts and new resources, and with automatic remediation on, fix noncompliance instead of only reporting it. Policy types:
- AWS WAF — applies a web ACL / rule groups to ALBs, API Gateway stages, CloudFront and similar resources; auto remediation associates it with new in-scope resources and restores it if removed.
- Security group, in three kinds: common (applies a primary security group to in-scope resources), content audit (checks the rules in teams' own security groups against a reference group or managed rule list, and with remediation removes noncompliant rules — e.g. world-open admin ports — without taking over the groups themselves), and usage audit (finds and cleans up unused or redundant groups).
- Shield Advanced (requires a Shield Advanced subscription), Network Firewall, Route 53 Resolver DNS Firewall, network ACL, and third-party firewall policies.
A stack set deploys what its template declares at deployment time; it does not keep watching for in-scope resources that appear afterwards or for protections that are later changed. Firewall Manager evaluates its policies continuously across the organization and, with automatic remediation on, applies or reapplies the protection — continuous, org-wide enforcement is what it adds over a one-time deployment.
Choosing the mechanism — and tagging at deploy time
Four services in this lesson all "deploy things to many accounts"; they differ in what is standardized and who initiates:
| Mechanism | What it standardizes | Initiated by | New accounts | Fits when |
|---|---|---|---|---|
| StackSets (service-managed) | A stack deployed into every target account | Central admin, pushed to OUs | Auto-deployment on OU join | Every account must have the same baseline resources |
| Service Catalog | Approved products teams launch on demand | End user, self-service | Portfolio shared org-wide | Teams provision vetted stacks without the underlying permissions |
| Control Tower Account Factory | The account itself, vended pre-governed | Central admin or delegated requester | Baseline applied at creation | New accounts must start compliant |
| Firewall Manager | Network protections (WAF, security groups, Shield, Network Firewall, DNS Firewall) | Central admin policy, continuously enforced | Covered automatically | Protections must exist and stay attached on every in-scope resource |
| AWS RAM | Nothing is copied — one live resource is shared | Resource owner | Share to an OU or the organization | A resource must stay single-owner but be usable by many accounts |
Tagging is part of deploy-time consistency too: bake required tags into StackSet templates and Service Catalog products so correct tags are the default, then enforce and group them as described in the next section.
Least-privilege pipelines and a troubleshooting walkthrough
The pipeline that deploys infrastructure is itself infrastructure to secure: the CI/CD system should hold almost nothing and assume a scoped deploy role per environment — a cross-account role in each target account (dev, staging, prod), scoped to the resource types its templates create. Production adds a manual approval gate on the change set before execute.
External CI systems should authenticate with OIDC federation: the CI provider issues a short-lived token that an IAM OIDC identity provider trusts, and the pipeline assumes a role with no long-term access keys stored anywhere. Environment isolation lives in each role's trust policy: condition it on the token's sub claim (and aud) so only that environment's pipeline — a specific repository, branch, or deployment environment — can assume it. If the trust condition is broad — matching every repository or workflow the CI provider issues tokens for — any of those pipelines can assume the role, so a compromise in a lower environment becomes a production compromise. IAM Roles Anywhere is the alternative for workloads outside AWS that can hold an X.509 certificate: it needs a certificate authority registered as a trust anchor, so it fits where certificate issuance is already managed; where the CI system issues OIDC tokens, web identity federation needs no certificates to distribute or rotate. (The federation mechanics live in Domain 4.)
Remember which policies can stop a deploy role that "has every permission it needs." SCPs cap what principals in member accounts can do, and resource control policies (RCPs) cap what can be done to resources in member accounts; neither ever grants anything — they only set a maximum, so the deploy role still needs an allow from its own policies (or a resource policy), and anything outside the SCP or RCP limit is refused regardless. SCPs and RCPs do not apply to the management account. Artifact integrity closes the loop: the template that was reviewed and scanned must be the one that deploys, so pipeline stages consume immutable, access-controlled artifacts rather than re-fetching from a mutable branch.
Walkthrough. A baseline stack set reports FAILED for one member account while the others succeed. Start with the account-specific causes. Under self-managed permissions, the execution role is missing from that account or its trust policy does not name the administration role; StackSets then cannot get into the account at all, so none of the work the operation should have done appears in that account's activity history. Recreate or fix the role and retry the instance. If the account's CloudTrail instead records the template's API calls being refused with AccessDenied although the execution role's own policies permit them, look above the role: a guardrail attached to the account or its OU (an SCP, or an RCP on the target resource) is capping it; adjust the guardrail exemption or the template. A cause that explains one failing account has to be specific to that account; operation preferences such as concurrency and failure tolerance govern the whole rollout, so they never single out one account. Later, drift detection shows the account's Config recorder MODIFIED: drift names the change, CloudTrail names the actor; re-apply the template and close the path with an SCP denying recorder changes except by the deploy role, so the gap is shut, not just repaired.
Tip. Task 6.2 scenarios turn on knowing the scope and limits of each mechanism. Expect to separate StackSets (push a baseline, service-managed for future accounts, management account excluded) from Service Catalog (self-service under a launch role), AWS RAM (one owner-controlled resource) and Firewall Manager (continuous network policy enforcement); to place cfn-lint, Guard, Hooks, change sets and Config before, during or after deployment; to rank secret handling (dynamic references over NoEcho, Secrets Manager for rotation, protected Terraform state); to distinguish tag policies, SCP tag conditions, Resource Groups and cost allocation tags; and to troubleshoot a stack set that fails in one account, or drift that drift detection does not report.
- Peer review, attribution, and revert together need source control with pull requests; a versioned S3 bucket gives only the revert.
- Drift detection compares only properties explicitly set in the template or parameters, so declare security-relevant settings; CloudTrail, not drift, tells you who changed something.
- Stack policies govern CloudFormation updates only; stopping direct API edits needs an SCP deny with an aws:PrincipalArn exception for the deploy role. Scope iam:PassRole to the specific service role ARN.
- Service-managed StackSets with automatic deployment cover future accounts in targeted OUs, but never deploy to the management account; trusted access applies only to service-managed permissions, and operation preferences control rollout, not per-account failures.
- cfn-lint checks CloudFormation validity; Guard checks your policy against any JSON/YAML, including Terraform plan JSON; Hooks enforce in the account at deploy time; Config rules detect after the fact.
- NoEcho only masks output and never covers Metadata or Outputs; use secretsmanager or ssm-secure dynamic references — only Secrets Manager rotates, and a plain ssm reference resolves an unencrypted value.
- Terraform state holds secrets in plaintext: encrypt it with KMS and restrict reads to the deploy role; locking and versioning protect integrity, not confidentiality.
- Tag policies standardize keys and values but do not block untagged creation; an SCP denying the create action when aws:RequestTag is null does. Resource groups are Regional and AND their tag keys; cost allocation tags affect billing only.
- Service Catalog launch constraints (LocalRoleName for shared portfolios) remove underlying permissions from users; RAM shares one owner-controlled resource; Firewall Manager needs AWS Config and continuously enforces WAF and security group policies.
Frequently asked questions
What is the difference between CloudFormation StackSets and AWS Service Catalog?
They standardize different things in different directions. StackSets push one template from a central administrator into many accounts and Regions — the mechanism for a mandatory security baseline, especially with service-managed permissions and automatic deployment covering accounts that join targeted OUs later. Service Catalog is pull, not push: it publishes approved templates as products that end users launch on demand under a launch role, without holding the underlying resource permissions. A baseline everyone must have suits StackSets; a golden path teams choose from suits Service Catalog.
How do you keep secrets out of CloudFormation templates?
Use dynamic references so the template stores a pointer instead of a value: {{resolve:secretsmanager:...}} pulls from AWS Secrets Manager at deployment time, and {{resolve:ssm-secure:...}} pulls a Parameter Store SecureString for supported properties. Only Secrets Manager provides scheduled rotation; Parameter Store has none, and a plain {{resolve:ssm:...}} reference resolves an unencrypted String. Resources like RDS can also manage their master password in Secrets Manager. The NoEcho attribute is not a substitute: it masks output only, a NoEcho default still sits in the template, and Metadata and Outputs are never masked.
What does CloudFormation drift detection check, and what does it miss?
Drift detection compares a stack's deployed resources with the template and reports each as IN_SYNC, MODIFIED, DELETED, or NOT_CHECKED for unsupported resource types. It compares only property values explicitly set in the template or its parameters, so a change to a property the template never declares goes unnoticed and the resource stays IN_SYNC. It also does not say who made a change; the CloudTrail event for the modifying API call gives the principal and source IP address. Declare security-relevant properties explicitly, and block out-of-band edits with an SCP.
What is the difference between cfn-lint, CloudFormation Guard, and CloudFormation Hooks?
cfn-lint checks that a CloudFormation template is valid against the resource specification and best-practice rules; it knows nothing about your security policy and reads only CloudFormation. CloudFormation Guard evaluates your own policy rules against any JSON or YAML document — templates, Terraform plan JSON, other configuration — and is typically run in CI to fail a build. CloudFormation Hooks run inside the account when CloudFormation provisions resources, so they catch deployments that bypass the pipeline. Hooks can be written in Guard.
Why would a StackSet deployment fail in only one member account?
The cause is almost always in that account. Under self-managed permissions, the execution role may be missing or may not trust the administration role; StackSets then cannot enter the account, so the operation leaves no trace of its work there. Under either model, a guardrail on the account or its OU, such as an SCP, can deny an action the template needs; that shows up as AccessDenied events in the account's CloudTrail even though the execution role's policy allows the action. Operation preferences such as maximum concurrent accounts or failure tolerance apply to the whole rollout, so they cannot single out one account. Fix the role or guardrail and retry the instance.
How does a Service Catalog launch constraint improve security?
A launch constraint attaches a launch role to a product; when a user provisions it, Service Catalog assumes that role to create the resources, so the user needs only Service Catalog permissions and never holds EC2, IAM, or other underlying permissions. For a portfolio shared to many accounts, set LocalRoleName instead of a role ARN so Service Catalog uses the same-named role in whichever account the user launches from. Template constraints restrict parameter values; StackSet constraints set target accounts, Regions, and StackSet roles rather than the launch role.
How do you secure Terraform state on AWS?
Treat the state file as a secret store, because Terraform writes resource attributes — including passwords — to state in plaintext; marking a variable sensitive only hides it from command output. Keep remote state in an S3 backend encrypted with a KMS key and restrict both the bucket and the key to the pipeline deploy role. Use state locking and bucket Versioning for integrity and recovery; they do not protect confidentiality. Where possible keep secrets out of state with service-managed passwords or Terraform ephemeral values and write-only arguments.
Do AWS Organizations tag policies stop people creating untagged resources?
No. A tag policy standardizes tag keys and allowed values, reports noncompliance, and when enforced refuses noncompliant tagging operations, but resources created with no tags are not evaluated. To require a tag at creation from any client, attach an SCP that denies the create action (for example ec2:RunInstances) when aws:RequestTag for the key is null. The tag policy required-tag-key setting is enforced only by IaC tools that validate against it, such as a CloudFormation hook, so console and CLI launches are not blocked.
Source
This lesson covers the "Security Foundations and Governance" domain of the official SCS-C03 exam guide. Vendors revise their guides — check the source for the current version.
- AWS Certified Security – Specialty (SCS-C03) exam guide — Amazon Web Services
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.