diff --git a/Modules/VMware-vCD-TenantReport/README.md b/Modules/VMware-vCD-TenantReport/README.md new file mode 100644 index 0000000..ac9b9f8 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/README.md @@ -0,0 +1,30 @@ +VMware-vCD-TenantReport PowerShell Module +============= + +# About + +## Project Owner: + +Markus Kraus [@vMarkus_K](https://twitter.com/vMarkus_K) + +MY CLOUD-(R)EVOLUTION [mycloudrevolution.com](http://mycloudrevolution.com/) + +## Project WebSite: + +[mycloudrevolution.com](http://mycloudrevolution.com/) + +## Project Documentation: + +[Read the Docs](http://readthedocs.io/) + +## Project Description: + +The 'VMware-vCD-TenantReport' PowerShell Module creates with the Fuction 'Get-VcdTenantReport' a HTML Report of your vCloud Director Objects. + +![Get-VcdTenantReport](/media/Get-VcdTenantReport.png) + +Big thanks to [Timothy Dewin](https://twitter.com/tdewin) for his great [PowerStartHTML](https://github.com/tdewin/randomsamples/tree/master/powerstarthtml) PowerShell Module which is used to generate the Report for this Module. + + + + diff --git a/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 b/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 new file mode 100644 index 0000000..32afbb5 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 @@ -0,0 +1,122 @@ +# +# Modulmanifest für das Modul "VMware-vCD-TenantReport" +# +# Generiert von: Markus Kraus +# +# Generiert am: 8/22/2017 +# + +@{ + +# Die diesem Manifest zugeordnete Skript- oder Binärmoduldatei. +# RootModule = 'VMware-vCD-TenantReport.psm1' + +# Die Versionsnummer dieses Moduls +ModuleVersion = '1.0.2' + +# ID zur eindeutigen Kennzeichnung dieses Moduls +GUID = '21a71eaa-d259-48c5-8482-643ba152af76' + +# Autor dieses Moduls +Author = 'Markus' + +# Unternehmen oder Hersteller dieses Moduls +CompanyName = 'mycloudrevolution.com' + +# Urheberrechtserklärung für dieses Modul +Copyright = '(c) 2017 Markus Kraus. Alle Rechte vorbehalten.' + +# Beschreibung der von diesem Modul bereitgestellten Funktionen +# Description = '' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Moduls +# PowerShellVersion = '' + +# Der Name des für dieses Modul erforderlichen Windows PowerShell-Hosts +# PowerShellHostName = '' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Hosts +# PowerShellHostVersion = '' + +# Die für dieses Modul mindestens erforderliche Microsoft .NET Framework-Version +# DotNetFrameworkVersion = '' + +# Die für dieses Modul mindestens erforderliche Version der CLR (Common Language Runtime) +# CLRVersion = '' + +# Die für dieses Modul erforderliche Prozessorarchitektur ("Keine", "X86", "Amd64"). +# ProcessorArchitecture = '' + +# Die Module, die vor dem Importieren dieses Moduls in die globale Umgebung geladen werden müssen +# RequiredModules = @() + +# Die Assemblys, die vor dem Importieren dieses Moduls geladen werden müssen +# RequiredAssemblies = @() + +# Die Skriptdateien (PS1-Dateien), die vor dem Importieren dieses Moduls in der Umgebung des Aufrufers ausgeführt werden. +# ScriptsToProcess = @() + +# Die Typdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# TypesToProcess = @() + +# Die Formatdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# FormatsToProcess = @() + +# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen. +NestedModules = @( "modules/PowerStartHTML/PowerStartHTML.psd1", + "functions/Get-VcdTenantReport.psm1" ) + +# Aus diesem Modul zu exportierende Funktionen +FunctionsToExport = '*' + +# Aus diesem Modul zu exportierende Cmdlets +CmdletsToExport = '*' + +# Die aus diesem Modul zu exportierenden Variablen +VariablesToExport = '*' + +# Aus diesem Modul zu exportierende Aliase +AliasesToExport = '*' + +# Aus diesem Modul zu exportierende DSC-Ressourcen +# DscResourcesToExport = @() + +# Liste aller Module in diesem Modulpaket +# ModuleList = @() + +# Liste aller Dateien in diesem Modulpaket +# FileList = @() + +# Die privaten Daten, die an das in "RootModule/ModuleToProcess" angegebene Modul übergeben werden sollen. Diese können auch eine PSData-Hashtabelle mit zusätzlichen von PowerShell verwendeten Modulmetadaten enthalten. +PrivateData = @{ + + PSData = @{ + + # 'Tags' wurde auf das Modul angewendet und unterstützt die Modulermittlung in Onlinekatalogen. + # Tags = @() + + # Eine URL zur Lizenz für dieses Modul. + # LicenseUri = '' + + # Eine URL zur Hauptwebsite für dieses Projekt. + # ProjectUri = '' + + # Eine URL zu einem Symbol, das das Modul darstellt. + # IconUri = '' + + # 'ReleaseNotes' des Moduls + # ReleaseNotes = '' + + } # Ende der PSData-Hashtabelle + +} # Ende der PrivateData-Hashtabelle + +# HelpInfo-URI dieses Moduls +# HelpInfoURI = '' + +# Standardpräfix für Befehle, die aus diesem Modul exportiert werden. Das Standardpräfix kann mit "Import-Module -Prefix" überschrieben werden. +# DefaultCommandPrefix = '' + +} + + diff --git a/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 b/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 new file mode 100644 index 0000000..ee1296e --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 @@ -0,0 +1,251 @@ +function Get-VcdTenantReport { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 1.0.0 - Inital Release + 1.0.1 - Removed "Test-IP" Module + 1.0.2 - More Detailed Console Log + =========================================================================== + External Code Sources: + Examle Usage of BOOTSTRAP with PowerShell + https://github.com/tdewin/randomsamples/tree/master/powershell-veeamallstat + BOOTSTRAP with PowerShell + https://github.com/tdewin/randomsamples/tree/master/powerstarthtml + =========================================================================== + Tested Against Environment: + vCD Version: 8.20 + PowerCLI Version: PowerCLI 6.5.1 + PowerShell Version: 5.0 + OS Version: Windows 8.1 + Keyword: VMware, vCD, Report, HTML + =========================================================================== + + .DESCRIPTION + This Function creates a HTML Report for your vCloud Director Organization. + + This Function is fully tested as Organization Administrator. + With lower permissions a unexpected behavior is possible. + + .Example + Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Credential $MyCedential + + .Example + Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Path "C:\Temp\Report.html" + + .PARAMETER Server + The FQDN of your vCloud Director Endpoint. + + .PARAMETER Org + The Organization Name. + + .PARAMETER Credential + PowerShell Credentials to access the Eénvironment. + + .PARAMETER Path + The Path of the exported HTML Report. + +#> +#Requires -Version 5 +#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.5.1.0"} + +[CmdletBinding()] +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Server, + [Parameter(Mandatory=$True, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Org, + [Parameter(Mandatory=$False, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [PSCredential] $Credential, + [Parameter(Mandatory=$false, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Path = ".\Report.html" + +) + +Process { + + # Start Connection to vCD + + if ($global:DefaultCIServers) { + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Disconnect existing vCD Server ..." + $Trash = Disconnect-CIServer -Server * -Force:$true -Confirm:$false -ErrorAction SilentlyContinue + } + + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Connect vCD Server ..." + if ($Credential) { + $Trash = Connect-CIServer -Server $Server -Org $Org -Credential $Credential -ErrorAction Stop + } + else { + $Trash = Connect-CIServer -Server $Server -Org $Org -ErrorAction Stop + } + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Create HTML Report..." + + # Init HTML Report + $ps = New-PowerStartHTML -title "vCD Tenant Report" + + #Set CSS Style + $ps.cssStyles['.bgtitle'] = "background-color:grey" + $ps.cssStyles['.bgsubsection'] = "background-color:#eee;" + + # Processing Data + ## Get Main Objects + [Array] $OrgVdcs = Get-OrgVdc + [Array] $Catalogs = Get-Catalog + [Array] $Users = Get-CIUser + + ## Add Header to Report + $ps.Main().Add("div","jumbotron").N() + $ps.Append("h1","display-3",("vCD Tenant Report" -f $OrgVdcs.Count)).Append("p","lead","Organization User Count: {0}" -f $Users.Count).Append("p","lead","Organization Catalog Count: {0}" -f $Catalogs.Count).Append("p","lead","Organization VDC Count: {0}" -f $OrgVdcs.Count).Append("hr","my-4").Append("p","font-italic","This Report lists the most important objects in your vCD Environmet. For more details contact your Service Provider").N() + + ## add Org Users to Report + $ps.Main().Append("h2",$null,"Org Users").N() + + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"User Name").Append("th",$null,"Locked").Append("th",$null,"DeployedVMCount").Append("th",$null,"StoredVMCount").N() + $ps.Add("tr").N() + + foreach ($User in $Users) { + $ps.Append("td",$null,$User.Name).N() + $ps.Append("td",$null,$User.Locked).N() + $ps.Append("td",$null,$User.DeployedVMCount).N() + $ps.Append("td",$null,$User.StoredVMCount).N() + $ps.Up().N() + + } + $ps.Up().N() + + ## add Org Catalogs to Report + $ps.Main().Append("h2",$null,"Org Catalogs").N() + + foreach ($Catalog in $Catalogs) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Catalog Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Catalog.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Add("tr").N() + + $headers = @("Item") + foreach ($h in $headers) { + $ps.Append("th",$null,$h).N() + } + $ps.Up().N() + + ### add Itens of the Catalog to the Report + [Array] $Items = $Catalog.ExtensionData.CatalogItems.CatalogItem + + foreach ($Item in $Items) { + $ps.Add("tr").N() + $ps.Append("td",$null,$Item.Name).N() + + $ps.Up().N() + + } + + $ps.Up().Up().N() + } + $ps.Up().N() + + ## add Org VDC`s to the Report + $ps.Main().Append("h2",$null,"Org VDCs").N() + + foreach ($OrgVdc in $OrgVdcs) { + $ps.Main().Add('table','table table-striped table-inverse').Add("tr").Append("th",$null,"VDC Name").Append("th",$null,"Enabled").Append("th",$null,"CpuUsedGHz").Append("th",$null,"MemoryUsedGB").Append("th",$null,"StorageUsedGB").Up().N() + $ps.Add("tr").N() + $ps.Append("td",$null,$OrgVdc.Name).Append("td",$null,$OrgVdc.Enabled).Append("td",$null,$OrgVdc.CpuUsedGHz).Append("td",$null,$OrgVdc.MemoryUsedGB).Append("td",$null,[Math]::Round($OrgVdc.StorageUsedGB,2)).Up().N() + + ### add Edge Gateways of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC Edge Gateways").N() + [Array] $Edges = Search-Cloud -QueryType EdgeGateway -Filter "Vdc==$($OrgVdc.Id)" + + foreach ($Edge in $Edges) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Edge Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Edge.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Append("tr").Append("td","font-weight-bold","HaStatus").Append("td",$null,($Edge.HaStatus)).N() + $ps.Append("td","font-weight-bold","AdvancedNetworkingEnabled").Append("td",$null,$Edge.AdvancedNetworkingEnabled).N() + $ps.Append("tr").Append("td","font-weight-bold","NumberOfExtNetworks").Append("td",$null,($Edge.NumberOfExtNetworks)).N() + $ps.Append("td","font-weight-bold","NumberOfOrgNetworks").Append("td",$null,$Edge.NumberOfOrgNetworks).N() + + $ps.Up().Up().N() + } + $ps.Up().N() + + ### add Org Networks of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC Networks").N() + [Array] $Networks = $OrgVdc | Get-OrgVdcNetwork + + foreach ($Network in $Networks) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Network Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Network.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Append("tr").Append("td","font-weight-bold","DefaultGateway").Append("td",$null,($Network.DefaultGateway)).N() + $ps.Append("td","font-weight-bold","Netmask").Append("td",$null,$Network.Netmask).N() + $ps.Append("tr").Append("td","font-weight-bold","NetworkType").Append("td",$null,($Network.NetworkType)).N() + $ps.Append("td","font-weight-bold","StaticIPPool").Append("td",$null,$Network.StaticIPPool).N() + + $ps.Up().Up().N() + } + $ps.Up().N() + + ### add vApps of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC vApps").N() + + [Array] $Vapps = $OrgVdc | Get-CIVApp + + foreach ($Vapp in $Vapps) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"vApp Name").Append("th",$null,"Owner").Up().N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Vapp.Name).Append("td",$null,$Vapp.Owner).Up().N() + + #### add VMs of this vApp to Report + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Add("tr").N() + + $headers = @("Name","Status","GuestOSFullName","CpuCount","MemoryGB") + foreach ($h in $headers) { + $ps.Append("th",$null,$h).N() + } + $ps.Up().N() + + [Array] $VMs = $Vapp | Get-CIVM + + foreach ($VM in $VMs) { + $ps.Add("tr").N() + $ps.Append("td",$null,$VM.Name).N() + $ps.Append("td",$null,$VM.Status).N() + $ps.Append("td",$null,$VM.GuestOSFullName).N() + $ps.Append("td",$null,$VM.CpuCount).N() + $ps.Append("td",$null,$VM.MemoryGB).N() + + $ps.Up().N() + + } + $ps.Up().Up().N() + + } + $ps.Up().N() + + } + $ps.save($Path) + + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Open HTML Report..." + Start-Process $Path + +} +} diff --git a/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png b/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png new file mode 100644 index 0000000..c42dacf Binary files /dev/null and b/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png differ diff --git a/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 new file mode 100644 index 0000000..4bf4ee8 Binary files /dev/null and b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 differ diff --git a/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 new file mode 100644 index 0000000..0004cea --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 @@ -0,0 +1,237 @@ +class PowerStartHTML { + [string]$PowerStartHtmlTemplate = "
" + [xml]$xmlDocument = $null + $onLoadJS = $null + $cssStyles= @{} + $lastEl = $null + $newEl = $null + $indentedOutput = $false + $bootstrapAtCompile = $false + PowerStartHTML([string]$title) { + $this.xmlDocument = $this.PowerStartHtmlTemplate + $this.xmlDocument.html.head.title = $title + $this.lastEl = $this.xmlDocument.html.body.ChildNodes[0] + $this.onLoadJS = New-Object System.Collections.Generic.List[System.String] + } + [string] GetHtml() { + $xmlclone = $this.xmlDocument.Clone() + $csb = [System.Text.StringBuilder]::new() + foreach ($cssStyle in $this.cssStyles.GetEnumerator()) { + $null = $csb.AppendFormat("{0} {{ {1} }}",$cssStyle.Name,$cssStyle.Value) + } + $this.xmlDocument.html.head.style = $csb.toString() + $this.AddBootStrapAtCompile() + if($this.onLoadJS.Count -gt 0) { + $this.onLoadJs.Insert(0,"`r`n`$(document).ready(function() {") + $this.onLoadJs.Add("})`r`n") + $el = $this.xmlDocument.CreateElement("script") + $el.AppendChild($this.xmlDocument.CreateTextNode([System.String]::Join("`r`n",$this.onLoadJs))) + $this.xmlDocument.html.body.AppendChild($el) + } + $ms = [System.IO.MemoryStream]::new() + $xmlWriter = [System.Xml.XmlTextWriter]::new($ms,[System.Text.Encoding]::UTF8) + if($this.indentedOutput) { + $xmlWriter.Formatting = [System.Xml.Formatting]::Indented + } + $this.xmlDocument.WriteContentTo($xmlWriter) + $xmlWriter.Flush() + $ms.Flush() + #make sure that everytime we do gethtml we keep it clean + $this.xmlDocument = $xmlclone + $ms.Position = 0; + $sr = [System.IO.StreamReader]::new($ms); + return ("{0}`r`n" -f $sr.ReadToEnd()) + } + Save($path) { + $this.GetHtml() | Set-Content -path $path -Encoding UTF8 + } + + AddAttr($el,$name,$value) { + $attr = $this.xmlDocument.CreateAttribute($name) + $attr.Value = $value + $el.Attributes.Append($attr) + } + + AddAttrs($el,$dict) { + foreach($a in $dict.GetEnumerator()) { + $this.AddAttr($el,$a.Name,$a.Value) + } + } + [PowerStartHTML] AddBootStrap() { + $this.bootstrapAtCompile = $true + return $this + } + AddJSScript($href,$integrity) { + $el = $this.xmlDocument.CreateElement("script") + $attrs = @{ + "src"="$href"; + "integrity"="$integrity"; + "crossorigin"="anonymous" + } + $this.AddAttrs($el,$attrs) + $el.AppendChild($this.xmlDocument.CreateTextNode("")) + $this.xmlDocument.html.body.AppendChild($el) + } + AddBootStrapAtCompile() { #Bootstrap script needs to be added at the end + if($this.bootstrapAtCompile) { + $el = $this.xmlDocument.CreateElement("link") + $attrs = @{ + "rel"="stylesheet"; + "href"='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css'; + "integrity"="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"; + "crossorigin"="anonymous" + } + $this.AddAttrs($el,$attrs) + $el.AppendChild($this.xmlDocument.CreateTextNode("")) + $this.xmlDocument.html.head.AppendChild($el) + $this.AddJSScript('https://code.jquery.com/jquery-3.2.1.slim.min.js',"sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN") + $this.AddJSScript('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js',"sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4") + $this.AddJSScript('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js',"sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1") + } + } + [PowerStartHTML] AddContainerAttrToMain() { + $this.AddAttr($this.xmlDocument.html.body.ChildNodes[0],"class","container") + return $this + } + [PowerStartHTML] Append($elType = "table",$className=$null,[string]$text=$null) { + $el = $this.xmlDocument.CreateElement($elType) + if($text -ne $null) { + $el.AppendChild($this.xmlDocument.CreateTextNode($text)) + } + if($className -ne $null) { + $this.AddAttr($el,"class",$className) + } + $this.lastEl.AppendChild($el) + $this.newEl = $el + + return $this + } + [PowerStartHTML] Append($elType = "table",$className=$null) { return $this.Append($elType,$className,$null) } + [PowerStartHTML] Append($elType = "table") { return $this.Append($elType,$null,$null) } + [PowerStartHTML] Add($elType = "table",$className=$null,[string]$text=$null) { + $this.Append($elType,$className,$text) + $this.lastEl = $this.newEl + return $this + } + [PowerStartHTML] Add($elType = "table",$className=$null) { return $this.Add($elType,$className,$null) } + [PowerStartHTML] Add($elType = "table") { return $this.Add($elType,$null,$null) } + [PowerStartHTML] Main() { + $this.lastEl = $this.xmlDocument.html.body.ChildNodes[0]; + return $this + } + [PowerStartHTML] Up() { + $this.lastEl = $this.lastEl.ParentNode; + return $this + } + N() {} +} +class PowerStartHTMLPassThroughLine { + $object;$cells + PowerStartHTMLPassThroughLine($object) { + $this.object = $object; + $this.cells = new-object System.Collections.HashTable; + } +} +class PowerStartHTMLPassThroughElement { + $name;$text;$element;$id + PowerStartHTMLPassThroughElement($name,$text,$element,$id) { + $this.name = $name; $this.text = $text; $this.element = $element;$this.id = $id + } +} +function New-PowerStartHTML { + param( + [Parameter(Mandatory=$true)][string]$title, + [switch]$nobootstrap=$false + ) + $pshtml = (new-object PowerStartHTML($title)) + if(-not $nobootstrap) { + $pshtml.AddBootStrap().AddContainerAttrToMain().N() + } + return $pshtml +} +function Add-PowerStartHTMLTable { + param( + [Parameter(Mandatory=$True,ValueFromPipeline=$True)]$object, + [PowerStartHTML]$psHtml, + [string]$tableTitle = $null, + [string]$tableClass = $null, + [string]$idOverride = $(if($tableTitle -ne $null) {($tableTitle.toLower() -replace "[^a-z0-9]","-") }), + [switch]$passthroughTable = $false, + [switch]$noheaders = $false + ) + begin { + if($tableTitle -ne $null) { + $psHtml.Main().Append("h1",$null,$tableTitle).N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","header-$idOverride") + } + } + $psHtml.Main().Add("table").N() + [int]$r = 0 + [int]$c = 0 + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride") + } + if($tableClass -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"class",$tableClass) + } + [bool]$isFirst = $true + } + process { + $c = 0 + + $props = $object | Get-Member -Type Properties + if(-not $noheaders -and $isFirst) { + $psHtml.Add("tr").N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-trh") + } + $props | % { + $n = $_.Name; + $psHtml.Append("th",$null,$n).N() + if($idOverride -ne $null) { + $cellid = "table-$idOverride-td-$r-$c" + $psHtml.AddAttr($psHtml.newEl,"id",$cellid) + } + $c++ + } + $c = 0 + $psHtml.Up().N() + } + + $psHtml.Add("tr").N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-tr-$r") + } + $pstableln = [PowerStartHTMLPassThroughLine]::new($object) + + $props | % { + $n = $_.Name; + $psHtml.Append("td",$null,$object."$n").N() + $cellid = $null + if($idOverride -ne $null) { + $cellid = "table-$idOverride-td-$r-$c" + $psHtml.AddAttr($psHtml.newEl,"id",$cellid) + } + if($passthroughTable) { + $pstableln.cells.Add($n,[PowerStartHTMLPassThroughElement]::new($n,($object."$n"),$psHtml.newEl,$cellid)) + } + + $c++ + } + if($passthroughTable) { + $pstableln + } + $psHtml.Up().N() + $isFirst = $false + $r++ + } + end { + } +} + + +Export-ModuleMember -Function @('New-PowerStartHTML','Add-PowerStartHTMLTable') + + + diff --git a/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 b/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 new file mode 100644 index 0000000..2effa50 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 @@ -0,0 +1,24 @@ +$moduleRoot = Resolve-Path "$PSScriptRoot\.." +$moduleName = "VMware-vCD-TenantReport" + +Describe "General project validation: $moduleName" { + + $scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse + + # TestCases are splatted to the script so we need hashtables + $testCase = $scripts | Foreach-Object {@{file = $_}} + It "Script should be valid powershell" -TestCases $testCase { + param($file) + + $file.fullname | Should Exist + + $contents = Get-Content -Path $file.fullname -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors.Count | Should Be 0 + } + + It "Module '$moduleName' can import cleanly" { + {Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force } | Should Not Throw + } +}