@rulebricks/cli 2.1.6 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -14
- package/cluster-setup/aws/README.md +123 -0
- package/cluster-setup/aws/check-aws-access.sh +242 -0
- package/cluster-setup/aws/parameters.json +13 -0
- package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
- package/cluster-setup/azure/README.md +141 -0
- package/cluster-setup/azure/check-aks-prereqs.sh +276 -0
- package/cluster-setup/azure/parameters.json +30 -0
- package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
- package/cluster-setup/gcp/README.md +189 -0
- package/cluster-setup/gcp/check-gke-prereqs.sh +260 -0
- package/dist/commands/backup.d.ts +5 -0
- package/dist/commands/backup.js +104 -0
- package/dist/commands/deploy.d.ts +3 -1
- package/dist/commands/deploy.js +226 -326
- package/dist/commands/destroy.d.ts +1 -1
- package/dist/commands/destroy.js +73 -123
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.js +78 -47
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +74 -0
- package/dist/commands/open.d.ts +1 -1
- package/dist/commands/open.js +4 -12
- package/dist/commands/redeploy.d.ts +6 -0
- package/dist/commands/redeploy.js +310 -0
- package/dist/commands/restore.d.ts +5 -0
- package/dist/commands/restore.js +338 -0
- package/dist/commands/status.js +62 -49
- package/dist/commands/upgrade.js +74 -51
- package/dist/components/DNSWaitScreen.d.ts +5 -1
- package/dist/components/DNSWaitScreen.js +47 -41
- package/dist/components/Wizard/WizardContext.d.ts +174 -29
- package/dist/components/Wizard/WizardContext.js +896 -91
- package/dist/components/Wizard/steps/CloudProviderStep.js +192 -102
- package/dist/components/Wizard/steps/DomainStep.js +5 -24
- package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
- package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
- package/dist/components/Wizard/steps/FeatureConfigStep.js +959 -248
- package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
- package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
- package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
- package/dist/components/Wizard/steps/ReviewStep.js +56 -7
- package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
- package/dist/components/Wizard/steps/StorageStep.js +592 -0
- package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
- package/dist/components/Wizard/steps/VersionStep.js +45 -23
- package/dist/components/Wizard/steps/index.d.ts +3 -3
- package/dist/components/Wizard/steps/index.js +3 -3
- package/dist/components/common/CommandApproval.d.ts +12 -0
- package/dist/components/common/CommandApproval.js +91 -0
- package/dist/components/common/DeploymentPicker.d.ts +14 -0
- package/dist/components/common/DeploymentPicker.js +16 -0
- package/dist/components/common/index.d.ts +2 -0
- package/dist/components/common/index.js +2 -0
- package/dist/index.js +94 -62
- package/dist/lib/cloudCli.d.ts +134 -63
- package/dist/lib/cloudCli.js +512 -220
- package/dist/lib/clusterSetupDefaults.d.ts +30 -0
- package/dist/lib/clusterSetupDefaults.js +64 -0
- package/dist/lib/commandApproval.d.ts +26 -0
- package/dist/lib/commandApproval.js +114 -0
- package/dist/lib/config.d.ts +12 -10
- package/dist/lib/config.js +91 -33
- package/dist/lib/configFixtures.d.ts +5 -0
- package/dist/lib/configFixtures.js +513 -0
- package/dist/lib/deploymentHealth.d.ts +32 -0
- package/dist/lib/deploymentHealth.js +157 -0
- package/dist/lib/dns.d.ts +1 -1
- package/dist/lib/dns.js +19 -1
- package/dist/lib/dns.test.d.ts +1 -0
- package/dist/lib/dns.test.js +27 -0
- package/dist/lib/dockerHub.d.ts +12 -1
- package/dist/lib/dockerHub.js +18 -8
- package/dist/lib/helm.d.ts +4 -0
- package/dist/lib/helm.js +16 -0
- package/dist/lib/helmValues.d.ts +25 -0
- package/dist/lib/helmValues.js +1937 -259
- package/dist/lib/helmValues.test.d.ts +1 -0
- package/dist/lib/helmValues.test.js +966 -0
- package/dist/lib/htpasswd.d.ts +1 -0
- package/dist/lib/htpasswd.js +15 -0
- package/dist/lib/kubernetes.d.ts +126 -13
- package/dist/lib/kubernetes.js +624 -134
- package/dist/lib/secrets.d.ts +23 -0
- package/dist/lib/secrets.js +158 -0
- package/dist/lib/validateValues.d.ts +31 -0
- package/dist/lib/validateValues.js +253 -0
- package/dist/lib/versions.d.ts +82 -11
- package/dist/lib/versions.js +131 -31
- package/dist/lib/versions.test.d.ts +1 -0
- package/dist/lib/versions.test.js +81 -0
- package/dist/lib/wizardSteps.d.ts +14 -0
- package/dist/lib/wizardSteps.js +23 -0
- package/dist/lib/workloadIdentity.d.ts +26 -0
- package/dist/lib/workloadIdentity.js +323 -0
- package/dist/lib/workloadIdentity.test.d.ts +1 -0
- package/dist/lib/workloadIdentity.test.js +57 -0
- package/dist/types/index.d.ts +2152 -95
- package/dist/types/index.js +554 -286
- package/package.json +10 -4
- package/schema/values.schema.json +1934 -0
- package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
- package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
- package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
- package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
- package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
- package/dist/components/Wizard/steps/TierStep.js +0 -29
- package/dist/lib/terraform.d.ts +0 -66
- package/dist/lib/terraform.js +0 -754
- package/terraform/aws/main.tf +0 -355
- package/terraform/azure/main.tf +0 -371
- package/terraform/gcp/main.tf +0 -407
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
targetScope = 'resourceGroup'
|
|
2
|
+
|
|
3
|
+
@description('Name of the AKS cluster.')
|
|
4
|
+
param clusterName string = 'rulebricks-cluster'
|
|
5
|
+
|
|
6
|
+
@description('Azure region for all resources.')
|
|
7
|
+
param location string = resourceGroup().location
|
|
8
|
+
|
|
9
|
+
@description('AKS Kubernetes version.')
|
|
10
|
+
param kubernetesVersion string = '1.34'
|
|
11
|
+
|
|
12
|
+
@description('Number of nodes in the default node pool.')
|
|
13
|
+
param nodeCount int = 2
|
|
14
|
+
|
|
15
|
+
@description('Maximum number of nodes in the default (core) pool. Core services need only 2-4 small nodes; burst capacity lives in the dedicated burst pool, so keeping this ceiling low also steers the autoscaler toward the burst pool when the worker fleet scales out.')
|
|
16
|
+
param maxNodeCount int = 4
|
|
17
|
+
|
|
18
|
+
@description('VM size for the default node pool.')
|
|
19
|
+
param nodeVmSize string = 'Standard_F4as_v6'
|
|
20
|
+
|
|
21
|
+
@description('Maximum pods per node in the default node pool.')
|
|
22
|
+
@minValue(10)
|
|
23
|
+
@maxValue(250)
|
|
24
|
+
param maxPods int = 110
|
|
25
|
+
|
|
26
|
+
@description('OS disk size in GB for the default node pool. 64+ keeps image churn and container ephemeral usage safely under the kubelet disk-pressure eviction threshold (~85%); 30GB disks ran at 65-82% under load.')
|
|
27
|
+
@minValue(30)
|
|
28
|
+
@maxValue(2048)
|
|
29
|
+
param osDiskSizeGB int = 64
|
|
30
|
+
|
|
31
|
+
@description('OS disk type for the default node pool.')
|
|
32
|
+
@allowed([
|
|
33
|
+
'Managed'
|
|
34
|
+
'Ephemeral'
|
|
35
|
+
])
|
|
36
|
+
param osDiskType string = 'Managed'
|
|
37
|
+
|
|
38
|
+
@description('Provision the dedicated burst worker pool: one large VM, scale 0->burstMaxCount, Deallocate scale-down (parked at disk-only cost with images cached, ~30-60s resume). Labeled and tainted rulebricks.com/pool=burst; the Rulebricks chart makes workers tolerate and softly prefer it out of the box.')
|
|
39
|
+
param enableBurstPool bool = true
|
|
40
|
+
|
|
41
|
+
@description('VM size for the burst worker pool. Default 16 vCPU (the Fas_v6 family has no 24-vCPU size): 2x4 vCPU core floor + 16 = 24 vCPU running steady-state at full burst, and exactly 32 vCPU even with the core pool at its 4-node max - sized to a 32-vCPU family quota. One big node beats many small ones for bang-bang scaling: one start event, one image set, no straggler tail.')
|
|
42
|
+
param burstVmSize string = 'Standard_F16as_v6'
|
|
43
|
+
|
|
44
|
+
@description('Maximum nodes in the burst pool.')
|
|
45
|
+
param burstMaxCount int = 1
|
|
46
|
+
|
|
47
|
+
// This template is deployment-independent: it provisions the shared identity,
|
|
48
|
+
// storage, and Azure Monitor resources but NOT the federated identity
|
|
49
|
+
// credentials, which are namespace-scoped. The Rulebricks CLI creates the
|
|
50
|
+
// per-deployment federated credentials at `rulebricks deploy` time (it knows the
|
|
51
|
+
// namespace and ServiceAccounts), so one cluster can host many deployments
|
|
52
|
+
// without re-running this template.
|
|
53
|
+
|
|
54
|
+
@description('Enable a user-assigned identity and federated credential for external-dns with Azure DNS.')
|
|
55
|
+
param enableExternalDns bool = false
|
|
56
|
+
|
|
57
|
+
@description('Resource group containing the Azure DNS zone. Required when enableExternalDns is true.')
|
|
58
|
+
param dnsZoneResourceGroup string = ''
|
|
59
|
+
|
|
60
|
+
@description('Namespace for the external-dns federated credential. Only used when enableExternalDns is true; set it to the CLI deployment namespace (rulebricks-<deploymentName>). The core vector/backup/prometheus credentials are created by the CLI at deploy time and do not use this.')
|
|
61
|
+
param rulebricksNamespace string = 'rulebricks'
|
|
62
|
+
|
|
63
|
+
// ----------------------------------------------------------------------------
|
|
64
|
+
// OBJECT STORAGE (all Rulebricks data)
|
|
65
|
+
//
|
|
66
|
+
// One identity, one storage account, one container. Decision logs and database
|
|
67
|
+
// backups are just key prefixes within it (decision-logs/ and db-backups/), so
|
|
68
|
+
// adding more data types later never means another identity or container.
|
|
69
|
+
//
|
|
70
|
+
// createStorage = true -> this template provisions the storage account and the
|
|
71
|
+
// single data container (turnkey; deterministic
|
|
72
|
+
// globally-unique account name via uniqueString()).
|
|
73
|
+
// createStorage = false -> bring your own: set existingStorageAccountName.
|
|
74
|
+
// ----------------------------------------------------------------------------
|
|
75
|
+
@description('Provision a storage account + the single data container in this template (turnkey). Set false to bring your own.')
|
|
76
|
+
param createStorage bool = true
|
|
77
|
+
|
|
78
|
+
@description('BYO: existing storage account for all Rulebricks data. Required when createStorage is false and decision-log or backup export is enabled.')
|
|
79
|
+
param existingStorageAccountName string = ''
|
|
80
|
+
|
|
81
|
+
@description('Blob container holding all Rulebricks data (decision-logs/ and db-backups/ prefixes).')
|
|
82
|
+
param dataContainerName string = '${clusterName}-data'
|
|
83
|
+
|
|
84
|
+
@description('Enable Vector decision-log export to Blob (federates the Rulebricks identity to the vector ServiceAccount).')
|
|
85
|
+
param enableDecisionLogExport bool = false
|
|
86
|
+
|
|
87
|
+
@description('Enable database backup export to Blob (federates the Rulebricks identity to the backup ServiceAccount).')
|
|
88
|
+
param enableBackupExport bool = false
|
|
89
|
+
|
|
90
|
+
// ----------------------------------------------------------------------------
|
|
91
|
+
// METRICS (Prometheus remote write -> Azure Monitor managed Prometheus)
|
|
92
|
+
//
|
|
93
|
+
// createMonitorWorkspace = true -> provision Azure Monitor workspace + an
|
|
94
|
+
// explicit Data Collection Endpoint + Data Collection Rule in THIS resource
|
|
95
|
+
// group, so the Monitoring Metrics Publisher role can be scoped to a DCR we
|
|
96
|
+
// own and name. (Creating only the workspace would auto-spawn a DCR/DCE in a
|
|
97
|
+
// separate MA_<name>_<region>_managed RG that this template can't cleanly
|
|
98
|
+
// scope a role to.)
|
|
99
|
+
// createMonitorWorkspace = false -> bring your own: set existingDataCollectionRuleId.
|
|
100
|
+
// ----------------------------------------------------------------------------
|
|
101
|
+
@description('Provision Azure Monitor workspace + DCE + DCR in this template (turnkey). Set false to bring your own DCR.')
|
|
102
|
+
param createMonitorWorkspace bool = true
|
|
103
|
+
|
|
104
|
+
@description('BYO: resource ID of an existing DCR associated with an Azure Monitor workspace. Required when createMonitorWorkspace is false and metrics remote write is enabled. The Monitoring Metrics Publisher role is assigned on this DCR.')
|
|
105
|
+
param existingDataCollectionRuleId string = ''
|
|
106
|
+
|
|
107
|
+
@description('Enable identity + role for Prometheus remote write to Azure Monitor.')
|
|
108
|
+
param enableMetricsRemoteWrite bool = false
|
|
109
|
+
|
|
110
|
+
var networkContributorRoleId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
|
|
111
|
+
var dnsZoneContributorRoleId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')
|
|
112
|
+
var storageBlobDataContributorRoleId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
|
|
113
|
+
var monitoringMetricsPublisherRoleId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')
|
|
114
|
+
|
|
115
|
+
// Deterministic, globally-unique storage account name (3-24 chars, lowercase alphanumeric).
|
|
116
|
+
var generatedStorageAccountName = take('rb${uniqueString(resourceGroup().id, clusterName)}', 24)
|
|
117
|
+
var effectiveStorageAccountName = createStorage ? generatedStorageAccountName : existingStorageAccountName
|
|
118
|
+
var enableBlobAccess = enableDecisionLogExport || enableBackupExport
|
|
119
|
+
var monitorWorkspaceName = '${clusterName}-amw'
|
|
120
|
+
var dceName = '${clusterName}-dce'
|
|
121
|
+
var dcrName = '${clusterName}-dcr'
|
|
122
|
+
|
|
123
|
+
resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
|
|
124
|
+
name: '${clusterName}-vnet'
|
|
125
|
+
location: location
|
|
126
|
+
tags: {
|
|
127
|
+
Environment: 'rulebricks'
|
|
128
|
+
}
|
|
129
|
+
properties: {
|
|
130
|
+
addressSpace: {
|
|
131
|
+
addressPrefixes: [
|
|
132
|
+
'10.0.0.0/8'
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
subnets: [
|
|
136
|
+
{
|
|
137
|
+
name: 'aks-subnet'
|
|
138
|
+
properties: {
|
|
139
|
+
addressPrefix: '10.240.0.0/16'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {
|
|
147
|
+
name: '${clusterName}-nsg'
|
|
148
|
+
location: location
|
|
149
|
+
tags: {
|
|
150
|
+
Environment: 'rulebricks'
|
|
151
|
+
}
|
|
152
|
+
properties: {
|
|
153
|
+
securityRules: [
|
|
154
|
+
{
|
|
155
|
+
name: 'AllowVNetInbound'
|
|
156
|
+
properties: {
|
|
157
|
+
priority: 100
|
|
158
|
+
direction: 'Inbound'
|
|
159
|
+
access: 'Allow'
|
|
160
|
+
protocol: '*'
|
|
161
|
+
sourcePortRange: '*'
|
|
162
|
+
destinationPortRange: '*'
|
|
163
|
+
sourceAddressPrefix: 'VirtualNetwork'
|
|
164
|
+
destinationAddressPrefix: 'VirtualNetwork'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
{
|
|
168
|
+
name: 'AllowVNetOutbound'
|
|
169
|
+
properties: {
|
|
170
|
+
priority: 100
|
|
171
|
+
direction: 'Outbound'
|
|
172
|
+
access: 'Allow'
|
|
173
|
+
protocol: '*'
|
|
174
|
+
sourcePortRange: '*'
|
|
175
|
+
destinationPortRange: '*'
|
|
176
|
+
sourceAddressPrefix: 'VirtualNetwork'
|
|
177
|
+
destinationAddressPrefix: 'VirtualNetwork'
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
{
|
|
181
|
+
name: 'AllowHTTPInbound'
|
|
182
|
+
properties: {
|
|
183
|
+
priority: 110
|
|
184
|
+
direction: 'Inbound'
|
|
185
|
+
access: 'Allow'
|
|
186
|
+
protocol: 'Tcp'
|
|
187
|
+
sourcePortRange: '*'
|
|
188
|
+
destinationPortRange: '80'
|
|
189
|
+
sourceAddressPrefix: 'Internet'
|
|
190
|
+
destinationAddressPrefix: '*'
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
{
|
|
194
|
+
name: 'AllowHTTPSInbound'
|
|
195
|
+
properties: {
|
|
196
|
+
priority: 120
|
|
197
|
+
direction: 'Inbound'
|
|
198
|
+
access: 'Allow'
|
|
199
|
+
protocol: 'Tcp'
|
|
200
|
+
sourcePortRange: '*'
|
|
201
|
+
destinationPortRange: '443'
|
|
202
|
+
sourceAddressPrefix: 'Internet'
|
|
203
|
+
destinationAddressPrefix: '*'
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = {
|
|
211
|
+
parent: vnet
|
|
212
|
+
name: 'aks-subnet'
|
|
213
|
+
properties: {
|
|
214
|
+
addressPrefix: '10.240.0.0/16'
|
|
215
|
+
networkSecurityGroup: {
|
|
216
|
+
id: nsg.id
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
resource aksIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
|
|
222
|
+
name: '${clusterName}-identity'
|
|
223
|
+
location: location
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
resource aksNetworkRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
|
227
|
+
name: guid(vnet.id, aksIdentity.id, 'Network Contributor')
|
|
228
|
+
scope: vnet
|
|
229
|
+
properties: {
|
|
230
|
+
roleDefinitionId: networkContributorRoleId
|
|
231
|
+
principalId: aksIdentity.properties.principalId
|
|
232
|
+
principalType: 'ServicePrincipal'
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
resource aks 'Microsoft.ContainerService/managedClusters@2024-05-01' = {
|
|
237
|
+
name: clusterName
|
|
238
|
+
location: location
|
|
239
|
+
tags: {
|
|
240
|
+
Environment: 'rulebricks'
|
|
241
|
+
}
|
|
242
|
+
identity: {
|
|
243
|
+
type: 'UserAssigned'
|
|
244
|
+
userAssignedIdentities: {
|
|
245
|
+
'${aksIdentity.id}': {}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
properties: {
|
|
249
|
+
dnsPrefix: clusterName
|
|
250
|
+
kubernetesVersion: kubernetesVersion
|
|
251
|
+
agentPoolProfiles: concat(
|
|
252
|
+
[
|
|
253
|
+
{
|
|
254
|
+
name: 'default'
|
|
255
|
+
count: nodeCount
|
|
256
|
+
enableAutoScaling: true
|
|
257
|
+
minCount: nodeCount
|
|
258
|
+
maxCount: maxNodeCount
|
|
259
|
+
vmSize: nodeVmSize
|
|
260
|
+
maxPods: maxPods
|
|
261
|
+
osDiskSizeGB: osDiskSizeGB
|
|
262
|
+
osDiskType: osDiskType
|
|
263
|
+
osType: 'Linux'
|
|
264
|
+
type: 'VirtualMachineScaleSets'
|
|
265
|
+
mode: 'System'
|
|
266
|
+
// Scale-down parks VMs (stopped, disk-only cost) instead of
|
|
267
|
+
// deleting them; resume is ~30-60s with container images cached.
|
|
268
|
+
scaleDownMode: 'Deallocate'
|
|
269
|
+
vnetSubnetID: subnet.id
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
enableBurstPool
|
|
273
|
+
? [
|
|
274
|
+
{
|
|
275
|
+
// Dedicated burst capacity for the stateless worker fleet:
|
|
276
|
+
// one large VM that parks (Deallocate) between bursts. The
|
|
277
|
+
// taint keeps everything except workers off it; the label is
|
|
278
|
+
// what the chart's soft node affinity targets. First-ever
|
|
279
|
+
// burst cold-provisions (~2-4 min); thereafter resume is
|
|
280
|
+
// ~30-60s with images cached.
|
|
281
|
+
name: 'burst'
|
|
282
|
+
count: 0
|
|
283
|
+
enableAutoScaling: true
|
|
284
|
+
minCount: 0
|
|
285
|
+
maxCount: burstMaxCount
|
|
286
|
+
vmSize: burstVmSize
|
|
287
|
+
maxPods: maxPods
|
|
288
|
+
osDiskSizeGB: osDiskSizeGB
|
|
289
|
+
osDiskType: osDiskType
|
|
290
|
+
osType: 'Linux'
|
|
291
|
+
type: 'VirtualMachineScaleSets'
|
|
292
|
+
mode: 'User'
|
|
293
|
+
scaleDownMode: 'Deallocate'
|
|
294
|
+
nodeLabels: {
|
|
295
|
+
'rulebricks.com/pool': 'burst'
|
|
296
|
+
}
|
|
297
|
+
nodeTaints: [
|
|
298
|
+
'rulebricks.com/pool=burst:NoSchedule'
|
|
299
|
+
]
|
|
300
|
+
vnetSubnetID: subnet.id
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
: [],
|
|
304
|
+
)
|
|
305
|
+
// Tuned for bursty traffic: detect pending pods quickly and pick the
|
|
306
|
+
// node pool that wastes the least capacity. With the core pool capped at
|
|
307
|
+
// maxNodeCount (4), a scaled-out worker fleet overflows to the burst
|
|
308
|
+
// pool within 1-2 autoscaler iterations. Scale-down keeps defaults -
|
|
309
|
+
// gaps between bursts should not thrash nodes.
|
|
310
|
+
autoScalerProfile: {
|
|
311
|
+
'scan-interval': '10s'
|
|
312
|
+
expander: 'least-waste'
|
|
313
|
+
}
|
|
314
|
+
networkProfile: {
|
|
315
|
+
networkPlugin: 'azure'
|
|
316
|
+
networkPolicy: 'calico'
|
|
317
|
+
loadBalancerSku: 'standard'
|
|
318
|
+
serviceCidr: '10.0.0.0/16'
|
|
319
|
+
dnsServiceIP: '10.0.0.10'
|
|
320
|
+
}
|
|
321
|
+
oidcIssuerProfile: {
|
|
322
|
+
enabled: true
|
|
323
|
+
}
|
|
324
|
+
securityProfile: {
|
|
325
|
+
workloadIdentity: {
|
|
326
|
+
enabled: true
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
storageProfile: {
|
|
330
|
+
diskCSIDriver: {
|
|
331
|
+
enabled: true
|
|
332
|
+
}
|
|
333
|
+
fileCSIDriver: {
|
|
334
|
+
enabled: true
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
dependsOn: [
|
|
339
|
+
aksNetworkRole
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
resource externalDnsIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (enableExternalDns) {
|
|
344
|
+
name: '${clusterName}-external-dns'
|
|
345
|
+
location: location
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
resource externalDnsRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableExternalDns && (empty(dnsZoneResourceGroup) || dnsZoneResourceGroup == resourceGroup().name)) {
|
|
349
|
+
name: guid(resourceGroup().id, externalDnsIdentity!.id, 'DNS Zone Contributor')
|
|
350
|
+
scope: resourceGroup()
|
|
351
|
+
properties: {
|
|
352
|
+
roleDefinitionId: dnsZoneContributorRoleId
|
|
353
|
+
principalId: externalDnsIdentity!.properties.principalId
|
|
354
|
+
principalType: 'ServicePrincipal'
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
resource externalDnsFederatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = if (enableExternalDns) {
|
|
359
|
+
parent: externalDnsIdentity
|
|
360
|
+
name: 'external-dns'
|
|
361
|
+
properties: {
|
|
362
|
+
issuer: aks.properties.oidcIssuerProfile.issuerURL
|
|
363
|
+
subject: 'system:serviceaccount:${rulebricksNamespace}:external-dns'
|
|
364
|
+
audiences: [
|
|
365
|
+
'api://AzureADTokenExchange'
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ----------------------------------------------------------------------------
|
|
371
|
+
// STORAGE ACCOUNT + CONTAINERS (created only when createStorage = true)
|
|
372
|
+
// ----------------------------------------------------------------------------
|
|
373
|
+
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = if (createStorage) {
|
|
374
|
+
name: generatedStorageAccountName
|
|
375
|
+
location: location
|
|
376
|
+
sku: {
|
|
377
|
+
name: 'Standard_LRS'
|
|
378
|
+
}
|
|
379
|
+
kind: 'StorageV2'
|
|
380
|
+
properties: {
|
|
381
|
+
accessTier: 'Hot'
|
|
382
|
+
allowBlobPublicAccess: false
|
|
383
|
+
minimumTlsVersion: 'TLS1_2'
|
|
384
|
+
supportsHttpsTrafficOnly: true
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = if (createStorage) {
|
|
389
|
+
parent: storageAccount
|
|
390
|
+
name: 'default'
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
resource dataContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = if (createStorage && enableBlobAccess) {
|
|
394
|
+
parent: blobService
|
|
395
|
+
name: dataContainerName
|
|
396
|
+
properties: {
|
|
397
|
+
publicAccess: 'None'
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Existing-account reference for BYO (createStorage = false)
|
|
402
|
+
resource existingStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = if (!createStorage && enableBlobAccess && !empty(effectiveStorageAccountName)) {
|
|
403
|
+
name: effectiveStorageAccountName
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ----------------------------------------------------------------------------
|
|
407
|
+
// RULEBRICKS WORKLOAD IDENTITY (single identity for all data paths)
|
|
408
|
+
// Identity: ${clusterName}-rulebricks
|
|
409
|
+
// Roles: Storage Blob Data Contributor (logs + backups) on the storage
|
|
410
|
+
// account, and Monitoring Metrics Publisher on the DCR (below).
|
|
411
|
+
// Federation to the vector / backup / prometheus ServiceAccounts is created by
|
|
412
|
+
// the Rulebricks CLI at deploy time (namespace-scoped), so this one identity can
|
|
413
|
+
// back any number of deployments on the cluster.
|
|
414
|
+
// ----------------------------------------------------------------------------
|
|
415
|
+
resource rulebricksIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
|
|
416
|
+
name: '${clusterName}-rulebricks'
|
|
417
|
+
location: location
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
resource blobRoleCreated 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableBlobAccess && createStorage) {
|
|
421
|
+
name: guid(storageAccount.id, rulebricksIdentity.id, 'Storage Blob Data Contributor')
|
|
422
|
+
scope: storageAccount
|
|
423
|
+
properties: {
|
|
424
|
+
roleDefinitionId: storageBlobDataContributorRoleId
|
|
425
|
+
principalId: rulebricksIdentity.properties.principalId
|
|
426
|
+
principalType: 'ServicePrincipal'
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
resource blobRoleByo 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableBlobAccess && !createStorage && !empty(effectiveStorageAccountName)) {
|
|
431
|
+
name: guid(existingStorageAccount.id, rulebricksIdentity.id, 'Storage Blob Data Contributor')
|
|
432
|
+
scope: existingStorageAccount
|
|
433
|
+
properties: {
|
|
434
|
+
roleDefinitionId: storageBlobDataContributorRoleId
|
|
435
|
+
principalId: rulebricksIdentity.properties.principalId
|
|
436
|
+
principalType: 'ServicePrincipal'
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Federated credentials for vector / backup / prometheus are created by the
|
|
441
|
+
// Rulebricks CLI at deploy time (they are namespace-scoped). This template only
|
|
442
|
+
// grants the identity its roles.
|
|
443
|
+
|
|
444
|
+
// ----------------------------------------------------------------------------
|
|
445
|
+
// METRICS PATH (same Rulebricks identity)
|
|
446
|
+
// Federated: created by the CLI at deploy time for SA ${rulebricksNamespace}:prometheus
|
|
447
|
+
// Role: Monitoring Metrics Publisher on the DCR
|
|
448
|
+
// Note: role takes ~30 min to propagate; expect HTTP 403 until then.
|
|
449
|
+
//
|
|
450
|
+
// createMonitorWorkspace = true provisions AMW + DCE + DCR here so the role is
|
|
451
|
+
// scoped to a DCR we own. The remote-write endpoint for Prometheus is the DCE
|
|
452
|
+
// metricsIngestion endpoint (surfaced as an output).
|
|
453
|
+
// ----------------------------------------------------------------------------
|
|
454
|
+
resource monitorWorkspace 'Microsoft.Monitor/accounts@2023-04-03' = if (enableMetricsRemoteWrite && createMonitorWorkspace) {
|
|
455
|
+
name: monitorWorkspaceName
|
|
456
|
+
location: location
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
resource dce 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = if (enableMetricsRemoteWrite && createMonitorWorkspace) {
|
|
460
|
+
name: dceName
|
|
461
|
+
location: location
|
|
462
|
+
kind: 'Linux'
|
|
463
|
+
properties: {}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = if (enableMetricsRemoteWrite && createMonitorWorkspace) {
|
|
467
|
+
name: dcrName
|
|
468
|
+
location: location
|
|
469
|
+
kind: 'Linux'
|
|
470
|
+
properties: {
|
|
471
|
+
dataCollectionEndpointId: dce.id
|
|
472
|
+
dataSources: {
|
|
473
|
+
prometheusForwarder: [
|
|
474
|
+
{
|
|
475
|
+
name: 'PrometheusDataSource'
|
|
476
|
+
streams: [
|
|
477
|
+
'Microsoft-PrometheusMetrics'
|
|
478
|
+
]
|
|
479
|
+
labelIncludeFilter: {}
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
}
|
|
483
|
+
destinations: {
|
|
484
|
+
monitoringAccounts: [
|
|
485
|
+
{
|
|
486
|
+
accountResourceId: monitorWorkspace.id
|
|
487
|
+
name: 'MonitoringAccountDestination'
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
dataFlows: [
|
|
492
|
+
{
|
|
493
|
+
streams: [
|
|
494
|
+
'Microsoft-PrometheusMetrics'
|
|
495
|
+
]
|
|
496
|
+
destinations: [
|
|
497
|
+
'MonitoringAccountDestination'
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
]
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// BYO DCR reference (createMonitorWorkspace = false)
|
|
505
|
+
resource existingDcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' existing = if (enableMetricsRemoteWrite && !createMonitorWorkspace && !empty(existingDataCollectionRuleId)) {
|
|
506
|
+
name: last(split(existingDataCollectionRuleId, '/'))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
resource metricsPublisherRoleCreated 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableMetricsRemoteWrite && createMonitorWorkspace) {
|
|
510
|
+
name: guid(dcr.id, rulebricksIdentity.id, 'Monitoring Metrics Publisher')
|
|
511
|
+
scope: dcr
|
|
512
|
+
properties: {
|
|
513
|
+
roleDefinitionId: monitoringMetricsPublisherRoleId
|
|
514
|
+
principalId: rulebricksIdentity.properties.principalId
|
|
515
|
+
principalType: 'ServicePrincipal'
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
resource metricsPublisherRoleByo 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableMetricsRemoteWrite && !createMonitorWorkspace && !empty(existingDataCollectionRuleId)) {
|
|
520
|
+
name: guid(existingDataCollectionRuleId, rulebricksIdentity.id, 'Monitoring Metrics Publisher')
|
|
521
|
+
scope: existingDcr
|
|
522
|
+
properties: {
|
|
523
|
+
roleDefinitionId: monitoringMetricsPublisherRoleId
|
|
524
|
+
principalId: rulebricksIdentity.properties.principalId
|
|
525
|
+
principalType: 'ServicePrincipal'
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
output clusterName string = aks.name
|
|
530
|
+
output resourceGroupName string = resourceGroup().name
|
|
531
|
+
output location string = location
|
|
532
|
+
output kubeconfigCommand string = 'az aks get-credentials --name ${clusterName} --resource-group ${resourceGroup().name}'
|
|
533
|
+
output externalDnsClientId string = enableExternalDns ? externalDnsIdentity!.properties.clientId : ''
|
|
534
|
+
|
|
535
|
+
// Storage outputs. One identity + one account + one container back every data
|
|
536
|
+
// path; decision logs and backups are prefixes within the container.
|
|
537
|
+
output storageAccountName string = effectiveStorageAccountName
|
|
538
|
+
output dataContainer string = enableBlobAccess ? dataContainerName : ''
|
|
539
|
+
output rulebricksClientId string = rulebricksIdentity.properties.clientId
|
|
540
|
+
|
|
541
|
+
// Metrics outputs (Prometheus remote write uses the same rulebricksClientId).
|
|
542
|
+
// DCE ingestion endpoint + DCR immutableId. The Prometheus remote_write URL is:
|
|
543
|
+
// <dceMetricsIngestionEndpoint>/dataCollectionRules/<dcrImmutableId>/streams/Microsoft-PrometheusMetrics/api/v1/write?api-version=2023-04-24
|
|
544
|
+
output dceMetricsIngestionEndpoint string = (enableMetricsRemoteWrite && createMonitorWorkspace) ? dce!.properties.metricsIngestion.endpoint : ''
|
|
545
|
+
output dcrImmutableId string = (enableMetricsRemoteWrite && createMonitorWorkspace) ? dcr!.properties.immutableId : ''
|
|
546
|
+
output dataCollectionRuleId string = (enableMetricsRemoteWrite && createMonitorWorkspace) ? dcr!.id : existingDataCollectionRuleId
|