|
48 | 48 | "visible": true |
49 | 49 | }, |
50 | 50 | { |
51 | | - "name": "dataExplorer", |
| 51 | + "name": "analyticsBackend", |
52 | 52 | "type": "Microsoft.Common.Section", |
53 | | - "label": "Azure Data Explorer (optional)", |
| 53 | + "label": "Analytics backend", |
54 | 54 | "elements": [ |
55 | 55 | { |
56 | | - "name": "dataExplorerIntro", |
| 56 | + "name": "analyticsIntro", |
57 | 57 | "type": "Microsoft.Common.TextBlock", |
58 | 58 | "visible": true, |
59 | 59 | "options": { |
60 | | - "text": "Azure Data Explorer is a fast, scalable service for advanced, big data analytics. Data Explorer is optional but recommended when monitoring more than $100K in spend. FinOps hubs with Data Explorer starts at $120/mo for a single node cluster plus $10/mo per million in monitored spend (<0.02% of annual spend)." |
| 60 | + "text": "Select how this hub will process and store data. Azure Data Explorer or Microsoft Fabric are recommended for medium to large accounts. Select Remote hub to push data to a central FinOps hub in another tenant. Only select storage-only for small accounts with simple requirements." |
| 61 | + } |
| 62 | + }, |
| 63 | + { |
| 64 | + "name": "analyticsEngine", |
| 65 | + "type": "Microsoft.Common.OptionsGroup", |
| 66 | + "label": "Hub mode", |
| 67 | + "defaultValue": "Azure Data Explorer", |
| 68 | + "toolTip": "Select the analytics backend for this hub. Azure Data Explorer and Microsoft Fabric provide analytics. None uses storage only for Power BI reports. Remote Hub forwards data to a central hub for cross-tenant scenarios.", |
| 69 | + "constraints": { |
| 70 | + "allowedValues": [ |
| 71 | + { |
| 72 | + "label": "Azure Data Explorer", |
| 73 | + "value": "adx" |
| 74 | + }, |
| 75 | + { |
| 76 | + "label": "Microsoft Fabric", |
| 77 | + "value": "fabric" |
| 78 | + }, |
| 79 | + { |
| 80 | + "label": "None (storage only)", |
| 81 | + "value": "none" |
| 82 | + }, |
| 83 | + { |
| 84 | + "label": "Remote hub", |
| 85 | + "value": "remote" |
| 86 | + } |
| 87 | + ], |
| 88 | + "required": true |
| 89 | + }, |
| 90 | + "visible": true |
| 91 | + }, |
| 92 | + { |
| 93 | + "name": "dataExplorerIntro", |
| 94 | + "type": "Microsoft.Common.TextBlock", |
| 95 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]", |
| 96 | + "options": { |
| 97 | + "text": "Azure Data Explorer is a fast, scalable service for advanced, big data analytics. Recommended when monitoring more than $100K in spend. Starts at $120/mo for a single node cluster plus $10/mo per million in monitored spend (<0.02% of annual spend)." |
61 | 98 | } |
62 | 99 | }, |
63 | 100 | { |
64 | 101 | "name": "dataExplorerName", |
65 | 102 | "type": "Microsoft.Common.TextBox", |
66 | 103 | "label": "Cluster name", |
67 | | - "toolTip": "Name of the Azure Data Explorer cluster, if desired. If not specified, Data Explorer will not be deployed.", |
| 104 | + "toolTip": "Name of the Azure Data Explorer cluster.", |
68 | 105 | "constraints": { |
69 | | - "required": false, |
| 106 | + "required": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]", |
70 | 107 | "regex": "^[a-zA-Z0-9][a-zA-Z0-9\\-]{0,20}[a-z0-9]$", |
71 | 108 | "validationMessage": "Name must be between 4 and 22 characters long and can contain only lowercase letters, numbers, and hyphens. The first and last characters in the name must be alphanumeric." |
72 | 109 | }, |
73 | | - "visible": true |
74 | | - } |
75 | | - ], |
76 | | - "visible": true |
77 | | - }, |
78 | | - { |
79 | | - "name": "fabric", |
80 | | - "type": "Microsoft.Common.Section", |
81 | | - "label": "Microsoft Fabric (optional)", |
82 | | - "elements": [ |
| 110 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]" |
| 111 | + }, |
83 | 112 | { |
84 | 113 | "name": "fabricIntro", |
85 | 114 | "type": "Microsoft.Common.TextBlock", |
86 | | - "visible": true, |
| 115 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'fabric')]", |
87 | 116 | "options": { |
88 | | - "text": "Microsoft Fabric is a unified data platform for data ingestion, processing, and analytics. FinOps hubs with Fabric starts at $300/mo for an F2 SKU plus $10/mo per million in monitored spend." |
| 117 | + "text": "Microsoft Fabric is a unified data platform for data ingestion, processing, and analytics. Starts at $300/mo for an F2 SKU plus $10/mo per million in monitored spend." |
89 | 118 | } |
90 | 119 | }, |
91 | 120 | { |
92 | 121 | "name": "fabricQueryUri", |
93 | 122 | "type": "Microsoft.Common.TextBox", |
94 | 123 | "label": "Fabric eventhouse query URI", |
95 | | - "toolTip": "Query URI for the Microsoft Fabric eventhouse, if applicable.", |
| 124 | + "toolTip": "Query URI for the Microsoft Fabric eventhouse.", |
96 | 125 | "constraints": { |
97 | | - "required": false, |
| 126 | + "required": "[equals(basics('analyticsBackend').analyticsEngine, 'fabric')]", |
98 | 127 | "regex": "^https://.*", |
99 | 128 | "validationMessage": "Query URI must be a full Microsoft Fabric eventhouse URL." |
100 | 129 | }, |
101 | | - "visible": true |
| 130 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'fabric')]" |
102 | 131 | }, |
103 | 132 | { |
104 | 133 | "name": "fabricCapacity", |
|
123 | 152 | { "label": "F2048", "value": "2048" } |
124 | 153 | ] |
125 | 154 | }, |
126 | | - "visible": true |
| 155 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'fabric')]" |
| 156 | + }, |
| 157 | + { |
| 158 | + "name": "remoteHubIntro", |
| 159 | + "type": "Microsoft.Common.TextBlock", |
| 160 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]", |
| 161 | + "options": { |
| 162 | + "text": "Configure this hub as a satellite that sends data to a remote FinOps hub in another tenant or subscription. This enables cross-tenant cost management scenarios where a central hub collects cost data from multiple tenants." |
| 163 | + } |
| 164 | + }, |
| 165 | + { |
| 166 | + "name": "remoteHubStorageUri", |
| 167 | + "type": "Microsoft.Common.TextBox", |
| 168 | + "label": "Remote hub storage URI", |
| 169 | + "toolTip": "Data Lake storage endpoint from the remote hub storage account. Copy from the storage account Settings > Endpoints > Data Lake storage. Example: https://myremotehub.dfs.core.windows.net/", |
| 170 | + "constraints": { |
| 171 | + "required": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]", |
| 172 | + "regex": "^https://.*\\.dfs\\.core\\.windows\\.net/?$", |
| 173 | + "validationMessage": "Must be a valid Data Lake storage endpoint URL in the format: https://storageaccount.dfs.core.windows.net/" |
| 174 | + }, |
| 175 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]" |
| 176 | + }, |
| 177 | + { |
| 178 | + "name": "remoteHubStorageKey", |
| 179 | + "type": "Microsoft.Common.PasswordBox", |
| 180 | + "label": { |
| 181 | + "password": "Remote hub storage key" |
| 182 | + }, |
| 183 | + "toolTip": "Storage account access key for the remote hub. Copy from the remote hub storage account Security + networking > Access keys > key1/2 > Key.", |
| 184 | + "constraints": { |
| 185 | + "required": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]", |
| 186 | + "regex": "^[A-Za-z0-9+/]{86}==$", |
| 187 | + "validationMessage": "Must be a valid storage account access key (base64 encoded, ending with ==)" |
| 188 | + }, |
| 189 | + "options": { |
| 190 | + "hideConfirmation": true |
| 191 | + }, |
| 192 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]" |
| 193 | + }, |
| 194 | + { |
| 195 | + "name": "remoteHubSecurityNote", |
| 196 | + "type": "Microsoft.Common.TextBlock", |
| 197 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]", |
| 198 | + "options": { |
| 199 | + "text": "The storage key will be securely stored in Azure Key Vault and is never logged or exposed in deployment outputs." |
| 200 | + } |
| 201 | + }, |
| 202 | + { |
| 203 | + "name": "enablePurgeProtection", |
| 204 | + "type": "Microsoft.Common.CheckBox", |
| 205 | + "label": "Enable Key Vault purge protection", |
| 206 | + "toolTip": "Enables purge protection for the Key Vault used to store the remote hub storage key. Purge protection prevents permanent deletion of the Key Vault for 90 days after deletion. Note: If the key is lost, you can regenerate it from the remote hub's storage account.", |
| 207 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]" |
127 | 208 | } |
128 | 209 | ], |
129 | 210 | "visible": true |
|
190 | 271 | { |
191 | 272 | "name": "dataExplorer", |
192 | 273 | "type": "Microsoft.Common.Section", |
193 | | - "label": "Data Explorer (optional)", |
| 274 | + "label": "Data Explorer", |
194 | 275 | "elements": [ |
195 | 276 | { |
196 | 277 | "name": "dataExplorerPricingIntro", |
197 | 278 | "type": "Microsoft.Common.TextBlock", |
198 | 279 | "visible": true, |
199 | 280 | "options": { |
200 | | - "text": "Azure Data Explorer is optional. If a cluster name is not specified on the Basics tab, this setting is ignored." |
| 281 | + "text": "Select the Azure Data Explorer SKU for your analytics workload." |
201 | 282 | } |
202 | 283 | }, |
203 | 284 | { |
|
478 | 559 | "visible": true |
479 | 560 | } |
480 | 561 | ], |
481 | | - "visible": true |
| 562 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]" |
482 | 563 | }, |
483 | 564 | { |
484 | 565 | "name": "spacer", |
|
505 | 586 | { |
506 | 587 | "name": "retentionIntro", |
507 | 588 | "type": "Microsoft.Common.TextBlock", |
508 | | - "visible": true, |
| 589 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]", |
509 | 590 | "options": { |
510 | 591 | "text": "Data retention settings indicate how long to keep data available for reporting or additional processing. Retained data contributes to monitored spend and the total storage costs for the hub instance." |
511 | 592 | } |
512 | 593 | }, |
| 594 | + { |
| 595 | + "name": "storageOnlyRetentionInfo", |
| 596 | + "type": "Microsoft.Common.TextBlock", |
| 597 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'none')]", |
| 598 | + "options": { |
| 599 | + "text": "Storage-only mode keeps all ingested data in Azure Data Lake Storage. Data is retained indefinitely until manually deleted. Use Power BI to connect directly to storage for reporting." |
| 600 | + } |
| 601 | + }, |
| 602 | + { |
| 603 | + "name": "fabricRetentionInfo", |
| 604 | + "type": "Microsoft.Common.TextBlock", |
| 605 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'fabric')]", |
| 606 | + "options": { |
| 607 | + "text": "Data retention for Microsoft Fabric is managed within your Fabric eventhouse. Configure retention policies directly in Fabric." |
| 608 | + } |
| 609 | + }, |
| 610 | + { |
| 611 | + "name": "remoteHubRetentionInfo", |
| 612 | + "type": "Microsoft.Common.TextBlock", |
| 613 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'remote')]", |
| 614 | + "options": { |
| 615 | + "text": "Remote Hub satellites forward data to the central hub. Data retention is managed by the central hub, not the satellite." |
| 616 | + } |
| 617 | + }, |
513 | 618 | { |
514 | 619 | "name": "storage", |
515 | 620 | "type": "Microsoft.Common.Section", |
|
521 | 626 | "type": "Microsoft.Common.TextBlock", |
522 | 627 | "visible": true, |
523 | 628 | "options": { |
524 | | - "text": "Storage retention is not implemented yet. You can set the retention period but files will not be automatically removed." |
| 629 | + "text": "Configure how long to retain data in Azure Data Lake Storage. Storage retention is not fully implemented yet - files will not be automatically removed." |
525 | 630 | } |
526 | 631 | }, |
527 | 632 | { |
|
555 | 660 | { |
556 | 661 | "name": "dataExplorer", |
557 | 662 | "type": "Microsoft.Common.Section", |
558 | | - "label": "Data Explorer (optional)", |
559 | | - "visible": true, |
| 663 | + "label": "Data Explorer", |
| 664 | + "visible": "[equals(basics('analyticsBackend').analyticsEngine, 'adx')]", |
560 | 665 | "elements": [ |
561 | 666 | { |
562 | 667 | "name": "dataExplorerIntro", |
563 | 668 | "type": "Microsoft.Common.TextBlock", |
564 | 669 | "visible": true, |
565 | 670 | "options": { |
566 | | - "text": "Azure Data Explorer is optional. If a cluster name is not specified on the Basics tab, this setting is ignored." |
| 671 | + "text": "Configure data retention settings for Azure Data Explorer." |
567 | 672 | } |
568 | 673 | }, |
569 | 674 | { |
|
692 | 797 | "message": "Address prefix must be a valid IPv4 address in the format 'XXX.XXX.XXX.XXX/YY' where YY is between 8 and 26 (minimum /26 subnet size required)." |
693 | 798 | } |
694 | 799 | ] |
695 | | - } |
696 | | - } |
697 | | - ], |
698 | | - "visible": true |
699 | | - }, |
700 | | - { |
701 | | - "name": "remoteHub", |
702 | | - "type": "Microsoft.Common.Section", |
703 | | - "label": "Remote hub configuration", |
704 | | - "elements": [ |
705 | | - { |
706 | | - "name": "remoteHubIntro", |
707 | | - "type": "Microsoft.Common.TextBlock", |
708 | | - "visible": true, |
709 | | - "options": { |
710 | | - "text": "Configure this hub to send data to a remote FinOps hub in another tenant or subscription. This enables cross-tenant cost management scenarios where a central tenant collects cost data from multiple tenants. Leave these fields empty if this is not a remote hub setup." |
711 | | - } |
712 | | - }, |
713 | | - { |
714 | | - "name": "remoteHubStorageUri", |
715 | | - "type": "Microsoft.Common.TextBox", |
716 | | - "label": "Remote hub storage URI", |
717 | | - "toolTip": "Data Lake storage endpoint from the remote hub storage account. Copy from the storage account Settings > Endpoints > Data Lake storage. Example: https://myremotehub.dfs.core.windows.net/", |
718 | | - "constraints": { |
719 | | - "required": false, |
720 | | - "regex": "^$|^https://.*\\.dfs\\.core\\.windows\\.net/?$", |
721 | | - "validationMessage": "Must be a valid Data Lake storage endpoint URL in the format: https://storageaccount.dfs.core.windows.net/" |
722 | | - }, |
723 | | - "visible": true |
724 | | - }, |
725 | | - { |
726 | | - "name": "remoteHubStorageKey", |
727 | | - "type": "Microsoft.Common.PasswordBox", |
728 | | - "label": { |
729 | | - "password": "Remote hub storage key" |
730 | 800 | }, |
731 | | - "toolTip": "Storage account access key for the remote hub. Copy from the remote hub storage account Security + networking > Access keys > key1/2 > Key.", |
732 | | - "constraints": { |
733 | | - "required": false, |
734 | | - "regex": "^$|^[A-Za-z0-9+/]{86}==$", |
735 | | - "validationMessage": "Must be a valid storage account access key (base64 encoded, ending with ==)" |
736 | | - }, |
737 | | - "options": { |
738 | | - "hideConfirmation": true |
739 | | - }, |
740 | | - "visible": true |
| 801 | + "visible": "[equals(steps('advanced').networking.enablePublicAccess, false)]" |
741 | 802 | } |
742 | 803 | ], |
743 | 804 | "visible": true |
|
772 | 833 | "hubName": "[basics('hubName')]", |
773 | 834 | "location": "[location()]", |
774 | 835 | "storageSku": "[steps('pricing').storage.storageSku]", |
775 | | - "dataExplorerName": "[basics('dataExplorer').dataExplorerName]", |
776 | | - "fabricQueryUri": "[basics('fabric').fabricQueryUri]", |
777 | | - "fabricCapacity": "[basics('fabric').fabricCapacity]", |
| 836 | + "dataExplorerName": "[if(equals(basics('analyticsBackend').analyticsEngine, 'adx'), basics('analyticsBackend').dataExplorerName, '')]", |
| 837 | + "fabricQueryUri": "[if(equals(basics('analyticsBackend').analyticsEngine, 'fabric'), basics('analyticsBackend').fabricQueryUri, '')]", |
| 838 | + "fabricCapacityUnits": "[if(equals(basics('analyticsBackend').analyticsEngine, 'fabric'), basics('analyticsBackend').fabricCapacity, '')]", |
778 | 839 | "enableInfrastructureEncryption": "[steps('advanced').storage.enableInfrastructureEncryption]", |
779 | 840 | "enableManagedExports": "[steps('advanced').managedExports.enableManagedExports]", |
780 | 841 | "enablePublicAccess": "[steps('advanced').networking.enablePublicAccess]", |
|
784 | 845 | "ingestionRetentionInMonths": "[steps('retention').storage.ingestionMonths]", |
785 | 846 | "dataExplorerRawRetentionInDays": "[steps('retention').dataExplorer.rawDays]", |
786 | 847 | "dataExplorerFinalRetentionInMonths": "[steps('retention').dataExplorer.finalMonths]", |
787 | | - "remoteHubStorageUri": "[steps('advanced').remoteHub.remoteHubStorageUri]", |
788 | | - "remoteHubStorageKey": "[steps('advanced').remoteHub.remoteHubStorageKey]", |
| 848 | + "remoteHubStorageUri": "[if(equals(basics('analyticsBackend').analyticsEngine, 'remote'), basics('analyticsBackend').remoteHubStorageUri, '')]", |
| 849 | + "remoteHubStorageKey": "[if(equals(basics('analyticsBackend').analyticsEngine, 'remote'), basics('analyticsBackend').remoteHubStorageKey, '')]", |
| 850 | + "enablePurgeProtection": "[if(equals(basics('analyticsBackend').analyticsEngine, 'remote'), coalesce(basics('analyticsBackend').enablePurgeProtection, false), false)]", |
789 | 851 | "tagsByResource": "[steps('tags').tagsByResource]" |
790 | 852 | } |
791 | 853 | } |
|
0 commit comments