-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoutputs.tf
More file actions
264 lines (217 loc) · 12.5 KB
/
Copy pathoutputs.tf
File metadata and controls
264 lines (217 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
###############################################################################
# Identity
###############################################################################
output "id" {
description = <<-EOT
The Resource ID of the Sentinel onboarding state.
A singleton child of the workspace, so it always ends in
`/onboardingStates/default`. That is not a convention this module chose -
the provider hardcodes the name, with the source comment "the service only
support `default` state".
Its shape stacks two resource providers:
`.../Microsoft.OperationalInsights/workspaces/<ws>/providers/Microsoft.SecurityInsights/onboardingStates/default`.
Use it as the scope for a management lock; a `CanNotDelete` lock here is
worth more than on most resources, because the destroy offboards Sentinel.
EOT
value = azurerm_sentinel_log_analytics_workspace_onboarding.this.id
}
output "workspace_id" {
description = <<-EOT
The onboarded workspace's ARM Resource ID.
THIS IS THE OUTPUT EVERY OTHER SENTINEL MODULE SHOULD CONSUME as its
`log_analytics_workspace_id`. Reading it from here rather than from the
workspace module makes Terraform order onboarding before the rules that
depend on it, which is what the provider's own examples do. Wiring the
workspace module's `id` directly works but leaves the ordering to chance -
and an analytics rule created against an un-onboarded workspace fails at
apply.
Read back from the resource rather than echoed from the input. Note that the
provider RECONSTRUCTS this value on every read, from the components of the
onboarding-state ID, rather than returning what was sent - so it comes back
in the provider's canonical form.
EOT
value = azurerm_sentinel_log_analytics_workspace_onboarding.this.workspace_id
}
output "workspace_name" {
description = <<-EOT
The workspace's name, parsed from `workspace_id`.
Emitted so a composition can build a diagnostic label, an alert rule name or
a lock name without re-parsing the ID, and so `terraform output` names the
workspace rather than showing a long Resource ID.
Derived from the input, so it is known at plan time. Null only if the ID did
not match the expected shape, which this module's validation refuses first.
EOT
value = try(one(regex("(?i)/workspaces/([^/]+)$", trimspace(var.workspace_id))), null)
}
output "workspace_resource_group_name" {
description = <<-EOT
The resource group containing the workspace, parsed from `workspace_id`.
The onboarding state is a child of the workspace, so this is also the
resource group its own Resource ID sits under - which matters when scoping a
management lock or a role assignment by group rather than by resource.
Derived from the input, so it is known at plan time.
EOT
value = try(one(regex("(?i)/resourceGroups/([^/]+)/", trimspace(var.workspace_id))), null)
}
output "onboarding_state_name" {
description = <<-EOT
Always `"default"`. The service supports no other onboarding-state name, and
the provider hardcodes it rather than exposing a `name` argument.
Emitted because its absence from the inputs is easy to read as an oversight.
It is not: it is why this resource is a singleton per workspace, why the
module owns one record rather than a keyed map, and why two configurations
onboarding the same workspace collide on one identity rather than producing
two onboardings.
EOT
value = "default"
}
###############################################################################
# The customer-managed key decision
###############################################################################
output "customer_managed_key_enabled" {
description = <<-EOT
Whether the onboarding uses a customer-managed key.
Emitted because the choice is PERMANENT FOR THE WORKSPACE: once onboarded
with this true, the workspace cannot be onboarded again with it false. Having
it in the outputs means a review can see the decision without inferring it
from the inputs.
The field is also force-new, which combines badly with that permanence - see
`changing_the_key_setting_can_strand_the_workspace`.
EOT
value = azurerm_sentinel_log_analytics_workspace_onboarding.this.customer_managed_key_enabled
}
output "changing_the_key_setting_can_strand_the_workspace" {
description = <<-EOT
Always true, and it is the most expensive fact about this small resource.
`customer_managed_key_enabled` is FORCE-NEW, so changing it plans as
destroy-then-create. The destroy OFFBOARDS Sentinel from the workspace. And
the provider states that a workspace onboarded with the key enabled cannot be
onboarded again with it disabled - the constraint belongs to the workspace,
not to the Terraform resource.
So a plan that looks like an ordinary replacement can leave the workspace
offboarded and unable to be onboarded the way it was just asked for. The
intermediate state is not a brief flicker: it is Sentinel switched off, with
the requested end state refused.
Decide the key setting before the first apply and treat it as immutable
afterwards. This is also why the module keeps the provider's `false` default
rather than the more protective value that this suite's convention would
otherwise suggest: a CMK-by-default would make the empty call take an
irreversible action.
EOT
value = true
}
###############################################################################
# Facts that are consequential, invisible in state, and inferable from nothing
###############################################################################
output "sentinel_is_onboarded" {
description = <<-EOT
Always true while this resource exists, and emitted because the inverse is
the point: destroying this resource OFFBOARDS Sentinel from the workspace,
which stops every rule, connector and watchlist that targets it.
A constant output is a poor substitute for a guard rail, but it does put the
dependency into the state review - and it gives a composition something to
depend on, which is the mechanism that actually orders the family correctly.
EOT
value = true
}
output "gates_the_entire_sentinel_family" {
description = <<-EOT
Always true. This resource is the gate for every other `sentinel_*` resource
in the library.
Analytics rules, data connectors, watchlists, automation rules, threat
intelligence indicators and the rest all target a workspace, and none of them
works until that workspace is onboarded. None of them declares a dependency
on this record either - they take a workspace ID, not an onboarding ID.
Which means Terraform will happily plan an analytics rule and an onboarding
in the same apply, in either order, and the rule fails if it goes first.
**Wire every sibling's `log_analytics_workspace_id` from this module's
`workspace_id` output** rather than from the workspace module, so the
reference creates the ordering that nothing else enforces.
EOT
value = true
}
output "destroying_this_offboards_sentinel" {
description = <<-EOT
Always true, and it deserves its own entry rather than a clause in another.
A destroy here does not remove a small registration record. It offboards
Microsoft Sentinel from the Log Analytics workspace, which stops detection:
analytics rules no longer run, connectors stop ingesting, and incidents stop
being raised. Nothing else in the plan reflects that - the sibling resources
are not being destroyed, so the plan shows one small resource going away.
The delete is also a plain API call with no wait, unlike the create, which
polls until the onboarding appears. So the destroy returns before the
consequences are necessarily complete.
A caller cannot add `prevent_destroy` here - `lifecycle` is not valid inside a
`module` block. A `CanNotDelete` management lock on the onboarding state is
the available control: it makes the deletion fail rather than preventing a
plan from proposing it.
EOT
value = true
}
output "is_a_singleton_per_workspace" {
description = <<-EOT
Always true. One onboarding state per workspace, always named `default`.
The provider hardcodes the name - its source comment reads "the service only
support `default` state" - so there is no `name` argument to vary and no way
to have two. Two configurations onboarding the same workspace do not produce
two onboardings; the second is caught by the provider's import guard and
reported as a resource that already exists and must be imported.
That is the good failure direction, and worth knowing when splitting a
Sentinel estate across repositories: onboarding belongs to exactly one of
them, and the others should consume its `workspace_id` output.
EOT
value = true
}
output "create_waits_for_onboarding_to_appear" {
description = <<-EOT
Always true, and it explains an apply that sits still for several minutes on
a two-argument resource.
Onboarding is asynchronous. After issuing the create the provider POLLS the
onboarding state - every 15 seconds, until it stops returning 404 - and only
then records the resource. Its failure message is "waiting for ... to be
fully onboarded". The poll runs against the remaining create timeout, whose
default is 30 minutes.
So a slow apply here is the provider doing its job rather than hanging, and
raising the create timeout is the right response to a workspace that is slow
to onboard. Note the asymmetry: the DELETE does not poll at all.
EOT
value = true
}
output "onboarding_alone_detects_nothing" {
description = <<-EOT
Always true, and worth stating because a clean apply here looks like Sentinel
is working.
Onboarding enables Microsoft Sentinel on the workspace. It creates no
analytics rules, connects no data sources, and raises no incidents. A
workspace that is onboarded and nothing else is a Sentinel that watches
nothing - and it looks identical, from Terraform, to a fully configured one.
The detection surface is built by the sibling modules: data connectors to
bring logs in, analytics rules to evaluate them, automation rules to respond.
Each is a separate resource, and each is silent when absent.
This resource also carries no `tags` - the whole Sentinel family exposes
none. Tag the Log Analytics workspace instead.
EOT
value = true
}
output "provider_parse_error_names_a_field_that_does_not_exist" {
description = <<-EOT
Always true, and it will cost someone twenty minutes if it is not written
down.
If `workspace_id` cannot be parsed as a workspace Resource ID, the provider
reports *"parsing `log_analytics_workspace_id`"* - naming an argument this
resource does not have. The sibling `azurerm_log_analytics_saved_search` does
call its equivalent field `log_analytics_workspace_id`; this one calls it
`workspace_id`. The error message was written against the other name.
So a reader hunting for `log_analytics_workspace_id` in their configuration
will not find it. This module refuses the malformed value at
`terraform validate` instead, with a message that names the argument that
actually exists - which is the point of mirroring the check rather than
leaving it to the provider.
EOT
value = true
}
# --- Service lifecycle -----------------------------------------------------------------------------
output "management_moves_to_the_defender_portal" {
value = true
description = "Always true, and a transition-planning fact rather than a Terraform one. Microsoft states that after March 31, 2027 Microsoft Sentinel will no longer be supported in the Azure portal and will be available only in the Microsoft Defender portal, and that customers using Microsoft Sentinel in the Azure portal will be redirected to the Defender portal; since July 2025 many new customers are onboarded and redirected there automatically. This changes the management EXPERIENCE, not the resources: azurerm_sentinel_* are ARM resources and Terraform talks to ARM rather than to a portal, so the resources this module manages continue to exist and stay manageable from code. What does change is everything that assumes the Azure portal - runbooks, screenshots, analyst training, and any procedure that ends in a human clicking through Sentinel in the Azure portal. Emitted as a constant because the date is invisible in state, is inferable from nothing else this module emits, and needs planning long before it arrives."
}