Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .changelog/4727.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
Comment thread
EspenAlbert marked this conversation as resolved.
resource/mongodbatlas_service_account: Adds `without_initial_secret` attribute to create a Service Account without generating an initial secret. `secret_expires_after_hours` is now optional when `without_initial_secret` is `true`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't project_service_account have the same change? Or was this not added on the API side?

```
42 changes: 20 additions & 22 deletions docs/data-sources/service_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,37 @@ subcategory: "Service Accounts"

## Example Usages
```terraform
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}
# Create a Service Account without an Atlas-generated secret, then create the first secret
# explicitly with mongodbatlas_service_account_secret so this configuration owns it.

data "mongodbatlas_service_account" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
without_initial_secret = true
}

data "mongodbatlas_service_accounts" "this" {
org_id = var.org_id
resource "mongodbatlas_service_account_secret" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
secret_expires_after_hours = 2160 # 90 days
}

output "service_account_client_id" {
value = mongodbatlas_service_account.this.client_id
description = "The Client ID of the Service Account. Use it with a secret to authenticate."
value = mongodbatlas_service_account.this.client_id
}

output "service_account_name" {
value = data.mongodbatlas_service_account.this.name
output "secret_id" {
description = "The ID of the Service Account secret."
value = mongodbatlas_service_account_secret.this.secret_id
}

output "service_account_first_secret" {
description = "The secret value of the first secret created with the Service Account. Available only immediately after initial creation."
value = try(mongodbatlas_service_account.this.secrets[0].secret, null)
output "secret" {
description = "The secret value for the Service Account. Returned only when the secret is created."
sensitive = true
}

output "service_accounts_results" {
value = data.mongodbatlas_service_accounts.this.results
value = mongodbatlas_service_account_secret.this.secret
}
```

Expand Down
42 changes: 20 additions & 22 deletions docs/data-sources/service_accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,37 @@ subcategory: "Service Accounts"

## Example Usages
```terraform
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}
# Create a Service Account without an Atlas-generated secret, then create the first secret
# explicitly with mongodbatlas_service_account_secret so this configuration owns it.

data "mongodbatlas_service_account" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
without_initial_secret = true
}

data "mongodbatlas_service_accounts" "this" {
org_id = var.org_id
resource "mongodbatlas_service_account_secret" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
secret_expires_after_hours = 2160 # 90 days
}

output "service_account_client_id" {
value = mongodbatlas_service_account.this.client_id
description = "The Client ID of the Service Account. Use it with a secret to authenticate."
value = mongodbatlas_service_account.this.client_id
}

output "service_account_name" {
value = data.mongodbatlas_service_account.this.name
output "secret_id" {
description = "The ID of the Service Account secret."
value = mongodbatlas_service_account_secret.this.secret_id
}

output "service_account_first_secret" {
description = "The secret value of the first secret created with the Service Account. Available only immediately after initial creation."
value = try(mongodbatlas_service_account.this.secrets[0].secret, null)
output "secret" {
description = "The secret value for the Service Account. Returned only when the secret is created."
sensitive = true
}

output "service_accounts_results" {
value = data.mongodbatlas_service_accounts.this.results
value = mongodbatlas_service_account_secret.this.secret
}
```

Expand Down
49 changes: 25 additions & 24 deletions docs/resources/service_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,40 @@ subcategory: "Service Accounts"

## Example Usages

The following example creates a Service Account without an Atlas-generated secret, then creates its first secret as a managed resource.

```terraform
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}
# Create a Service Account without an Atlas-generated secret, then create the first secret
# explicitly with mongodbatlas_service_account_secret so this configuration owns it.

data "mongodbatlas_service_account" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
without_initial_secret = true
}

data "mongodbatlas_service_accounts" "this" {
org_id = var.org_id
resource "mongodbatlas_service_account_secret" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
secret_expires_after_hours = 2160 # 90 days
}

output "service_account_client_id" {
value = mongodbatlas_service_account.this.client_id
description = "The Client ID of the Service Account. Use it with a secret to authenticate."
value = mongodbatlas_service_account.this.client_id
}

output "service_account_name" {
value = data.mongodbatlas_service_account.this.name
output "secret_id" {
description = "The ID of the Service Account secret."
value = mongodbatlas_service_account_secret.this.secret_id
}

output "service_account_first_secret" {
description = "The secret value of the first secret created with the Service Account. Available only immediately after initial creation."
value = try(mongodbatlas_service_account.this.secrets[0].secret, null)
output "secret" {
description = "The secret value for the Service Account. Returned only when the secret is created."
sensitive = true
}

output "service_accounts_results" {
value = data.mongodbatlas_service_accounts.this.results
value = mongodbatlas_service_account_secret.this.secret
}
```

Expand All @@ -59,7 +59,8 @@ output "service_accounts_results" {

### Optional

- `secret_expires_after_hours` (Number) The expiration time of the new Service Account secret, provided in hours. The minimum and maximum allowed expiration times are subject to change and are controlled by the organization's settings. This attribute is required when creating the Service Account and you cannot update it later.
- `secret_expires_after_hours` (Number) The expiration time of the new Service Account secret, provided in hours. The minimum and maximum allowed expiration times are subject to change and are controlled by the organization's settings. Set this field when you set `without_initial_secret` to false or omit `without_initial_secret`. Do not set this field when you set `without_initial_secret` to true. You cannot update this field after you create the Service Account.
- `without_initial_secret` (Boolean) When true, creates the Service Account without generating an initial secret. If you set this field to true, do not set `secret_expires_after_hours`.

### Read-Only

Expand All @@ -86,6 +87,6 @@ Import the Service Account resource by using the Organization ID and Client ID i
$ terraform import mongodbatlas_service_account.test 6117ac2fe2a3d04ed27a987v/mdb_sa_id_1234567890abcdef12345678
```

-> **NOTE:** `secret_expires_after_hours` is not populated during import and should be omitted in the resource definition when importing the resource.
-> **NOTE:** Atlas does not populate `secret_expires_after_hours` or `without_initial_secret` during import. Omit both attributes from the resource definition when you import a Service Account.

For more information, see [Create One Organization Service Account](https://www.mongodb.com/docs/api/doc/atlas-admin-api-v2/operation/operation-createorgserviceaccount) in the MongoDB Atlas API documentation.
63 changes: 49 additions & 14 deletions examples/mongodbatlas_service_account/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,62 @@
# MongoDB Atlas Provider -- Service Account

This example shows how to create a Service Account in MongoDB Atlas.
This example shows how to create a Service Account without an Atlas-generated secret by setting `without_initial_secret = true`, then create its first secret as a managed resource.

Setting `without_initial_secret = true` means Atlas returns no secret from the create request, so every secret is created and rotated through `mongodbatlas_service_account_secret`, which is what a rotation submodule expects.

## Important Notes

When you create a Service Account, Atlas automatically generates a secret. The secret value is returned only once, at creation time.
`without_initial_secret` and `secret_expires_after_hours` are mutually exclusive on the Service Account. Set `without_initial_secret = true` and omit `secret_expires_after_hours`, or set `secret_expires_after_hours` and omit `without_initial_secret`. Atlas rejects a create request that sets both. This example sets the expiration only on the secret resource.

The example includes a sensitive output `service_account_first_secret` that captures this initial secret.
You can retrieve it using (**warning**: this prints the secret to your terminal):
The example includes a sensitive output `secret` that captures the secret value. You can retrieve it using (**warning**: this prints the secret to your terminal):

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

For secret rotation, see [Guide: Service Account Secret Rotation](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/service-account-secret-rotation).
For managing and rotating secrets, see [Guide: Service Account Secret Rotation](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/guides/service-account-secret-rotation).

## Prerequisites

- Service Account with Organization Owner permissions used for provider authentication.
Comment thread
EspenAlbert marked this conversation as resolved.

## Variables Required to be set:
- `atlas_client_id`: MongoDB Atlas Service Account Client ID
- `atlas_client_secret`: MongoDB Atlas Service Account Client Secret
- `org_id`: Organization ID where the Service Account will be created
## Variables Required to be set

- `atlas_client_id`: MongoDB Atlas Service Account Client ID.
- `atlas_client_secret`: MongoDB Atlas Service Account Client Secret.
- `org_id`: Atlas Organization ID where this configuration creates the Service Account.

## Outputs
- `service_account_client_id`: The Client ID of the created Service Account
- `service_account_name`: The name of the Service Account
- `service_account_first_secret` (sensitive): The initial secret value (only available at creation)
- `service_accounts_results`: All Service Accounts in the organization

- `service_account_client_id`: The Client ID of the Service Account
- `secret_id`: The ID of the Service Account secret
- `secret` (sensitive): The secret value

## Usage

**1. Create `terraform.tfvars`.**

```hcl
atlas_client_id = "<ATLAS_CLIENT_ID>"
atlas_client_secret = "<ATLAS_CLIENT_SECRET>"
org_id = "your-org-id"
```

**2. Plan and apply.**

```bash
terraform plan
terraform apply
```

**3. Read the secret** (**warning**: this prints the secret to your terminal).

```bash
terraform output -raw secret
```

**4. Destroy.**

```bash
terraform destroy
```
42 changes: 20 additions & 22 deletions examples/mongodbatlas_service_account/main.tf
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
secret_expires_after_hours = 2160 # 90 days
}
# Create a Service Account without an Atlas-generated secret, then create the first secret
# explicitly with mongodbatlas_service_account_secret so this configuration owns it.

data "mongodbatlas_service_account" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
resource "mongodbatlas_service_account" "this" {
org_id = var.org_id
name = "example-service-account"
description = "Example Service Account"
roles = ["ORG_READ_ONLY"]
without_initial_secret = true
}

data "mongodbatlas_service_accounts" "this" {
org_id = var.org_id
resource "mongodbatlas_service_account_secret" "this" {
org_id = var.org_id
client_id = mongodbatlas_service_account.this.client_id
secret_expires_after_hours = 2160 # 90 days
}

output "service_account_client_id" {
value = mongodbatlas_service_account.this.client_id
description = "The Client ID of the Service Account. Use it with a secret to authenticate."
value = mongodbatlas_service_account.this.client_id
}

output "service_account_name" {
value = data.mongodbatlas_service_account.this.name
output "secret_id" {
description = "The ID of the Service Account secret."
value = mongodbatlas_service_account_secret.this.secret_id
}

output "service_account_first_secret" {
description = "The secret value of the first secret created with the Service Account. Available only immediately after initial creation."
value = try(mongodbatlas_service_account.this.secrets[0].secret, null)
output "secret" {
description = "The secret value for the Service Account. Returned only when the secret is created."
sensitive = true
}

output "service_accounts_results" {
value = data.mongodbatlas_service_accounts.this.results
value = mongodbatlas_service_account_secret.this.secret
}
2 changes: 1 addition & 1 deletion examples/mongodbatlas_service_account/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ variable "atlas_client_secret" {
}

variable "org_id" {
description = "Atlas Organization ID"
description = "Atlas Organization ID where this configuration creates the Service Account."
type = string
}
1 change: 0 additions & 1 deletion examples/mongodbatlas_service_account/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ terraform {
}
required_version = ">= 1.10"
}

10 changes: 8 additions & 2 deletions internal/serviceapi/serviceaccount/resource_schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading