Examples/New-PfaProtectionGroupReport.ps1
function New-PfaProtectionGroupReport { param ( [Parameter(Mandatory = $true, ParameterSetName = 'NewConnection', Position = 0)] [String[]]$Array, [Parameter(Mandatory = $true, ParameterSetName = 'NewConnection', Position = 1)] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, [Parameter(Mandatory = $true, ParameterSetName = 'ExistingConnection', Position = 0)] [ValidateNotNullOrEmpty()] [PureStorageRestApi[]]$Connection ) begin { $MyCSS = ' body { color: #333333; font-family: Calibri,Tahoma,Arial,Verdana; font-size: 11pt; margin: 0px; padding: 0px; } h3 { margin: 5px 0px 5px 0px; } h4 { margin: 0px; } table { border-collapse: collapse; } th { text-align: center; font-weight: bold; border-top: 1px solid black; border-bottom: 1px solid black; white-space: nowrap; padding: 0px 10px 0px 10px; } th.align-left { text-align: left; } th.no-border { border: none; } td { padding: 2px 10px 2px 10px; text-align: center; white-space: nowrap; } td.align-left { text-align: left; } td.array { background-color: #333333; color: #ffffff; font-weight: bold; font-size: 18px; text-align: left; padding: 5px 10px 5px 10px; } td.top-border { border-top: 1px solid black; } td.no-padding { padding: 0px;l } td.pb-20 { padding-bottom: 20px; } .odd { background-color: #ffffff; } .even { background-color: #dddddd; }' $paramsErrorMessage = @{ # Column name Column = "Error Message" # Test criteria: always highlight cell ScriptBlock = {$true} # CSS attribute to add if ScriptBlock is true CSSAttribute = "style" } $AllProtectionGroups = @() $AllErrors = @() if ($PSCmdlet.ParameterSetName -eq 'NewConnection') { foreach ($ArrayName in $Array) { $Connection += Connect-PfaApi -ArrayName $ArrayName -Credential $Credential -SkipCertificateCheck -ErrorVariable +ErrorMessage } } foreach ($FlashArray in $Connection) { try { $ProtectionGroups = Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/protection-groups" -SkipCertificateCheck -PipelineVariable ProtectionGroup -ErrorVariable +ErrorMessage -ErrorAction Stop | ForEach-Object { if ($PSVersionTable.PSVersion -lt "7.2.0") { $ProtectionGroup = $_ } $Replication = [PSCustomObject]@{ Frequency = $_.Replication_Schedule.Frequency / 1000 Time = $_.Replication_Schedule.At Blackout = $_.Replication_Schedule.Blackout Enabled = $_.Replication_Schedule.Enabled Snapshot = [PSCustomObject]@{ Retention = $_.Target_Retention.All_For_Sec RetentionDaily = $_.Target_Retention.Per_Day RetentionDays = $_.Target_Retention.Days Enabled = $_.Snapshot_Schedule.Enabled } } $Snapshot = [PSCustomObject]@{ Frequency = $_.Snapshot_Schedule.Frequency / 1000 Time = $_.Snapshot_Schedule.At Enabled = $_.Snapshot_Schedule.Enabled Retention = $_.Source_Retention.All_For_Sec RetentionDaily = $_.Source_Retention.Per_Day RetentionDays = $_.Source_Retention.Days } $Volumes = (Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/protection-groups/volumes?group_names=$($_.Name)" -SkipCertificateCheck -ErrorAction Stop).Member.Name if ($Volumes.Count -gt 0) { $Members = $Volumes | Select-Object -PipelineVariable VolumeName | ForEach-Object { Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/volumes/space?names=$VolumeName" -SkipCertificateCheck -PipelineVariable Member -ErrorAction Stop | ForEach-Object { $HostConnections = Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/connections?volume_names=$VolumeName" -SkipCertificateCheck -ErrorAction Stop [PSCustomObject]@{ Name = $VolumeName Size = $_.Space.Total_Provisioned Used = $_.Space.Unique LUN = if (-not $HostConnections.Host -and -not ($HostConnections.Host_Group)) { "" } else { if (($HostConnections.Host_Group.Name | Sort-Object -Unique).Count -gt 0) { (($HostConnections.Lun | Sort-Object -Unique) | ForEach-Object { if ($_ -gt 255) { $_ | Format-Hex | Select-Object @{Expression = {"$($_.Bytes[0]):$($_.Bytes[4])"}} | Select-Object -ExpandProperty * } else { $_ } }) -join ", " } else { if (($HostConnections.Host.Name | Sort-Object -Unique).Count -gt 1) { (($HostConnections.Lun | Sort-Object -Unique) | ForEach-Object { if ($_ -gt 255) { $_ | Format-Hex | Select-Object @{Expression = {"$($_.Bytes[0]):$($_.Bytes[4])"}} | Select-Object -ExpandProperty * } else { $_ } }) -join ", " } else { if ($HostConnections.Lun -gt 255) { $HostConnections.Lun | Format-Hex | Select-Object @{Expression = {"$($_.Bytes[0]):$($_.Bytes[4])"}} | Select-Object -ExpandProperty * } else { $HostConnections.Lun } } } } Serial = (Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/volumes?names=$VolumeName" -SkipCertificateCheck -ErrorAction Stop).Serial Source = Invoke-Command -Command { if (-not (($FlashArray.ApiVersion[2].Minor | Select-Object -Last 1) -gt 1)) { (Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/array" -ApiVersion 1.18 -SkipCertificateCheck -ErrorAction Stop).Array_Name } else { (Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/arrays" -SkipCertificateCheck -ErrorAction Stop).Name } } Target = (Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/protection-groups/targets?group_names=$($ProtectionGroup.Name)" -SkipCertificateCheck -PipelineVariable Member -ErrorAction Stop).Member.Name -join ", " } } } } else { $Members = $null } [PSCustomObject]@{ Name = $_.Name Size = ($Members.Size | Measure-Object -Sum).Sum Used = ($Members.Used | Measure-Object -Sum).Sum SnapshotSize = ((Invoke-PfaApiRequest -Array $FlashArray -Request RestMethod -Method GET -Path "/protection-group-snapshots?source_names=$($_.Name)" -SkipCertificateCheck -ErrorAction Stop).Items.Space.Snapshots | Measure-Object -Sum).Sum Members = $Members SnapshotStatus = $Replication.Snapshot.Enabled SnapshotSchedule = $Snapshot ReplicationStatus = $Replication.Enabled ReplicationSchedule = $Replication Array = $FlashArray.ArrayName } } $AllProtectionGroups += $ProtectionGroups if ($ErrorMessage -and $ErrorMessage.Message -ne "System error.") { $AllErrors = $ErrorMessage | ForEach-Object { [PSCustomObject]@{ ArrayName = $FlashArray.ArrayName ErrorMessage = $ErrorMessage } } } if ($AllProtectionGroups.Count -gt 0) { if (Get-Module -Name "PS2HTMLTable") { $HTML = New-HTMLHead -Style $MyCSS -Title "Flash Array - Protection Group Report" $HTML += '<table id="container" cellpadding="0" cellspacing="0" border="0">' $AllProtectionGroups | Sort-Object Array, Name | Group-Object Array | ForEach-Object { $HTML += '<tr><td colspan="11" class="array">{0} ({1})</td></tr>' -f $_.Name, $_.Count $_.Group | Select-Object -PipelineVariable Group | ForEach-Object { $HTML += '<tr><th class="no-border">Protection Group Name</th><th class="no-border">Size</th><th class="no-border">Used</th><th colspan="8" class="no-border align-left">Snapshot</th></tr>' $HTMLTable = $_ | Select-Object Name, @{Name = "Size"; Expression = {Format-Byte $_.Size}}, @{Name = "Used"; Expression = {Format-Byte $_.Used}}, @{Name = "Snapshots"; Expression = {Format-Byte $_.SnapshotSize}} | New-HTMLTable -HTMLDecode -NestedTable -RemoveColumnGroup -RemoveHeader $HTML += $HTMLTable if ($_.Members.Count -gt 0) { $HTMLTable = $_.Members | Select-Object @{Name = "Members"; Expression = {$_.Name}}, @{Name = "Size"; Expression = {Format-Byte $_.Size}}, @{Name = "Used"; Expression = {Format-Byte $_.Used}}, LUN, Serial, Source, Target, @{Name = "Snapshot Status"; Expression = {if ($Group.SnapshotStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Snapshot Schedule"; Expression = {"$(Format-PfaSecond $Group.SnapshotSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.SnapshotSchedule.Time -AsInt)/$(Format-PfaSecond $Group.SnapshotSchedule.Retention -AsInt)/$($Group.SnapshotSchedule.RetentionDaily)/$(Format-PfaSecond $Group.SnapshotSchedule.RetentionDays -AsInt)"}}, @{Name = "Replication Status"; Expression = {if ($Group.ReplicationStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Replication Schedule"; Expression = {"$(Format-PfaSecond $Group.ReplicationSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Time -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.End -AsInt)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.Retention -AsInt)/$($Group.ReplicationSchedule.Snapshot.RetentionDaily)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.RetentionDays -AsInt)"}} | New-HTMLTable -HTMLDecode -SetAlternating -NestedTable -RemoveColumnGroup $HTMLTable += '<tr><td colspan="11" class="top-border no-padding"></td></tr>' $HTML += $HTMLTable } else { $HTMLTable = "" | Select-Object @{Name = "Members"; Expression = {"None"}}, Size, Used, LUN, Serial, Source, Target, @{Name = "Snapshot Status"; Expression = {if ($Group.SnapshotStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Snapshot Schedule"; Expression = {"$(Format-PfaSecond $Group.SnapshotSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.SnapshotSchedule.Time -AsInt)/$(Format-PfaSecond $Group.SnapshotSchedule.Retention -AsInt)/$($Group.SnapshotSchedule.RetentionDaily)/$(Format-PfaSecond $Group.SnapshotSchedule.RetentionDays -AsInt)"}}, @{Name = "Replication Status"; Expression = {if ($Group.ReplicationStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Replication Schedule"; Expression = {"$(Format-PfaSecond $Group.ReplicationSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Time -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.End -AsInt)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.Retention -AsInt)/$($Group.ReplicationSchedule.Snapshot.RetentionDaily)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.RetentionDays -AsInt)"}} | New-HTMLTable -HTMLDecode -SetAlternating -NestedTable -RemoveColumnGroup $HTMLTable += '<tr><td colspan="11" class="top-border no-padding"></td></tr>' $HTML += $HTMLTable } $HTML += "<tr><td colspan=""11"">Create a snapshot on source every $(Format-PfaSecond $_.SnapshotSchedule.Frequency)$(if ($null -ne $_.SnapshotSchedule.Time) {" at {0}" -f (Format-PfaSecondsFromMidnight $_.SnapshotSchedule.Time)}). Retain all snapshots on source for $(Format-PfaSecond $_.SnapshotSchedule.Retention)$(if ($_.SnapshotSchedule.RetentionDaily -ne 0 -or $_.SnapshotSchedule.RetentionDays -ne 0) {", then retain {0} snapshots per day for {1} more days" -f $_.SnapshotSchedule.RetentionDaily, $(Format-PfaSecond $_.SnapshotSchedule.RetentionDays -AsInt)}).</td></tr>" $HTML += "<tr><td colspan=""11"" class=""pb-20"">Replicate a snapshot to targets every $(Format-PfaSecond $_.ReplicationSchedule.Frequency)$(if ($null -ne $_.ReplicationSchedule.Time) {" at {0}" -f (Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Time)})$(if ($null -ne $_.ReplicationSchedule.Blackout.Start) {" except between $(Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Blackout.Start) and $(Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Blackout.End)"}). Retain all snapshots on targets for $(Format-PfaSecond $_.ReplicationSchedule.Snapshot.Retention)$(if ($_.ReplicationSchedule.Snapshot.RetentionDaily -ne 0 -or $_.ReplicationSchedule.Snapshot.RetentionDays -ne 0) {", then retain {0} snapshots per day for {1} more days" -f $_.ReplicationSchedule.Snapshot.RetentionDaily, $(Format-PfaSecond $_.ReplicationSchedule.Snapshot.RetentionDays -AsInt)}).</td></tr>" } } if ($null -ne $AllErrors) { $AllErrors | ForEach-Object { $HTML += '<tr><td colspan="11" class="array">{0}</td></tr>' -f $_.Name $HTMLTable = $_ | Select-Object @{Name = "Error Message";Expression = {($_.ErrorMessage.Exception.Message -split '\r\n')[0]}} | New-HTMLTable -HTMLDecode -SetAlternating -TableAttributes @{"width" = "100%"} -ColumnClass "align-left" # Color "Error Message" red $HTMLTable = Add-HTMLTableColor -HTML $HTMLTable -CSSAttributeValue "background-color:#ed5e3c;" @paramsErrorMessage $HTMLTable += '<tr><td colspan="11" class="top-border no-padding"></td></tr>' $HTML += "<tr><td colspan=""11"" style=""padding: 0px;"">$HTMLTable</td></tr>" } } $HTML += "</table>" $HTML = $HTML | Close-HTML -Validate } else { $HTML = @" <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Flash Array - Protection Group Report</title> <style> $MyCSS </Style> </head> <body> "@ $HTML += '<table id="container" cellpadding="0" cellspacing="0" border="0">' $AllProtectionGroups | Sort-Object Array, Name | Group-Object Array | ForEach-Object { $HTML += '<tr><td colspan="11" class="array">{0} ({1})</td></tr>' -f $_.Name, $_.Count $_.Group | Select-Object -PipelineVariable Group | ForEach-Object { $Xml = [System.Xml.Linq.XDocument]::Parse(($_ | Select-Object @{Name = "Protection Group Name";Expression = {$_.Name}}, @{Name = "Size"; Expression = {Format-Byte $_.Size}}, @{Name = "Used"; Expression = {Format-Byte $_.Used}}, @{Name = "Snapshot"; Expression = {Format-Byte $_.SnapshotSize}} | ConvertTo-Html -Fragment)) $Xml.Element("table").Element("colgroup").Remove() foreach ($XmlTr in $($Xml.Descendants("tr"))) { if ($XmlTr.Where({$_.Element('td')})) { if (($XmlTr.NodesBeforeSelf() | Measure-Object).Count % 2 -eq 0) { $XmlTr.SetAttributeValue("class", "even $($XMlTr.Attribute("class").Value)".Trim()) } else { $XmlTr.SetAttributeValue("class", "odd $($XMlTr.Attribute("class").Value)".Trim()) } } foreach ($XmlTh in $($XmlTr.Descendants("th"))) { $XmlTh.SetAttributeValue("class", "no-border") if ($null -eq $XmlTh.NextNode) { $XmlTh.SetAttributeValue("colspan", "8") $XmlTh.SetAttributeValue("class", "no-border align-left") } } } $HTMLTable = [System.Xml.Linq.XDocument]::Parse($Xml).Document.ToString().Replace("<table>", "").Replace("</table>", "") $HTML += $HTMLTable if ($_.Members.Count -gt 0) { $Xml = [System.Xml.Linq.XDocument]::Parse(($_.Members | Select-Object @{Name = "Members"; Expression = {$_.Name}}, @{Name = "Size"; Expression = {Format-Byte $_.Size}}, @{Name = "Used"; Expression = {Format-Byte $_.Used}}, LUN, Serial, Source, Target, @{Name = "Snapshot Status"; Expression = {if ($Group.SnapshotStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Snapshot Schedule"; Expression = {"$(Format-PfaSecond $Group.SnapshotSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.SnapshotSchedule.Time -AsInt)/$(Format-PfaSecond $Group.SnapshotSchedule.Retention -AsInt)/$($Group.SnapshotSchedule.RetentionDaily)/$(Format-PfaSecond $Group.SnapshotSchedule.RetentionDays -AsInt)"}}, @{Name = "Replication Status"; Expression = {if ($Group.ReplicationStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Replication Schedule"; Expression = {"$(Format-PfaSecond $Group.ReplicationSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Time -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.End -AsInt)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.Retention -AsInt)/$($Group.ReplicationSchedule.Snapshot.RetentionDaily)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.RetentionDays -AsInt)"}} | ConvertTo-Html -Fragment)) $Xml.Element("table").Element("colgroup").Remove() foreach ($XmlTr in $($Xml.Descendants("tr"))) { if ($XmlTr.Where({$_.Element('td')})) { if (($XmlTr.NodesBeforeSelf() | Measure-Object).Count % 2 -eq 0) { $XmlTr.SetAttributeValue("class", "even $($XMlTr.Attribute("class").Value)".Trim()) } else { $XmlTr.SetAttributeValue("class", "odd $($XMlTr.Attribute("class").Value)".Trim()) } } } $HTMLTable = [System.Xml.Linq.XDocument]::Parse($Xml).Document.ToString().Replace("<table>", "").Replace("</table>", "") $HTMLTable += '<tr><td colspan="11" class="top-border no-padding"></td></tr>' $HTML += $HTMLTable } else { $Xml = [System.Xml.Linq.XDocument]::Parse(("" | Select-Object @{Name = "Members"; Expression = {"None"}}, Size, Used, LUN, Serial, Source, Target, @{Name = "Snapshot Status"; Expression = {if ($Group.SnapshotStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Snapshot Schedule"; Expression = {"$(Format-PfaSecond $Group.SnapshotSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.SnapshotSchedule.Time -AsInt)/$(Format-PfaSecond $Group.SnapshotSchedule.Retention -AsInt)/$($Group.SnapshotSchedule.RetentionDaily)/$(Format-PfaSecond $Group.SnapshotSchedule.RetentionDays -AsInt)"}}, @{Name = "Replication Status"; Expression = {if ($Group.ReplicationStatus -eq $true) {"Active"} else {"Inactive"}}}, @{Name = "Replication Schedule"; Expression = {"$(Format-PfaSecond $Group.ReplicationSchedule.Frequency)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Time -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.Start -AsInt)/$(Format-PfaSecondsFromMidnight $Group.ReplicationSchedule.Blackout.End -AsInt)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.Retention -AsInt)/$($Group.ReplicationSchedule.Snapshot.RetentionDaily)/$(Format-PfaSecond $Group.ReplicationSchedule.Snapshot.RetentionDays -AsInt)"}} | ConvertTo-Html -Fragment)) $Xml.Element("table").Element("colgroup").Remove() foreach ($XmlTr in $($Xml.Descendants("tr"))) { if ($XmlTr.Where({$_.Element('td')})) { if (($XmlTr.NodesBeforeSelf() | Measure-Object).Count % 2 -eq 0) { $XmlTr.SetAttributeValue("class", "even $($XMlTr.Attribute("class").Value)".Trim()) } else { $XmlTr.SetAttributeValue("class", "odd $($XMlTr.Attribute("class").Value)".Trim()) } } } $HTMLTable = [System.Xml.Linq.XDocument]::Parse($Xml).Document.ToString().Replace("<table>", "").Replace("</table>", "") $HTMLTable += '<tr><td colspan="11" class="top-border no-padding"></td></tr>' $HTML += $HTMLTable } $HTML += "<tr><td colspan=""11"">Create a snapshot on source every $(Format-PfaSecond $_.SnapshotSchedule.Frequency)$(if ($null -ne $_.SnapshotSchedule.Time) {" at {0}" -f (Format-PfaSecondsFromMidnight $_.SnapshotSchedule.Time)}). Retain all snapshots on source for $(Format-PfaSecond $_.SnapshotSchedule.Retention)$(if ($_.SnapshotSchedule.RetentionDaily -ne 0 -or $_.SnapshotSchedule.RetentionDays -ne 0) {", then retain {0} snapshots per day for {1} more days" -f $_.SnapshotSchedule.RetentionDaily, $(Format-PfaSecond $_.SnapshotSchedule.RetentionDays -AsInt)}).</td></tr>" $HTML += "<tr><td colspan=""11"" class=""pb-20"">Replicate a snapshot to targets every $(Format-PfaSecond $_.ReplicationSchedule.Frequency)$(if ($null -ne $_.ReplicationSchedule.Time) {" at {0}" -f (Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Time)})$(if ($null -ne $_.ReplicationSchedule.Blackout.Start) {" except between $(Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Blackout.Start) and $(Format-PfaSecondsFromMidnight $_.ReplicationSchedule.Blackout.End)"}). Retain all snapshots on targets for $(Format-PfaSecond $_.ReplicationSchedule.Snapshot.Retention)$(if ($_.ReplicationSchedule.Snapshot.RetentionDaily -ne 0 -or $_.ReplicationSchedule.Snapshot.RetentionDays -ne 0) {", then retain {0} snapshots per day for {1} more days" -f $_.ReplicationSchedule.Snapshot.RetentionDaily, $(Format-PfaSecond $_.ReplicationSchedule.Snapshot.RetentionDays -AsInt)}).</td></tr>" } } if ($null -ne $AllErrors) { $AllErrors | ForEach-Object { $HTML += '<tr><td colspan="11" class="array">{0}</td></tr>' -f $_.Name $Xml = [System.Xml.Linq.XDocument]::Parse(($_ | Select-Object @{Name = "Error Message";Expression = {($_.ErrorMessage.Exception.Message -split '\r\n')[0]}})) $Xml.Element("table").Element("colgroup").Remove() $Xml.Element("table").SetAttributeValue("width", "100%") foreach ($XmlTr in $($Xml.Descendants("tr"))) { if ($XmlTr.Where({$_.Element('td')})) { if (($XmlTr.NodesBeforeSelf() | Measure-Object).Count % 2 -eq 0) { $XmlTr.SetAttributeValue("class", "even $($XMlTr.Attribute("class").Value)".Trim()) } else { $XmlTr.SetAttributeValue("class", "odd $($XMlTr.Attribute("class").Value)".Trim()) } } foreach ($XmlTh in $($XmlTr.Descendants("th"))) { $XmlTh.SetAttributeValue("class", "align-left") } } $HTMLTable = [System.Xml.Linq.XDocument]::Parse($Xml).Document.ToString().Replace("<table>", "").Replace("</table>", "") $HTML += "<tr><td colspan=""11"" style=""padding: 0px;"">$HTMLTable</td></tr>" } } $HTML += "</table>" $HTML += "</body></html>" } $HTML | Out-File "FlashArray - Protection Groups.html" } } catch { throw $_ } } } } |