Functions/Get-RSCClusters.ps1
################################################ # Function - Get-RSCClusters - Getting CDM Clusters attached to RSC ################################################ Function Get-RSCClusters { <# .SYNOPSIS A Rubrik Security Cloud (RSC) Reporting Module Function returning every Rubrik cluster and associated useful information. .DESCRIPTION Makes the required GraphQL API calls to RSC via Invoke-RestMethod to get the data as described, then creates a usable array of the returned information, removing the need for the PowerShell user to understand GraphQL in order to interact with RSC. .LINK GraphQL schema reference: https://rubrikinc.github.io/rubrik-api-documentation/schema/reference .OUTPUTS Returns an array of all the available information on the GraphQL endpoint in a uniform and usable format. .EXAMPLE Get-RSCClusters This example returns an array of all the information returned by the GraphQL endpoint for this object type. .NOTES Author: Joshua Stenhouse Date: 05/11/2023 #> ################################################ # Importing Module & Running Required Functions ################################################ # Importing the module is it needs other modules Import-Module RSCReporting # Checking connectivity, exiting function with error if not connected Test-RSCConnection # Getting objects $RSCObjects = Get-RSCObjects -Logging ################################################ # Getting All Clusters ################################################ # Building GraphQL query $RSCGraphQL = @{"query" = "query clusterConnection { clusterConnection { edges { node { connectivityLastUpdated defaultAddress encryptionEnabled estimatedRunway id isHealthy lastConnectionTime name passesConnectivityCheck productType registrationTime snapshotCount status type version licensedProducts timezone pauseStatus registeredMode clusterNodeConnection { nodes { brikId needsInspection id status ipAddress } count } clusterDiskConnection { count nodes { capacityBytes clusterId diskType id isEncrypted nodeId path status unallocatedBytes usableBytes } } state { connectedState clusterRemovalUpdatedAt clusterRemovalState clusterRemovalCreatedAt } metric { totalCapacity availableCapacity ingestedSnapshotStorage lastUpdateTime liveMountCapacity miscellaneousCapacity physicalSnapshotStorage snapshotCapacity usedCapacity } geoLocation { address latitude longitude } cdmUpgradeInfo { version versionStatus previousVersion } replicationSources { id sourceClusterAddress sourceClusterName sourceClusterUuid totalStorage } replicationTargets { id targetClusterAddress targetClusterName targetClusterUuid totalStorage } } } } }" } ################################################ # RSCReporting SDK ################################################ # Querying API $RSCClusterListResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQL | ConvertTo-JSON -Depth 20) -Headers $RSCSessionHeader # Setting variable $RSCClusterList += $RSCClusterListResponse.data.clusterConnection.edges.node # Getting all results from paginations While ($RSCClusterListResponse.data.clusterConnection.pageInfo.hasNextPage) { # Getting next set $RSCGraphQL.variables.after = $RSCClusterListResponse.data.clusterConnection.pageInfo.endCursor $RSCClusterListResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQL | ConvertTo-JSON -Depth 20) -Headers $RSCSessionHeader $RSCClusterList += $RSCClusterListResponse.data.clusterConnection.edges.node } ############################ # Starting For Each Cluster ############################ $RSCClusters = [System.Collections.ArrayList]@() ForEach ($RSCCluster in $RSCClusterList) { # Setting variables $Cluster = $RSCCluster.name $ClusterID = $RSCCluster.id $ClusterVersion = $RSCCluster.version $ClusterStatus = $RSCCluster.status $ClusterType = $RSCCluster.type $ClusterProduct = $RSCCluster.productType $ClusterEncrypted = $RSCCluster.encryptionEnabled $ClusterSnapshots = $RSCCluster.snapshotCount $ClusterRunwayDays = $RSCCluster.estimatedRunway $ClusterNodes = $RSCCluster.clusterNodeConnection.nodes $ClusterDisks = $RSCCluster.clusterDiskConnection.nodes $ClusterLocation = $RSCCluster.geoLocation $ClusterArhivalLocations = $RSCCluster.archivalLocations $ClusterReplicationTargets = $RSCCluster.replicationSources $ClusterReplicationSources = $RSCCluster.replicationTargets $ClusterLastConnectedUNIX = $RSCCluster.lastConnectionTime $ClusterUpgradeInfo = $RSCCluster.cdmUpgradeInfo $ClusterVersionStatus = $ClusterUpgradeInfo.versionStatus # Added 05/08/23 $ClusterTimezone = $RSCCluster.timezone $ClusterPauseStatus = $RSCCluster.pauseStatus $ClusterRegisteredUNIX = $RSCCluster.registrationTime IF($ClusterRegisteredUNIX -ne $null){$ClusterRegisteredUTC = Convert-RSCUNIXTime $ClusterRegisteredUNIX}ELSE{$ClusterRegisteredUTC = $null} # Converting IF($ClusterLastConnectedUNIX -ne $null){$ClusterLastConnectedUTC = Convert-RSCUNIXTime $ClusterLastConnectedUNIX}ELSE{$ClusterLastConnectedUTC = $null} $UTCDateTime = [System.DateTime]::UtcNow IF($ClusterLastConnectedUTC -ne $null){$ClusterLastConnectedTimespan = New-TimeSpan -Start $ClusterLastConnectedUTC -End $UTCDateTime;$ClusterLastConnectedHoursSince = $ClusterLastConnectedTimespan | Select-Object -ExpandProperty TotalHours;$ClusterLastConnectedHoursSince = [Math]::Round($ClusterLastConnectedHoursSince,1)}ELSE{$ClusterLastConnectedHoursSince = $null} IF($ClusterLastConnectedUTC -ne $null){$ClusterLastConnectedMinutesSince = $ClusterLastConnectedTimespan | Select-Object -ExpandProperty TotalMinutes;$ClusterLastConnectedMinutesSince = [Math]::Round($ClusterLastConnectedMinutesSince)}ELSE{$ClusterLastConnectedMinutesSince = $null} # Getting cluster location IF ($ClusterLocation -ne $null) { $ClusterAddress = $ClusterLocation.address $ClusterLatitude = $ClusterLocation.latitude $ClusterLongitude = $ClusterLocation.longitude } ############################ # Cluster Stats ############################ # Selecting storage Bytes $ClusterStorage = $RSCCluster.metric $ClusterTotalStorageBytes = $ClusterStorage.totalCapacity $ClusterUsedStorageBytes = $ClusterStorage.usedCapacity $ClusterFreeStorageBytes = $ClusterStorage.availableCapacity # Adding additional storage counts $ClusterSnapshotStorageBytes = $ClusterStorage.snapshotCapacity $ClusterLiveMountStorageBytes = $ClusterStorage.liveMountCapacity # Converting to GB $ClusterLiveMountStorageGB = $ClusterLiveMountStorageBytes / 1000 / 1000 / 1000 $ClusterLiveMountStorageGB = [Math]::Round($ClusterLiveMountStorageGB,2) $ClusterFreeStorageGB = $ClusterFreeStorageBytes / 1000 / 1000 / 1000 $ClusterFreeStorageGB = [Math]::Round($ClusterFreeStorageGB,2) # Converting to TB $ClusterTotalStorageTB = $ClusterTotalStorageBytes / 1000 / 1000 / 1000 / 1000 $ClusterUsedStorageTB = $ClusterUsedStorageBytes / 1000 / 1000 / 1000 / 1000 $ClusterFreeStorageTB = $ClusterFreeStorageBytes / 1000 / 1000 / 1000 / 1000 $ClusterSnapshotStorageTB = $ClusterSnapshotStorageBytes / 1000 / 1000 / 1000 / 1000 $ClusterLiveMountStorageTB = $ClusterLiveMountStorageBytes / 1000 / 1000 / 1000 / 1000 # Rounding to 2 decimal places $ClusterTotalStorageTB = [Math]::Round($ClusterTotalStorageTB,2) $ClusterUsedStorageTB = [Math]::Round($ClusterUsedStorageTB,2) $ClusterFreeStorageTB = [Math]::Round($ClusterFreeStorageTB,2) $ClusterSnapshotStorageTB = [Math]::Round($ClusterSnapshotStorageTB,2) $ClusterLiveMountStorageTB = [Math]::Round($ClusterLiveMountStorageTB,2) # Calculating percentage used space $ClusterUsedPercentage = ($ClusterUsedStorageTB/$ClusterTotalStorageTB).tostring("P1") $ClusterUsedPercentageInt = ($ClusterUsedStorageTB/$ClusterTotalStorageTB)*100 $ClusterUsedPercentageInt = [Math]::Round($ClusterUsedPercentageInt,2) # Calculating percentage free space $ClusterFreePercentage = ($ClusterFreeStorageTB/$ClusterTotalStorageTB).tostring("P1") $ClusterFreePercentageInt = ($ClusterFreeStorageTB/$ClusterTotalStorageTB)*100 $ClusterFreePercentageInt = [Math]::Round($ClusterFreePercentageInt,2) # Counts $ClusterNodesCount = $ClusterNodes | Measure-Object | Select-Object -ExpandProperty Count $ClusterDisksCount = $ClusterDisks | Measure-Object | Select-Object -ExpandProperty Count $ClusterArchiveTargetCount = $ClusterArhivalLocations | Measure-Object | Select-Object -ExpandProperty Count $ClusterReplicationTargetCount = $ClusterReplicationTargets | Measure-Object | Select-Object -ExpandProperty Count $ClusterReplicationSourceCount = $ClusterReplicationSources | Measure-Object | Select-Object -ExpandProperty Count # Counts by health $ClusterHealthyNodesCount = $ClusterNodes | Where-Object {$_.status -eq "OK"} | Measure-Object | Select-Object -ExpandProperty Count $ClusterBadNodesCount = $ClusterNodes | Where-Object {$_.status -ne "OK"} | Measure-Object | Select-Object -ExpandProperty Count $ClusterHealthyDisksCount = $ClusterDisks | Where-Object {$_.status -eq "ACTIVE"} | Measure-Object | Select-Object -ExpandProperty Count $ClusterBadDisksCount = $ClusterDisks | Where-Object {$_.status -ne "ACTIVE"} | Measure-Object | Select-Object -ExpandProperty Count ############################ # Cluster Protected Objects ############################ $RSCClusterObjects = $RSCObjects | Where-Object {$_.RubrikClusterID -eq $ClusterID} # Getting protected object count $RSCClusterProtectedObjects = $RSCClusterObjects | Where-Object {$_.ProtectionStatus -eq "Protected"} | Measure-Object | Select-Object -ExpandProperty Count $RSCClusterUnProtectedObjects = $RSCClusterObjects | Where-Object {$_.ProtectionStatus -eq "NoSla"} | Measure-Object | Select-Object -ExpandProperty Count $RSCClusterDoNotProtectedObjects = $RSCClusterObjects | Where-Object {$_.ProtectionStatus -eq "DoNotProtect"} | Measure-Object | Select-Object -ExpandProperty Count # Creating URL $ClusterClusterURL = $RSCURL + "/clusters/" + $ClusterID + "/overview" ############################ # Deciding Cluster Status More Intelligently than RSC! ############################ $ClusterStatus = "Healthy";$ClusterError = $null # Rule 1 - Bad nodes IF($ClusterBadNodesCount -gt 0){$ClusterStatus = "Degraded";$ClusterError = "Bad nodes"} # Rule 2 - Bad dsisks IF($ClusterBadDisksCount -gt 0){$ClusterStatus = "Degraded";$ClusterError = "Bad disks"} # Rule 3 - Less than 5 PC space free IF($ClusterFreePercentageInt -lt 10){$ClusterStatus = "Degraded";$ClusterError = "Low space"} # Rule 4 - Not connected in 6 hours IF($ClusterLastConnectedHoursSince -gt 6){$ClusterStatus = "Degraded";$ClusterError = "No connection";$ClusterConnectionStatus = "Disconnected"}ELSE{$ClusterConnectionStatus = "Connected"} ############################ # Adding To Array ############################ $Object = New-Object PSObject $Object | Add-Member -MemberType NoteProperty -Name "RSCInstance" -Value $RSCInstance $Object | Add-Member -MemberType NoteProperty -Name "Cluster" -Value $Cluster $Object | Add-Member -MemberType NoteProperty -Name "ClusterID" -Value $ClusterID $Object | Add-Member -MemberType NoteProperty -Name "Status" -Value $ClusterStatus $Object | Add-Member -MemberType NoteProperty -Name "Errors" -Value $ClusterError $Object | Add-Member -MemberType NoteProperty -Name "Version" -Value $ClusterVersion $Object | Add-Member -MemberType NoteProperty -Name "VersionStatus" -Value $ClusterVersionStatus # Status etc $Object | Add-Member -MemberType NoteProperty -Name "ConnectionStatus" -Value $ClusterConnectionStatus $Object | Add-Member -MemberType NoteProperty -Name "LastConnected" -Value $ClusterLastConnectedUTC $Object | Add-Member -MemberType NoteProperty -Name "HoursSince" -Value $ClusterLastConnectedHoursSince $Object | Add-Member -MemberType NoteProperty -Name "MinutesSince" -Value $ClusterLastConnectedMinutesSince $Object | Add-Member -MemberType NoteProperty -Name "Type" -Value $ClusterType $Object | Add-Member -MemberType NoteProperty -Name "Product" -Value $ClusterProduct $Object | Add-Member -MemberType NoteProperty -Name "Encrypted" -Value $ClusterEncrypted $Object | Add-Member -MemberType NoteProperty -Name "Snapshots" -Value $ClusterSnapshots # Objects $Object | Add-Member -MemberType NoteProperty -Name "ProtectedObjects" -Value $RSCClusterProtectedObjects $Object | Add-Member -MemberType NoteProperty -Name "UnProtectedObjects" -Value $RSCClusterUnProtectedObjects $Object | Add-Member -MemberType NoteProperty -Name "DoNotProtectObjects" -Value $RSCClusterDoNotProtectedObjects # Location $Object | Add-Member -MemberType NoteProperty -Name "Location" -Value $ClusterAddress $Object | Add-Member -MemberType NoteProperty -Name "Latitude" -Value $ClusterLatitude $Object | Add-Member -MemberType NoteProperty -Name "Longitude" -Value $ClusterLongitude $Object | Add-Member -MemberType NoteProperty -Name "Timezone" -Value $ClusterTimezone # Storage $Object | Add-Member -MemberType NoteProperty -Name "TotalStorageTB" -Value $ClusterTotalStorageTB $Object | Add-Member -MemberType NoteProperty -Name "UsedStorageTB" -Value $ClusterUsedStorageTB $Object | Add-Member -MemberType NoteProperty -Name "FreeStorageTB" -Value $ClusterFreeStorageTB $Object | Add-Member -MemberType NoteProperty -Name "Used" -Value $ClusterUsedPercentage $Object | Add-Member -MemberType NoteProperty -Name "Free" -Value $ClusterFreePercentage $Object | Add-Member -MemberType NoteProperty -Name "UsedINT" -Value $ClusterUsedPercentageINT $Object | Add-Member -MemberType NoteProperty -Name "FreeINT" -Value $ClusterFreePercentageINT $Object | Add-Member -MemberType NoteProperty -Name "RunwayDays" -Value $ClusterRunwayDays # Cluster info $Object | Add-Member -MemberType NoteProperty -Name "TotalNodes" -Value $ClusterNodesCount $Object | Add-Member -MemberType NoteProperty -Name "BadNodes" -Value $ClusterBadNodesCount $Object | Add-Member -MemberType NoteProperty -Name "HealthyNodes" -Value $ClusterHealthyNodesCount $Object | Add-Member -MemberType NoteProperty -Name "TotalDisks" -Value $ClusterDisksCount $Object | Add-Member -MemberType NoteProperty -Name "BadDisks" -Value $ClusterBadDisksCount $Object | Add-Member -MemberType NoteProperty -Name "HealthyDisks" -Value $ClusterHealthyDisksCount $Object | Add-Member -MemberType NoteProperty -Name "ArchiveTargets" -Value $ClusterArchiveTargetCount $Object | Add-Member -MemberType NoteProperty -Name "ReplicationTargets" -Value $ClusterReplicationTargetCount $Object | Add-Member -MemberType NoteProperty -Name "ReplicationSources" -Value $ClusterReplicationSourceCount # Misc $Object | Add-Member -MemberType NoteProperty -Name "PauseStatus" -Value $ClusterPauseStatus $Object | Add-Member -MemberType NoteProperty -Name "RegisteredUTC" -Value $ClusterRegisteredUTC $Object | Add-Member -MemberType NoteProperty -Name "URL" -Value $ClusterClusterURL # Adding $RSCClusters.Add($Object) | Out-Null # End of for each cluster below } # End of for each cluster above # Returning array Return $RSCClusters # End of function } |