Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 276 additions & 0 deletions docs/guides/migrate-pak-to-service-account.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
---
page_title: "Migration Guide: Programmatic API Keys (PAKs) to Service Accounts (SAs)"
---

# Migration Guide: Programmatic API Keys (PAKs) to Service Accounts (SAs)

## Overview

This guide explains how to migrate from Programmatic API Key (PAK) resources to Service Account (SA) resources.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i'd add a clarification like to use them as authentication mechanism, see more info in: https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/provider-configuration


**Note:** Migration to Service Accounts is **not required**. If you are currently using API Key resources, you may continue to do so. This guide is for users who wish to adopt Service Accounts for greater security or best practices, but existing PAK configurations will continue to work and be supported.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Note:** Migration to Service Accounts is **not required**. If you are currently using API Key resources, you may continue to do so. This guide is for users who wish to adopt Service Accounts for greater security or best practices, but existing PAK configurations will continue to work and be supported.
**Note:** Migration to Service Accounts is recommended but **not required**. If you are currently using API Key resources, you may continue to do so. This guide is for users who wish to adopt Service Accounts for greater security or best practices, but existing PAK configurations will continue to work and be supported.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated! thank you!


## Before You Begin

- **Backup your Terraform state file** before making any changes.
- **Test the process in a non-production environment** if possible.
- **Secrets handling** - Managing Service Accounts with Terraform will expose sensitive organizational secrets in Terraform's state. We suggest following [Terraform's best practices](https://developer.hashicorp.com/terraform/language/state/sensitive-data).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion for smoother reading:

Suggested change
- **Secrets handling** - Managing Service Accounts with Terraform will expose sensitive organizational secrets in Terraform's state. We suggest following [Terraform's best practices](https://developer.hashicorp.com/terraform/language/state/sensitive-data).
- Managing Service Accounts with Terraform **exposes sensitive organizational secrets** in Terraform's state. We suggest following [Terraform's best practices](https://developer.hashicorp.com/terraform/language/state/sensitive-data).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated! thank you!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you missed the removal of **Secrets handling** - .
Can you also update this in the secrets rotation guide to keep consistency? Ty!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated. thank you! will update that guide in the next doc PR (soon) as I would need to rebase this one and it is not worth the effort.



---

<details>
<summary><span style="font-size:1.4em; font-weight:bold;">Organization-Level Migration</span></summary>

## Organization-Level API Keys to Service Accounts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Would it make sense to use L3 headings for everything within this section? (Right sub headings are all L2)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated! thank you!


**Objective**: Migrate from organization-level PAK resources (`mongodbatlas_api_key`, `mongodbatlas_api_key_project_assignment`, `mongodbatlas_access_list_api_key`) to organization-level Service Account resources (`mongodbatlas_service_account`, `mongodbatlas_service_account_project_assignment`, `mongodbatlas_service_account_access_list_entry`).

## Resource Mapping
The following table shows the mapping between organization-level PAK resources and their Service Account equivalents:

| PAK Resource | Service Account Resource | Notes |
|--------------|-------------------------|-------|
| `mongodbatlas_api_key` | `mongodbatlas_service_account` | API key / Service Account |
| `mongodbatlas_api_key_project_assignment` | `mongodbatlas_service_account_project_assignment` | Project assignment |
| `mongodbatlas_access_list_api_key` | `mongodbatlas_service_account_access_list_entry` | IP access list entry |

---

## Migration Steps

For complete working examples, see the [organization-level migration example](https://github.com/mongodb/terraform-provider-mongodbatlas/tree/master/examples/migrate_pak_to_service_account/org_level).

### Step 1: Initial Configuration - PAK Resources Only

Original configuration with PAK resources:

```terraform
resource "mongodbatlas_api_key" "this" {
org_id = var.org_id
description = "Example API Key"
role_names = ["ORG_MEMBER"]
}

# Project assignment for the API Key
resource "mongodbatlas_api_key_project_assignment" "this" {
project_id = var.project_id
api_key_id = mongodbatlas_api_key.this.api_key_id
roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
}

# IP Access List entry for the API Key
resource "mongodbatlas_access_list_api_key" "this" {
org_id = var.org_id
api_key_id = mongodbatlas_api_key.this.api_key_id
cidr_block = "192.168.1.100/32"
# Alternative: ip_address = "192.168.1.100"
}
```

### Step 2: Intermediate State - Add Service Account Resources Alongside Existing PAK Resources

Add the Service Account resources to your configuration while keeping the existing PAK resources. This allows both authentication methods to work simultaneously, enabling you to test Service Accounts before removing PAKs.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the statement "allows both authentication methods to work simultaneously" is not true, both SA and PAK exist simultaneously, but are not used simultaneously.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking on the case of using SA/PAKs as the authentication method for the MongoDB Atlas Terraform Provider, but in the cases of other applications/tools, the statement can be true. Feel free to ignore the previous comment


```terraform
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_MEMBER"]
secret_expires_after_hours = 2160 # 90 days
}

resource "mongodbatlas_service_account_project_assignment" "this" {
project_id = var.project_id
client_id = mongodbatlas_service_account.this.client_id
roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
}

resource "mongodbatlas_service_account_access_list_entry" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
cidr_block = "192.168.1.100/32"
# Alternative: ip_address = "192.168.1.100"
}

output "service_account_first_secret" {
description = "The secret value of the first secret created with the service account. Only available after initial creation."
value = try(mongodbatlas_service_account.this.secrets[0].secret, null)
sensitive = true
}
```

**Apply and test:**

1. Run `terraform plan` to review the changes.
2. Run `terraform apply` to create the Service Account resources.
3. Retrieve and securely store the `service_account_first_secret` value (**warning**: this prints the secret to your terminal):

```bash
terraform output -raw service_account_first_secret
```

4. Test your Service Account in your applications and verify that both PAK and SA authentication methods work correctly.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we explain how that would look like in the case of switching the auth method of the TF provider from PAK to SA? something similar to https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/provider-configuration#programmatic-access-key warning block where we say Update your provider attributes or environment variables to use SA credentials instead of PAK credentials, then run terraform plan to verify everything works correctly.

I say this because the guide is generic in terms of the application that uses the PAK/SA, but I think the main use case we want to enable is the TF provider authentication

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A previous iteration mentioned this. I suggested to remove it to avoid confusion and mention only "your applications".
For example: If the user were to change the provider credentials in this same config, they would not have permissions to re-apply as the SA has only ORG_MEMBER role.

I think it makes sense for the credentials used for provider authentication in a particular configuration to be managed outside of that same configuration.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should cover the end to end case of using the new SA resource sand switching from PAK to SA as the auth method, since it's the main problem we are trying to solve by implementing these resources in TF

Copy link
Collaborator

@manupedrozo manupedrozo Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am on board on including a link to the SA authentication registry page to reference how it is used but I would be careful to add it as a step here given the previous comment:

I think it makes sense for the credentials used for provider authentication in a particular configuration to be managed outside of that same configuration.

Do we know of PAK users authenticating with a PAK that is managed within the same configuration? I don't think we should be recommending this approach (can lock yourself out).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not saying to recommend the approach of using PAKs created in a configuration as the auth mechanism within the same configuration, but I would like to have in this guide the case of switching from PAK to SA as the auth mechanism of the TF provider, which is the main use case we want to enable, even if the SA/PAK is created in one configuration and used as auth in another. Hope that makes sense

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that makes sense. @filipcirtog can you mention this in the guide and include a link to the provider configuration guide with SA?
https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/provider-configuration

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestions. I have added some notes on this. Please let me know WDY.

5. Re-run `terraform plan` to ensure you have no unexpected changes: `No changes. Your infrastructure matches the configuration.`

### Step 3: Final State - Remove PAK Resources, SA Resources Only

Once you have verified that the Service Account works correctly, remove the PAK resources from your configuration:

```terraform
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_MEMBER"]
secret_expires_after_hours = 2160 # 90 days
}

resource "mongodbatlas_service_account_project_assignment" "this" {
project_id = var.project_id
client_id = mongodbatlas_service_account.this.client_id
roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
}

resource "mongodbatlas_service_account_access_list_entry" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
cidr_block = "192.168.1.100/32"
}
```

1. Run `terraform plan` to verify:
- PAK resources are planned for destruction
- Only the Service Account resources remain
- No unexpected changes

2. Run `terraform apply` to finalize the migration. This will delete the PAK resources from Atlas.

3. Verify that your applications and infrastructure continue to work with Service Accounts.

4. Re-run `terraform plan` to ensure you have no planned changes: `No changes. Your infrastructure matches the configuration.`


</details>

---

<details>
<summary><span style="font-size:1.4em; font-weight:bold;">Project-Level Migration</span></summary>

## Project-Level API Keys to Service Accounts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume same comments as for Org-level steps

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! Thank you!


**Objective**: Migrate from project-level PAK resources (`mongodbatlas_project_api_key`, `mongodbatlas_access_list_api_key`) to project-level Service Account resources (`mongodbatlas_project_service_account`, `mongodbatlas_project_service_account_access_list_entry`).

**Important:** Organization-level resources (`mongodbatlas_service_account`) are the recommended approach. Project-level resources (`mongodbatlas_project_service_account`) should only be used if you do not have organization-level permissions to manage service accounts. Otherwise, use the [Organization-Level Migration](#organization-level-api-keys-to-service-accounts) approach.

## Resource Mapping

The following table shows the mapping between project-level PAK resources and their Service Account equivalents:

| PAK Resource | Service Account Resource | Notes |
|--------------|-------------------------|-------|
| `mongodbatlas_project_api_key` | `mongodbatlas_project_service_account` | Project-level API key / Service Account (use only if you don't have org-level permissions) |
| `mongodbatlas_access_list_api_key` | `mongodbatlas_project_service_account_access_list_entry` | IP access list entry for project-level service account |

**Important:** Organization-level resources (`mongodbatlas_service_account`) are the recommended approach. Project-level resources (`mongodbatlas_project_service_account`) should only be used if you do not have organization-level permissions to manage service accounts.

---

## Migration Steps

For complete working examples, see the [project-level migration example](https://github.com/mongodb/terraform-provider-mongodbatlas/tree/master/examples/migrate_pak_to_service_account/project_level).

### Step 1: Initial Configuration - PAK Resources Only

Original configuration with PAK resources:

```terraform
resource "mongodbatlas_project_api_key" "this" {
description = "Example Project API Key"
project_assignment {
project_id = var.project_id
role_names = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
}
}

# IP Access List entry for the API Key
resource "mongodbatlas_access_list_api_key" "this" {
org_id = var.org_id
api_key_id = mongodbatlas_project_api_key.this.api_key_id
cidr_block = "192.168.1.100/32"
# Alternative: ip_address = "192.168.1.100"
}
```

### Step 2: Intermediate State - Add Service Account Resources Alongside Existing PAK Resources

Add the Service Account resources to your configuration while keeping the existing PAK resources. This allows both authentication methods to work simultaneously, enabling you to test Service Accounts before removing PAKs.

```terraform
resource "mongodbatlas_project_service_account" "this" {
project_id = var.project_id
name = "example-project-service-account"
description = "Example Project Service Account"
roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}

resource "mongodbatlas_project_service_account_access_list_entry" "this" {
project_id = var.project_id
client_id = mongodbatlas_project_service_account.this.client_id
cidr_block = "192.168.1.100/32"
# Alternative: ip_address = "192.168.1.100"
}

output "project_service_account_first_secret" {
description = "The secret value of the first secret created with the project service account. Only available after initial creation."
value = try(mongodbatlas_project_service_account.this.secrets[0].secret, null)
sensitive = true
}
```

**Apply and test:**

1. Run `terraform plan` to review the changes.
2. Run `terraform apply` to create the Service Account resource.
3. Retrieve and securely store the `project_service_account_first_secret` value (**warning**: this prints the secret to your terminal):

```bash
terraform output -raw project_service_account_first_secret
```

4. Test your Service Account in your applications and verify that both PAK and SA authentication methods work correctly.
5. Re-run `terraform plan` to ensure you have no unexpected changes: `No changes. Your infrastructure matches the configuration.`

### Step 3: Final State - Remove PAK Resources, SA Resources Only

Once you have verified that the Service Account works correctly, remove the PAK resources from your configuration:

```terraform
resource "mongodbatlas_project_service_account" "this" {
project_id = var.project_id
name = "example-project-service-account"
description = "Example Project Service Account"
roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}

resource "mongodbatlas_project_service_account_access_list_entry" "this" {
project_id = var.project_id
client_id = mongodbatlas_project_service_account.this.client_id
cidr_block = "192.168.1.100/32"
}
```

1. Run `terraform plan` to verify:
- PAK resources are planned for destruction
- Only the Service Account resources remain
- No unexpected changes

2. Run `terraform apply` to finalize the migration. This will delete the PAK resource from Atlas.

3. Verify that your applications and infrastructure continue to work with Service Accounts.

4. Re-run `terraform plan` to ensure you have no planned changes: `No changes. Your infrastructure matches the configuration.`
63 changes: 63 additions & 0 deletions examples/migrate_pak_to_service_account/org_level/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Migration Example: Organization-Level API Keys to Service Accounts

This example demonstrates how to migrate from organization-level Programmatic API Key (PAK) resources to Service Account (SA) resources.

## Migration Phases

### v1: Initial State (PAK Resources)
Shows the original configuration using PAK resources:
- `mongodbatlas_api_key` for organization-level API key
- `mongodbatlas_api_key_project_assignment` for project assignment
- `mongodbatlas_access_list_api_key` for IP access list entry

### v2: Migration Phase (Both PAK and SA Resources)
Demonstrates the migration approach:
- Adds Service Account resources alongside existing PAK resources
- Includes output to capture the Service Account secret
- Allows testing Service Accounts before removing PAKs

### v3: Final State (SA Resources Only)
Clean final configuration using only:
- `mongodbatlas_service_account` for organization-level service account
- `mongodbatlas_service_account_project_assignment` for project assignment
- `mongodbatlas_service_account_access_list_entry` for IP access list entry

## Usage

1. Start with v1 to understand the original setup
2. Apply v2 configuration to add Service Account resources
3. Retrieve and securely store the Service Account secret from the output
4. Verify that both PAK and SA authentication methods work correctly
5. Apply v3 configuration for the final clean state

## Prerequisites

- MongoDB Atlas Terraform Provider with Service Account support
- Valid MongoDB Atlas organization and project IDs
- Appropriate permissions to manage API keys and Service Accounts

## Variables

Set these variables for all versions:

```terraform
atlas_client_id = "<ATLAS_CLIENT_ID>" # Optional, can use env vars
atlas_client_secret = "<ATLAS_CLIENT_SECRET>" # Optional, can use env vars
org_id = "your-organization-id"
project_id = "your-project-id"
org_roles = ["ORG_MEMBER"]
project_roles = ["GROUP_READ_ONLY", "GROUP_DATA_ACCESS_READ_ONLY"]
cidr_block = "192.168.1.100/32"
```

For v2, also set:
```terraform
service_account_name = "example-service-account" # Optional
secret_expires_after_hours = 2160 # Optional, 90 days default
```

Alternatively, set environment variables:
```bash
export MONGODB_ATLAS_CLIENT_ID="<ATLAS_CLIENT_ID>"
export MONGODB_ATLAS_CLIENT_SECRET="<ATLAS_CLIENT_SECRET>"
```
22 changes: 22 additions & 0 deletions examples/migrate_pak_to_service_account/org_level/v1/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
############################################################
# v1: Initial State - PAK Resources Only
############################################################

resource "mongodbatlas_api_key" "this" {
org_id = var.org_id
description = "Description for the Organization API Key"
role_names = var.org_roles
}

resource "mongodbatlas_api_key_project_assignment" "this" {
project_id = var.project_id
api_key_id = mongodbatlas_api_key.this.api_key_id
roles = var.project_roles
}

resource "mongodbatlas_access_list_api_key" "this" {
org_id = var.org_id
api_key_id = mongodbatlas_api_key.this.api_key_id
cidr_block = var.cidr_block
# Alternative: ip_address = "192.168.1.100"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
provider "mongodbatlas" {
client_id = var.atlas_client_id
client_secret = var.atlas_client_secret
}
Loading
Loading