Skip to content

Commit

Permalink
Merge pull request #315 from dbt-labs/release-0.3.21
Browse files Browse the repository at this point in the history
  • Loading branch information
b-per authored Nov 5, 2024
2 parents aa6be8d + bc2755d commit 29c15f0
Show file tree
Hide file tree
Showing 18 changed files with 1,046 additions and 39 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@

All notable changes to this project will be documented in this file.

## [Unreleased](https://github.com/dbt-labs/terraform-provider-dbtcloud/compare/v0.3.20...HEAD)
## [Unreleased](https://github.com/dbt-labs/terraform-provider-dbtcloud/compare/v0.3.21...HEAD)

# [0.3.21](https://github.com/dbt-labs/terraform-provider-dbtcloud/compare/v0.3.20...v0.3.21)

### Changes

- Allow setting external OAuth config for global connections in Snowflake
- Add resource `dbtcloud_oauth_configuration` to define external OAuth integrations

### Fixes

- Fix acceptance test for jobs when using the ability to compare changes

# [0.3.20](https://github.com/dbt-labs/terraform-provider-dbtcloud/compare/v0.3.19...v0.3.20)

Expand Down
8 changes: 4 additions & 4 deletions docs/resources/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ resource "dbtcloud_environment" "dev_environment" {
### Optional

- `connection_id` (Number) The ID of the connection to use (can be the `id` of a `dbtcloud_global_connection` or the `connection_id` of a legacy connection).
- At the moment, it is optional and the environment will use the connection set in `dbtcloud_project_connection` if `connection_id` is not set in this resource
- In future versions this field will become required, so it is recommended to set it from now on
- When configuring this field, it needs to be configured for all the environments of the project
- To avoid Terraform state issues, when using this field, the `dbtcloud_project_connection` resource should be removed from the project or you need to make sure that the `connection_id` is the same in `dbtcloud_project_connection` and in the `connection_id` of the Development environment of the project
- At the moment, it is optional and the environment will use the connection set in `dbtcloud_project_connection` if `connection_id` is not set in this resource
- In future versions this field will become required, so it is recommended to set it from now on
- When configuring this field, it needs to be configured for all the environments of the project
- To avoid Terraform state issues, when using this field, the `dbtcloud_project_connection` resource should be removed from the project or you need to make sure that the `connection_id` is the same in `dbtcloud_project_connection` and in the `connection_id` of the Development environment of the project
- `credential_id` (Number) Credential ID to create the environment with. A credential is not required for development environments but is required for deployment environments
- `custom_branch` (String) Which custom branch to use in this environment
- `dbt_version` (String) Version number of dbt to use in this environment. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre` or `versionless`. Defaults to`versionless` if no version is provided
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/global_connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ resource "dbtcloud_global_connection" "synapse" {
- `bigquery` (Attributes) (see [below for nested schema](#nestedatt--bigquery))
- `databricks` (Attributes) Databricks connection configuration (see [below for nested schema](#nestedatt--databricks))
- `fabric` (Attributes) Microsoft Fabric connection configuration. (see [below for nested schema](#nestedatt--fabric))
- `oauth_configuration_id` (Number) External OAuth configuration ID (only Snowflake for now)
- `postgres` (Attributes) PostgreSQL connection configuration. (see [below for nested schema](#nestedatt--postgres))
- `private_link_endpoint_id` (String) Private Link Endpoint ID. This ID can be found using the `privatelink_endpoint` data source
- `redshift` (Attributes) Redshift connection configuration (see [below for nested schema](#nestedatt--redshift))
Expand All @@ -169,7 +170,6 @@ resource "dbtcloud_global_connection" "synapse" {
- `adapter_version` (String) Version of the adapter
- `id` (Number) Connection Identifier
- `is_ssh_tunnel_enabled` (Boolean) Whether the connection can use an SSH tunnel
- `oauth_configuration_id` (Number)

<a id="nestedatt--apache_spark"></a>
### Nested Schema for `apache_spark`
Expand Down
81 changes: 81 additions & 0 deletions docs/resources/oauth_configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
page_title: "dbtcloud_oauth_configuration Resource - dbtcloud"
subcategory: ""
description: |-
Configure an external OAuth integration for the data warehouse. Currently supports Okta and Entra ID (i.e. Azure AD) for Snowflake.
See the documentation https://docs.getdbt.com/docs/cloud/manage-access/external-oauth for more information on how to configure it.
---

# dbtcloud_oauth_configuration (Resource)


Configure an external OAuth integration for the data warehouse. Currently supports Okta and Entra ID (i.e. Azure AD) for Snowflake.

See the [documentation](https://docs.getdbt.com/docs/cloud/manage-access/external-oauth) for more information on how to configure it.

## Example Usage

```terraform
resource "dbtcloud_oauth_configuration" "test" {
type = "entra"
name = "My Entra ID Oauth integration"
client_id = "client-id"
client_secret = "client-secret"
redirect_uri = "http://example.com"
token_url = "http://example.com"
authorize_url = "http://example.com"
application_id_uri = "uri"
}
resource "dbtcloud_oauth_configuration" "test" {
type = "okta"
name = "My Okta Oauth integration"
client_id = "client-id"
client_secret = "client-secret"
redirect_uri = "http://example.com"
token_url = "http://example.com"
authorize_url = "http://example.com"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `authorize_url` (String) The Authorize URL for the OAuth integration
- `client_id` (String) The Client ID for the OAuth integration
- `client_secret` (String, Sensitive) The Client secret for the OAuth integration
- `name` (String) The name of OAuth integration
- `redirect_uri` (String) The redirect URL for the OAuth integration
- `token_url` (String) The Token URL for the OAuth integration
- `type` (String) The type of OAuth integration (`entra` or `okta`)

### Optional

- `application_id_uri` (String) The Application ID URI for the OAuth integration. Only for Entra

### Read-Only

- `id` (Number) The ID of the OAuth configuration

## Import

Import is supported using the following syntax:

```shell
# using import blocks (requires Terraform >= 1.5)
import {
to = dbtcloud_oauth_configuration.my_external_oauth
id = "external_oauth_id"
}

import {
to = dbtcloud_oauth_configuration.my_external_oauth
id = "12345"
}

# using the older import command
terraform import dbtcloud_oauth_configuration.my_external_oauth "external_oauth_id"
terraform import dbtcloud_oauth_configuration.my_external_oauth 12345
```
14 changes: 14 additions & 0 deletions examples/resources/dbtcloud_oauth_configuration/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# using import blocks (requires Terraform >= 1.5)
import {
to = dbtcloud_oauth_configuration.my_external_oauth
id = "external_oauth_id"
}

import {
to = dbtcloud_oauth_configuration.my_external_oauth
id = "12345"
}

# using the older import command
terraform import dbtcloud_oauth_configuration.my_external_oauth "external_oauth_id"
terraform import dbtcloud_oauth_configuration.my_external_oauth 12345
21 changes: 21 additions & 0 deletions examples/resources/dbtcloud_oauth_configuration/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

resource "dbtcloud_oauth_configuration" "test" {
type = "entra"
name = "My Entra ID Oauth integration"
client_id = "client-id"
client_secret = "client-secret"
redirect_uri = "http://example.com"
token_url = "http://example.com"
authorize_url = "http://example.com"
application_id_uri = "uri"
}

resource "dbtcloud_oauth_configuration" "test" {
type = "okta"
name = "My Okta Oauth integration"
client_id = "client-id"
client_secret = "client-secret"
redirect_uri = "http://example.com"
token_url = "http://example.com"
authorize_url = "http://example.com"
}
2 changes: 1 addition & 1 deletion pkg/dbt_cloud/global_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type GlobalConnectionCommon struct {
Name *string `json:"name,omitempty"`
IsSshTunnelEnabled *bool `json:"is_ssh_tunnel_enabled,omitempty"`
PrivateLinkEndpointId nullable.Nullable[string] `json:"private_link_endpoint_id,omitempty"`
OauthConfigurationId *int64 `json:"oauth_configuration_id,omitempty"`
OauthConfigurationId nullable.Nullable[int64] `json:"oauth_configuration_id,omitempty"`
// OauthRedirectUri *string `json:"oauth_redirect_uri"` //those are read-only fields, we could maybe get them as Computed but never send them
// IsConfiguredForNativeOauth bool `json:"is_configured_for_native_oauth"`
}
Expand Down
194 changes: 194 additions & 0 deletions pkg/dbt_cloud/oauth_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package dbt_cloud

import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
)

type OAuthConfiguration struct {
ID *int64 `json:"id,omitempty"`
AccountId int64 `json:"account_id"`
Type string `json:"type"`
Name string `json:"name"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
AuthorizeUrl string `json:"authorize_url"`
TokenUrl string `json:"token_url"`
RedirectUri string `json:"redirect_uri"`
OAuthConfigurationExtra *OAuthConfigurationExtra `json:"extra_data,omitempty"`
}

type OAuthConfigurationExtra struct {
ApplicationIdUri *string `json:"application_id_uri,omitempty"`
}

type OAuthConfigurationListResponse struct {
Data []OAuthConfiguration `json:"data"`
Status ResponseStatus `json:"status"`
Extra ResponseExtra `json:"extra"`
}

type OAuthConfigurationResponse struct {
Data OAuthConfiguration `json:"data"`
Status ResponseStatus `json:"status"`
}

func (c *Client) GetOAuthConfiguration(oAuthConfigurationID int64) (*OAuthConfiguration, error) {
req, err := http.NewRequest(
"GET",
fmt.Sprintf(
"%s/v3/accounts/%s/oauth-configurations/%d/",
c.HostURL,
strconv.Itoa(c.AccountID),
oAuthConfigurationID,
),
nil,
)
if err != nil {
return nil, err
}

body, err := c.doRequest(req)
if err != nil {
return nil, err
}

oAuthConfigurationResponse := OAuthConfigurationResponse{}
err = json.Unmarshal(body, &oAuthConfigurationResponse)
if err != nil {
return nil, err
}

return &oAuthConfigurationResponse.Data, nil
}

func (c *Client) CreateOAuthConfiguration(
oAuthType string,
name string,
clientId string,
clientSecret string,
authorizeUrl string,
tokenUrl string,
redirectUri string,
applicationURI string,
) (*OAuthConfiguration, error) {
newOAuthConfiguration := OAuthConfiguration{
AccountId: int64(c.AccountID),
Type: oAuthType,
Name: name,
ClientId: clientId,
ClientSecret: clientSecret,
AuthorizeUrl: authorizeUrl,
TokenUrl: tokenUrl,
RedirectUri: redirectUri,
}

if applicationURI != "" {
newOAuthConfigurationExtra := OAuthConfigurationExtra{
ApplicationIdUri: &applicationURI,
}
newOAuthConfiguration.OAuthConfigurationExtra = &newOAuthConfigurationExtra
}

newOAuthConfigurationData, err := json.Marshal(newOAuthConfiguration)
if err != nil {
return nil, err
}

req, err := http.NewRequest(
"POST",
fmt.Sprintf(
"%s/v3/accounts/%s/oauth-configurations/",
c.HostURL,
strconv.Itoa(c.AccountID),
),
strings.NewReader(string(newOAuthConfigurationData)),
)
if err != nil {
return nil, err
}

body, err := c.doRequest(req)
if err != nil {
return nil, err
}

oAuthConfigurationResponse := OAuthConfigurationResponse{}
err = json.Unmarshal(body, &oAuthConfigurationResponse)
if err != nil {
return nil, err
}

return &oAuthConfigurationResponse.Data, nil
}

func (c *Client) UpdateOAuthConfiguration(
oAuthConfigurationID int64,
oAuthConfiguration OAuthConfiguration,
) (*OAuthConfiguration, error) {
oAuthConfigurationData, err := json.Marshal(oAuthConfiguration)
if err != nil {
return nil, err
}

req, err := http.NewRequest(
"POST",
fmt.Sprintf(
"%s/v3/accounts/%s/oauth-configurations/%d/",
c.HostURL,
strconv.Itoa(c.AccountID),
oAuthConfigurationID,
),
strings.NewReader(string(oAuthConfigurationData)),
)
if err != nil {
return nil, err
}

body, err := c.doRequest(req)
if err != nil {
return nil, err
}

oAuthConfigurationResponse := OAuthConfigurationResponse{}
err = json.Unmarshal(body, &oAuthConfigurationResponse)
if err != nil {
return nil, err
}

return &oAuthConfigurationResponse.Data, nil
}

func (c *Client) DeleteOAuthConfiguration(
oAuthConfigurationID int64,
) error {
req, err := http.NewRequest(
"DELETE",
fmt.Sprintf(
"%s/v3/accounts/%s/oauth-configurations/%d/",
c.HostURL,
strconv.Itoa(c.AccountID),
oAuthConfigurationID,
),
nil,
)
if err != nil {
return err
}

body, err := c.doRequest(req)
if err != nil {
return err
}

oAuthConfigurationResponse := OAuthConfigurationResponse{}
err = json.Unmarshal(body, &oAuthConfigurationResponse)
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 29c15f0

Please sign in to comment.