Skip to main content

UNDERSTANDING AWS IAM AUTOMATION IN SWAMP

AWS IAM is a common place to start automating, and a risky one. A trust policy, an attached policy or an access key controls who can do what in an account. Mistakes in IAM tend to stay unnoticed until someone uses them. Swamp's IAM extensions put that work into behaviors whose definitions live in the repository and whose results are stored as data. This page covers how that mapping works and the decisions it leaves to the team.

One behavior type per IAM resource

The @swamp/aws/iam extension has one behavior type for each kind of IAM resource. The types are role, user, group, managed-policy, role-policy, user-policy, group-policy, instance-profile, oidcprovider, samlprovider, server-certificate, service-linked-role and virtual-mfadevice. A behavior is one configured instance of a type. Its global arguments hold the same properties you would set in a CloudFormation AWS::IAM::Role or AWS::IAM::User resource, and the names match, such as AssumeRolePolicyDocument, ManagedPolicyArns and PermissionsBoundary. Teams already used to CloudFormation will recognise them. Behaviors, Types, and Methods explains the general relationship between a type and its behaviors.

Methods that change AWS and methods that read it

Every type has the same methods. create, update and delete change the account. get, sync and, on most types, list only read it. Each run writes the type's resource state, for example IAM Role resource state, into the repository's data layer. This means a behavior definition is not a desired-state file that Swamp keeps enforcing in the background. It records what was asked for, and the resource state records what AWS reported the last time a method ran. Running sync regularly keeps that record current, which matters for IAM because people also change it through the console and other tools.

Choosing who owns a policy

IAM lets you express the same permission in several ways, and the extension supports all of them. A role, user or group can carry inline policies in its own Policies argument and attach managed policies through ManagedPolicyArns. The same attachments can also be separate behaviors: a role-policy, user-policy or group-policy for an inline policy, or a managed-policy that lists its Roles, Users and Groups.

Either approach works. The risk is using both for the same policy. If a role's Policies and a separate role-policy behavior both describe the same inline policy, each update overwrites the other's version, and which one wins depends on which ran last. A useful rule is one owner per policy. Some teams keep everything on the principal, so that one behavior shows everything a role can do. Others make each policy its own behavior, so that a policy shared by several roles is defined once. The separate policy types, along with oidcprovider, samlprovider, service-linked-role and virtual-mfadevice, take a required name argument because they are built for the factory pattern, where one definition creates many instances.

Credentials and secrets

Each type accepts accessKeyId, secretAccessKey and sessionToken. When they are unset, the AWS environment variables and profile are used. That suits a developer laptop with SSO, or a runner with an attached role. When the credentials have to come from somewhere specific, wire the arguments with ${{ vault.get('<vault-name>', '<key>') }} so that the values are resolved at run time and redacted from output, as shown in Storing Secrets. Auto-Refresh Cloud Credentials covers keeping short-lived credentials current.

Some IAM arguments are secrets themselves. These include the password in a user's LoginProfile, the PrivateKey of a server certificate and the AddPrivateKey of a SAML provider. They belong in a vault for the same reason the credentials do.

Access keys, which @swamp/aws/iam does not cover

CloudFormation-style resource types have no good way to represent an access key, because the secret half exists only once, when the key is created. @sntxrr/aws-access-key handles that moment. It writes the new key straight into a named vault item and reads it back to confirm the write. If the vault write fails, it rolls the key back, so a key never exists in AWS without a copy in the vault. Deactivation and deletion are separate methods, and delete will not remove an Active key. The result is a rotation you can undo until the last step: if something still depends on the old key, it fails while that key is only deactivated, and it can still be restored. Rotate an IAM user access key into a Swamp vault walks through the sequence.

Observing instead of managing

Not every account should be managed from a given repository. Security teams often need to see IAM across dozens of accounts that other teams own. @webframp/aws/iam scans a list of AWS profiles and records roles, users, customer-managed policies and a cross-account trust map. @jentz/aws-iam-role-audit checks named integration roles against expected policies, trust principals and external IDs. It also records a scan error for each account it could not assess, instead of stopping the sweep. Both extensions only read IAM. The trade-off is clear. They can never repair what they find, but they can run under read-only profiles in accounts where write access would be inappropriate. Many teams combine the two: @swamp/aws/iam for the accounts they own, and the read-only extensions everywhere else.

Ordering and repeatability

IAM resources depend on each other. A role must exist before an instance profile can hold it, before a role-policy can be embedded in it, and before a managed policy can list it in Roles. Running these behaviors one at a time works, but the order lives only in the operator's head. A workflow writes that order down as steps. Because a failed run can be retried, it is worth designing the steps so that running them twice does no harm. The Workflow Execution Model describes how steps are scheduled, and Make Workflows Idempotent covers the repeatable part.