Skip to content

Commit b575da5

Browse files
Document Stack Export/Import (#426)
This isn't explicitly documented, but is an important feature, documenting it fully here
1 parent 8d93701 commit b575da5

File tree

6 files changed

+209
-16
lines changed

6 files changed

+209
-16
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
---
2+
title: Sharing Outputs with Continuous Deployment
3+
description: Export Outputs of a Terraform Stack to a Kubernetes CD Service
4+
---
5+
6+
It's frequently necessary to take infrastructure created in a stack and use the data in a Service being deployed to K8s. A few usecases where this can be important:
7+
8+
* exporting IAM role ARNs for access to various AWS services like S3 or SQS
9+
* exporting DB connection strings to configure as k8s secrets for a webserver
10+
* exporting a dynamically created S3 bucket name to use in your deployed service
11+
12+
This also facilitates end-to-end self-service, as you no longer need a human in-the-loop to apply that last mile configuration, plus you get continuous reconciliation in the event a terraform change implies a recreation of those resources.
13+
14+
## End To End Example
15+
16+
We do this a lot in our service catalog, available here: https://github.com/pluralsh/scaffolds/tree/main/catalogs. A basic example would be something like our Airbyte setup, where the terraform stack has an outputs file like so:
17+
18+
```tf
19+
output "access_key_id" {
20+
value = aws_iam_access_key.airbyte.id
21+
}
22+
23+
output "secret_access_key" {
24+
value = aws_iam_access_key.airbyte.secret
25+
sensitive = true
26+
}
27+
28+
output "postgres_host" {
29+
value = try(module.db.db_instance_address, "")
30+
}
31+
32+
output "postgres_password" {
33+
value = random_password.password.result
34+
sensitive = true
35+
}
36+
37+
output "oidc_cookie_secret" {
38+
value = random_password.oidc_cookie.result
39+
sensitive = true
40+
}
41+
42+
output "oidc_client_id" {
43+
value = plural_oidc_provider.airbyte.client_id
44+
sensitive = true
45+
}
46+
47+
output "oidc_client_secret" {
48+
value = plural_oidc_provider.airbyte.client_secret
49+
sensitive = true
50+
}
51+
```
52+
53+
Airbyte needs fixed aws access keys to communicate with S3 and also there's a dynamically generated OIDC client that's used for auth against it's webserver, alongside postgres credentials.
54+
55+
with a InfrastructureStack resource will like this:
56+
57+
```yaml
58+
apiVersion: deployments.plural.sh/v1alpha1
59+
kind: InfrastructureStack
60+
metadata:
61+
name: airbyte-data
62+
namespace: apps
63+
spec:
64+
detach: false
65+
type: TERRAFORM
66+
approval: true
67+
manageState: true
68+
git:
69+
ref: main
70+
folder: terraform/apps/airbyte/data
71+
repositoryRef:
72+
name: infra
73+
namespace: infra
74+
clusterRef:
75+
name: mgmt
76+
namespace: infra
77+
```
78+
79+
All of this data needs to be used by the service that is actually deployed to K8s, so it will explicitly "import" that stack with its `imports` declaration, like so:
80+
81+
```yaml
82+
apiVersion: deployments.plural.sh/v1alpha1
83+
kind: ServiceDeployment
84+
metadata:
85+
name: airbyte-data
86+
namespace: apps
87+
spec:
88+
namespace: airbyte
89+
git:
90+
folder: helm/airbyte/data
91+
ref: main
92+
repositoryRef:
93+
kind: GitRepository
94+
name: infra
95+
namespace: infra
96+
helm:
97+
version: "1.x.x"
98+
chart: airbyte
99+
release: airbyte
100+
ignoreHooks: false
101+
url: https://airbytehq.github.io/helm-charts
102+
valuesFiles:
103+
- airbyte.yaml.liquid
104+
imports:
105+
- stackRef:
106+
name: airbyte-data # notice this is the same as the metadata.name and metadata.namespace of the `InfrastructureStack` CRD to resolve the ref
107+
namespace: apps
108+
configuration:
109+
cluster: data
110+
hostname: airbyte.example.com
111+
bucket: airbyte-bucket
112+
region: us-east-2
113+
clusterRef:
114+
kind: Cluster
115+
name: data
116+
namespace: infra
117+
```
118+
119+
When that is present, it will allow us to template the outputs under the key `imports["airbyte-data"].{output_field_name}` in any .liquid values or yaml file for the service, an example for the airbyte helm chart `airbyte.yaml.liquid` values file:
120+
121+
```yaml
122+
global:
123+
deploymentMode: oss
124+
edition: community
125+
126+
airbyteUrl: {{ configuration.hostname }}
127+
128+
storage:
129+
type: S3
130+
storageSecretName: airbyte-airbyte-secrets
131+
s3:
132+
region: {{ configuration.region }}
133+
authenticationType: credentials
134+
accessKeyId: {{ imports["airbyte-data"].access_key_id }}
135+
accessKeyIdSecretKey: AWS_ACCESS_KEY_ID
136+
secretAccessKey: {{ imports["airbyte-data"].secret_access_key }}
137+
secretAccessKeySecretKey: AWS_SECRET_ACCESS_KEY
138+
bucket:
139+
log: {{ configuration.bucket }}
140+
state: {{ configuration.bucket }}
141+
workloadOutput: {{ configuration.bucket }}
142+
143+
database:
144+
type: external
145+
database: airbyte
146+
host: {{ imports["airbyte-data"].postgres_host }}
147+
port: "5432"
148+
secretName: airbyte-airbyte-secrets
149+
user: airbyte
150+
userSecretKey: DATABASE_USER
151+
password: {{ imports["airbyte-data"].postgres_password }}
152+
passwordSecretKey: DATABASE_PASSWORD
153+
154+
postgresql:
155+
enabled: false
156+
157+
externalDatabase:
158+
database: airbyte
159+
host: {{ imports["airbyte-data"].postgres_host }}
160+
user: airbyte
161+
existingSecret: ~
162+
password: {{ imports["airbyte-data"].postgres_password }}
163+
port: 5432
164+
165+
webapp:
166+
ingress:
167+
enabled: false
168+
podAnnotations:
169+
security.plural.sh/oauth-env-secret: airbyte-proxy-config
170+
podLabels:
171+
security.plural.sh/inject-oauth-sidecar: "true"
172+
```
173+
174+
You an read more about templating [here](/plural-features/service-templating). In this case it's going to pass these variables through helm and configure the necessary secrets and yaml structures with the provided information.
175+
176+
{% callout severity="info" %}
177+
The imports structure is a map from stack name to stack outputs, eg a nested map type like { string => { string => any } }
178+
{% /callout %}
File renamed without changes.
File renamed without changes.

src/generated/pages.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
{
1111
"path": "/overview/management-api-reference",
12-
"lastmod": "2025-02-21T20:28:52.000Z"
12+
"lastmod": "2025-03-03T09:12:53.000Z"
1313
},
1414
{
1515
"path": "/overview/agent-api-reference",
@@ -116,8 +116,8 @@
116116
"lastmod": "2025-02-20T19:05:59.000Z"
117117
},
118118
{
119-
"path": "/plural-features/stacks-iac-management/local-execution",
120-
"lastmod": "2025-02-20T19:05:59.000Z"
119+
"path": "/plural-features/stacks-iac-management/sharing-outputs",
120+
"lastmod": "2025-03-05T22:14:12.000Z"
121121
},
122122
{
123123
"path": "/plural-features/stacks-iac-management/custom-stacks",
@@ -127,9 +127,13 @@
127127
"path": "/plural-features/stacks-iac-management/auto-cancellation",
128128
"lastmod": "2025-02-20T19:05:59.000Z"
129129
},
130+
{
131+
"path": "/plural-features/stacks-iac-management/local-execution",
132+
"lastmod": "2025-03-05T22:14:12.000Z"
133+
},
130134
{
131135
"path": "/plural-features/stacks-iac-management/service-contexts",
132-
"lastmod": "2025-02-20T19:05:59.000Z"
136+
"lastmod": "2025-03-05T22:14:12.000Z"
133137
},
134138
{
135139
"path": "/plural-features/stacks-iac-management",
@@ -185,7 +189,7 @@
185189
},
186190
{
187191
"path": "/plural-features/service-templating/supporting-liquid-filters",
188-
"lastmod": "2025-02-21T20:53:12.000Z"
192+
"lastmod": "2025-03-03T09:12:53.000Z"
189193
},
190194
{
191195
"path": "/plural-features/service-templating",

src/routes/docs.generated.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ export const docRoutes: DocRouteMap = {
186186
id: 'plural_features_stacks_iac_management_manual_runs',
187187
},
188188

189-
plural_features_stacks_iac_management_local_execution: {
190-
path: '/03-plural-features/03-stacks-iac-management/04-local-execution',
191-
title: 'Local execution',
192-
id: 'plural_features_stacks_iac_management_local_execution',
189+
plural_features_stacks_iac_management_sharing_outputs: {
190+
path: '/03-plural-features/03-stacks-iac-management/04-sharing-outputs',
191+
title: 'Sharing Outputs with Continuous Deployment',
192+
id: 'plural_features_stacks_iac_management_sharing_outputs',
193193
},
194194

195195
plural_features_stacks_iac_management_custom_stacks: {
@@ -204,8 +204,14 @@ export const docRoutes: DocRouteMap = {
204204
id: 'plural_features_stacks_iac_management_auto_cancellation',
205205
},
206206

207+
plural_features_stacks_iac_management_local_execution: {
208+
path: '/03-plural-features/03-stacks-iac-management/07-local-execution',
209+
title: 'Local execution',
210+
id: 'plural_features_stacks_iac_management_local_execution',
211+
},
212+
207213
plural_features_stacks_iac_management_service_contexts: {
208-
path: '/03-plural-features/03-stacks-iac-management/07-service-contexts',
214+
path: '/03-plural-features/03-stacks-iac-management/08-service-contexts',
209215
title: 'Terraform interop with service contexts',
210216
id: 'plural_features_stacks_iac_management_service_contexts',
211217
},
@@ -290,7 +296,7 @@ export const docRoutes: DocRouteMap = {
290296

291297
plural_features_service_templating_templating_filters: {
292298
path: '/03-plural-features/08-service-templating/01-supporting-liquid-filters',
293-
title: 'Supporting liquid filters',
299+
title: 'Supporting Liquid Filters',
294300
id: 'plural_features_service_templating_templating_filters',
295301
},
296302

src/routing/navigation.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ export const docNavigation: NavMenu = [
175175
"sortPath": "/03-plural-features/03-stacks-iac-management/03-manual-runs"
176176
},
177177
{
178-
"title": "Local execution",
179-
"href": "/plural-features/stacks-iac-management/local-execution",
180-
"sortPath": "/03-plural-features/03-stacks-iac-management/04-local-execution"
178+
"title": "Sharing Outputs with Continuous Deployment",
179+
"href": "/plural-features/stacks-iac-management/sharing-outputs",
180+
"sortPath": "/03-plural-features/03-stacks-iac-management/04-sharing-outputs"
181181
},
182182
{
183183
"title": "Custom stacks",
@@ -189,10 +189,15 @@ export const docNavigation: NavMenu = [
189189
"href": "/plural-features/stacks-iac-management/auto-cancellation",
190190
"sortPath": "/03-plural-features/03-stacks-iac-management/06-auto-cancellation"
191191
},
192+
{
193+
"title": "Local execution",
194+
"href": "/plural-features/stacks-iac-management/local-execution",
195+
"sortPath": "/03-plural-features/03-stacks-iac-management/07-local-execution"
196+
},
192197
{
193198
"title": "Terraform interop with service contexts",
194199
"href": "/plural-features/stacks-iac-management/service-contexts",
195-
"sortPath": "/03-plural-features/03-stacks-iac-management/07-service-contexts"
200+
"sortPath": "/03-plural-features/03-stacks-iac-management/08-service-contexts"
196201
}
197202
]
198203
},
@@ -264,7 +269,7 @@ export const docNavigation: NavMenu = [
264269
"href": "/plural-features/service-templating",
265270
"sections": [
266271
{
267-
"title": "Supporting liquid filters",
272+
"title": "Supporting Liquid Filters",
268273
"href": "/plural-features/service-templating/supporting-liquid-filters",
269274
"sortPath": "/03-plural-features/08-service-templating/01-supporting-liquid-filters"
270275
}

0 commit comments

Comments
 (0)