# Centralizing Developer Cloud Access

This guide shows you how to define your organization's cloud authentication configuration **once**, in
a private central repository, and have it "just work" for every developer who clones any project repo
that imports it — no manually copy-pasted `~/.aws/config` profile blocks, no per-developer setup docs to
keep in sync, no risk of someone hand-typing the wrong account ID.

The pattern is the same regardless of cloud — Atmos supports AWS, Azure, and GCP as first-class auth
providers. The example below shows all three side by side.

If you've only used Atmos in Terraform repositories, using it here may be unexpected. But Atmos isn't a
Terraform wrapper—it's a general-purpose task runner and configuration platform with best-in-class support
for Terraform. We recommend using it in every repository to give teams one consistent way to manage
commands, toolchains, authentication, secrets, and workflows, whether that repository contains
infrastructure code or not.

## The Problem

A common way teams distribute AWS SSO access today is a `[profile]` block passed around in Slack or a
wiki page:

```ini
[profile corp-nonprod]
sso_start_url = https://d-xxxxxxxxxx.awsapps.com/start
sso_region = us-east-1
sso_account_id = 111111111111
sso_role_name = ReadOnlyAccess
```

This works, but it doesn't scale or govern well:

- Every developer's `~/.aws/config` is a hand-rolled, ungoverned copy — nothing catches
  copy-paste mistakes (and it costs your team time supporting it).
- Every new team member goes through the same onboarding song-and-dance, usually capped off with a
  Slack message asking how to configure their AWS credentials.
- Making any changes requires coordinating the updates across the entire team (unsustainable)
- There's no single source of truth to audit or version-control.
- If your organization is multi-cloud, this ad hoc pattern doesn't generalize — AWS profiles, `az login`
  conventions, and `gcloud` configurations all look different, so you end up maintaining a separate,
  differently-shaped solution per cloud instead of one consistent pattern.

## The Solution: One Central, Private Config Repo

Atmos lets you define your `auth:` configuration once in a private repository and `import:` it from the
`atmos.yaml` of every project repo. Developers only need to clone a project and run `atmos auth login` —
they never see or manage the SSO block themselves.

### 1. Create the central config repo

Create a private repository — for example `github.com/acme-corp/atmos-config` — and organize it by
cloud, then by the group of developers (or team) the file grants access to. A simple starting layout:

```
atmos-config/
├── aws/
│   └── developers/
│       └── auth.yaml
├── azure/
│   └── developers/
│       └── auth.yaml
└── gcp/
    └── developers/
        └── auth.yaml
```

The import syntax, private-repo access, and `atmos auth` workflow are identical across tabs — only the
path (`aws/`, `azure/`, or `gcp/`) and the `providers:`/`identities:` block inside `auth.yaml` change.
Use the path and block for the cloud you're setting up:

### AWS

```yaml
# aws/developers/auth.yaml (in the private acme-corp/atmos-config repo)
auth:
  providers:
    acme-sso:
      kind: aws/iam-identity-center
      region: us-east-1
      start_url: https://d-xxxxxxxxxx.awsapps.com/start

  identities:
    nonprod:
      kind: aws/permission-set
      default: true                # makes this the default identity
      via:
        provider: acme-sso
      principal:
        name: ReadOnlyAccess        # the SSO permission set / role name
        account:
          id: "111111111111"        # quote it — it's a string, not a number
```

### Azure

```yaml
# azure/developers/auth.yaml (in the private acme-corp/atmos-config repo)
auth:
  providers:
    azure-cli:
      kind: azure/cli

  identities:
    nonprod:
      kind: azure/subscription
      default: true
      via:
        provider: azure-cli
      principal:
        subscription_id: "00000000-0000-0000-0000-000000000000"
        location: eastus
```

### GCP

```yaml
# gcp/developers/auth.yaml (in the private acme-corp/atmos-config repo)
auth:
  providers:
    gcp-adc:
      kind: gcp/adc

  identities:
    nonprod:
      kind: gcp/service-account
      default: true
      via:
        provider: gcp-adc
      principal:
        service_account_email: terraform@acme-nonprod.iam.gserviceaccount.com
        project_id: acme-nonprod
```

Tag releases (e.g. `v1.0.0`) so downstream repos can pin to a known-good version instead of tracking a
moving branch. A repo that needs more than one cloud isn't limited to a single provider — it can import
multiple provider files side by side (e.g. `aws.yaml` and `azure.yaml`).

### 2. Import it from every project repo

In each project's own `atmos.yaml`, add a remote `import:` pointing at that file, pinned to a tag:

```yaml
# atmos.yaml (in any project repo)
import:
  - "git::https://github.com/acme-corp/atmos-config.git//aws/developers/auth.yaml?ref=v1.0.0"
```

That's the entire integration. Every project that adds this one line inherits the same providers and
identities — add an account or rotate a role name once in the central repo, tag a new release, and bump
the `ref` across projects (or leave them tracking a branch if you'd rather changes roll out immediately).

By default this import is re-resolved (a fresh shallow clone) on every single `atmos` command in the
project, not just auth commands — so every command depends on network reachability to the central repo.
Set `imports: { ttl: ... }` once in `atmos.yaml` to cache the clone across commands instead (see
[Caching Remote Imports](/cli/configuration/imports#caching-remote-imports)).

#### Import only what a project needs

A project doesn't have to pull in the entire org's `auth:` config just to get one identity. Extend the
same `<cloud>/<team>/auth.yaml` layout with more teams as they need their own identities:

```
atmos-config/
└── aws/
    ├── developers/auth.yaml   # broad, mostly-read access — imported by most repos
    ├── platform/auth.yaml     # platform team's more privileged identities
    └── data/auth.yaml         # data team's identities
```

A platform-team repo then imports only its own file, not the whole org's config:

```yaml
# atmos.yaml (in a platform-team project repo)
import:
  - "git::https://github.com/acme-corp/atmos-config.git//aws/platform/auth.yaml?ref=v1.0.0"
```

This composes the same way whether the split is by team, by environment (`nonprod/auth.yaml`/
`prod/auth.yaml`), or by cloud provider — you're not limited to one monolithic file imported everywhere.

### 3. Private repo access — no token setup needed

Because `atmos-config` is private, fetching it needs authentication. This `git::` import is cloned by
Git itself under the hood, and Atmos resolves a token for that clone in this order:

1. `ATMOS_PRO_GITHUB_TOKEN` environment variable (set automatically if you use Atmos Pro's GitHub
   integration)
2. `ATMOS_GITHUB_TOKEN` environment variable
3. `GITHUB_TOKEN` environment variable
4. `gh auth token`, if the [GitHub CLI](https://cli.github.com/) is installed and the developer has
   already run `gh auth login`

For most developers, step 4 means there's nothing to configure at all — `gh auth login` is already a
common onboarding step, and Atmos reuses that session automatically for this import too.

## Developer Workflow

With the import wired up, a new developer's entire setup is:

```bash
git clone git@github.com:acme-corp/some-project.git
cd some-project
atmos auth login          # auto-uses the default identity; pass a bare --identity to force a selector
```

Then, to actually use the credentials:

```bash
# Option A: isolated subshell — credential env vars are scoped to this child
# shell and go away when it exits (the underlying credential files on disk
# aren't revoked; they just expire on their own normal schedule)
atmos auth shell

# Option B: run one command with credentials injected
atmos auth exec -- aws s3 ls

# Option C: make credentials ambient in the current shell
eval $(atmos auth env)
```

`atmos auth env` is safe to add to `~/.zshrc`/`~/.bashrc` unconditionally — it only prints (and, via
`eval`, exports) environment variables from cached credentials; it never triggers a login prompt on its
own. A new shell starts in `$HOME`, though, not inside your project checkout, so Atmos won't find the
`atmos.yaml` (and its import) unless you point it there explicitly with `--chdir`:

```bash
eval $(atmos auth env --chdir=/path/to/your/checkout)
```

## What's Actually Zero-Setup (and What's Optional)

Install Atmos once; use it on every repo in the organization. For a private central repo, that also means
being authenticated with GitHub — most developers already are, via `gh auth login` (see
[step 3](#3-private-repo-access--no-token-setup-needed) above for the exact resolution order). From there:
clone a repo, run `atmos auth login`, then `atmos auth shell` or `atmos auth exec` — no profile block to
copy, no account ID to transcribe.

The one thing that isn't automatic is **ambient parity with a manually managed `[profile]` block** — a
bare `aws s3 ls`, or an IDE extension finding credentials, with no `atmos auth` wrapper involved at all.
Atmos deliberately doesn't write to `~/.aws/credentials`/`~/.aws/config`; credentials go to Atmos-managed
files instead, exposed only to processes that inherit `AWS_SHARED_CREDENTIALS_FILE`/`AWS_CONFIG_FILE`/
`AWS_PROFILE`. This is a security default, not an oversight — it avoids silently overwriting a
developer's own credentials, and it means a tool (or an AI agent) isn't automatically signed in to AWS
just by existing in your shell; it only gets credentials if something explicitly runs
`atmos auth exec`/`shell` or has `eval`'d `atmos auth env`.

Want that ambient parity anyway? Add `eval $(atmos auth env --chdir=...)` to your shell profile (see
[Developer Workflow](#developer-workflow)) — optional, and most repo-scoped work doesn't need it.

Either way, updates are centrally managed rather than re-broadcast: add an account or rotate a role once
in the central repo and tag a release. Projects tracking a branch pick up the change immediately; projects
pinned to a tag (the pattern this guide recommends) pick it up the next time they bump their `ref` — still
a one-line change in one file, not a wiki post and a round of developers hand-editing `~/.aws/config`.

## Troubleshooting

**`atmos auth login` doesn't find the identity** — a failed import is non-fatal by design: a typo'd `ref`,
an unreachable repo, or a private repo Git can't authenticate to all produce an empty `auth:` section and
exit `0`, not an error. Run `atmos auth list` first to confirm the import actually resolved anything. If
it's empty, look for a `WARN failed to resolve import` line in the command's output — Atmos logs it at
the default log level, so it shows up without setting `ATMOS_LOGS_LEVEL`. That line names the real reason
(bad ref, 403, DNS failure, etc.).

**That warning shows an authentication error** — confirm `gh auth status` shows an active session (or
that `ATMOS_PRO_GITHUB_TOKEN`/`ATMOS_GITHUB_TOKEN`/`GITHUB_TOKEN` is set — see
[step 3](#3-private-repo-access--no-token-setup-needed) for the full resolution order), and that the
account has read access to the central config repo.

**AWS CLI/Terraform outside of Atmos can't find credentials** — you're missing `eval $(atmos auth env)`
in your shell profile, or the tool was launched before you ran it in that shell session.

## See Also

- [Imports](/cli/configuration/imports) — remote import syntax and pinning
- [Auth Providers](/cli/configuration/auth/providers) — full provider configuration reference
- [`atmos auth` commands](/cli/commands/auth/usage) — login, shell, exec, env, whoami
