Today I wanted to introduce a little product that I've created in the last few weeks called Dashimo. It doesn't cover everything I wanted from it (feature wise), but it already can be used in production. Therefore, I thought it would be a good idea to get some feedback on whether I should spend some more time on it or throw it in the dumpster. Dashimo joins it's older brother Statusimo of PowerShell modules allowing an easy way to build HTML output. If it will feel familiar, it's because it was inspired with Bradley Wyatt PowerShell script he did. It gave me the idea of how I would like to build something similar but in a bit different way then he did, with much more flexibility. Still, if it wasn't for him, the idea wouldn't be there, therefore you should send him your thanks.
While I could probably spend time here, trying to explain what it is, but I know it will take much less time to give you a picture. If you decide it's not for you to feel free to explore my other scripts/modules or read some blogs. I did create some useful content in the last two years.
That page you see above is generated in less than 40 lines of code. See for yourself
Dashboard -Name 'Dashimo Test' -FilePath $PSScriptRoot\Dashboard.html { Tab -Name 'Forest' { Section -Name 'Forest Information' -Invisible { Section -Name 'Forest Information' { Table -HideFooter -DataTable $DataSetForest.ForestInformation } Section -Name 'FSMO Roles' { Table -HideFooter -DataTable $DataSetForest.ForestFSMO } } Section -Name 'Forest Domain Controllers' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestDomainControllers } } Section -Name 'Forest Optional Features / UPN Suffixes / SPN Suffixes' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestOptionalFeatures -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestUPNSuffixes -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSPNSuffixes -Verbose } } Section -Name 'Sites / Subnets / SiteLinks' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestSites -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSubnets -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSiteLinks -Verbose } } } }
Well I know, I know. It's not really 40 lines. The code responsible for building HTML above is less than 40 lines, but the data in it requires a bit more code. Below is full code to generate four tabs of data.
Import-Module Dashimo -Force Import-Module PSWinDocumentation.AD -Force Import-Module PSWinReporting if ($null -eq $DataSetForest) { $DataSetForest = Get-WinADForestInformation -Verbose } if ($null -eq $DataSetEvents) { $DataSetEvents = Find-Events -Report UserChangesDetailed, UserChanges, UserLockouts, UserStatus, GroupChanges -Servers 'AD1', 'AD2' -DatesRange Last7days -Quiet } Dashboard -Name 'Dashimo Test' -FilePath $PSScriptRoot\Dashboard.html { Tab -Name 'Forest' { Section -Name 'Forest Information' -Invisible { Section -Name 'Forest Information' { Table -HideFooter -DataTable $DataSetForest.ForestInformation } Section -Name 'FSMO Roles' { Table -HideFooter -DataTable $DataSetForest.ForestFSMO } } Section -Name 'Forest Domain Controllers' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestDomainControllers } } Section -Name 'Forest Optional Features / UPN Suffixes / SPN Suffixes' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestOptionalFeatures -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestUPNSuffixes -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSPNSuffixes -Verbose } } Section -Name 'Sites / Subnets / SiteLinks' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestSites -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSubnets -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSiteLinks -Verbose } } } foreach ($Domain in $DataSetForest.Domains) { Tab -Name $Domain { Section -Name 'Domain Controllers / FSMO Roles' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainControllers -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainFSMO -Verbose } } Section -Name 'Password Policies' -Invisible { Section -Name 'Default Password Policy' { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainDefaultPasswordPolicy -Verbose } Section -Name 'Domain Fine Grained Policies' { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainFineGrainedPolicies -Verbose } } Section -Name 'Users' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainUsers } } Section -Name 'Computers' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainComputers } } Section -Name 'Groups Priviliged' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainGroupsPriviliged } Panel { #Chart -DataTable $DataSetForest.FoundDomains.'ad.evotec.xyz'.DomainGroupsPriviliged -DataNames 'Group Name' -DataCategories $DataSetForest.FoundDomains.'ad.evotec.xyz'.DomainGroupsPriviliged.'Members Count' -DataValues 'Members Count' } } Section -Name 'Organizational Units' { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainOrganizationalUnits } Section -Name 'OU ACL Basic' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainOrganizationalUnitsBasicACL } } Section -Name 'OU ACL Extended' { Panel { Table -HideFooter -DataTable $DataSetForest.FoundDomains.$Domain.DomainOrganizationalUnitsExtended } } } } Tab -Name 'Changes in Last 7 days' { Section -Name 'Group Changes' -Collapsable { Table -HideFooter -DataTable $DataSetEvents.GroupChanges } Section -Name 'User Status' -Collapsable { Table -HideFooter -DataTable $DataSetEvents.UserStatus } Section -Name 'User Changes' -Collapsable { Table -HideFooter -DataTable $DataSetEvents.GroupChanges } Section -Name 'User Lockouts' -Collapsable { Table -HideFooter -DataTable $DataSetEvents.UserStatus } } }
That's exactly 123 lines of code that generated four tabs of data. Nice huh? It is, at least to me 🙂 Now of course, as you may notice above I'm using two additional PowerShell Modules that I've written. The first one is PSWinDocumentation.AD and the other one is PSWinReporting (unfortunately that version is 2.0 edition, which is released as Preview to PSGallery therefore if you would like to use it you will have to do some jumps). To make it clear. You don't need those PowerShell modules. You're free to work with your objects, the way you like it. AD documentation and Events shown on screens are there for demo purposes.
A few days ago I've decided to split PSWinDocumentation module into a couple of smaller modules allowing for easy use of some features. The idea here is that those smaller modules will be easier to use in standalone projects, will be easier to modify and release fixes, and I hope (a lot really) that some people may invest their time to help me and build what I call DataSets. That means in coming weeks/months I will be rebuilding PSWinDocumentation and you should expect it to look like this:
There will be much smaller modules being part of PSWinDocumentation. I've started some work on ADConnect, but there's also the AWS edition. Wait and see or come and help out! I don't bite!
Dashimo is only having five functions for now and the idea is to add 6th function in the near future.
And that's it. Below code is all you need to understand how Dashimo works. It's simple, direct. You can add multiple panels, and it will add more of them. If you add three, you will have three columns, and if you add just two, you will have two columns. If you add 5, you will get five columns. The same idea is for sections. You can have multiple sections per row but they merely look differently, and those give you an additional way to name your data by using Name. Remember you can always use Invisible switch if you want to further position your data, just like I did using first, invisible section.
Dashboard -Name 'Dashimo Test' -FilePath $PSScriptRoot\Dashboard.html { Tab -Name 'Forest' { Section -Name 'Forest Information' -Invisible { Section -Name 'Forest Information' { Table -HideFooter -DataTable $DataSetForest.ForestInformation } Section -Name 'FSMO Roles' { Table -HideFooter -DataTable $DataSetForest.ForestFSMO } } Section -Name 'Forest Domain Controllers' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestDomainControllers } } Section -Name 'Forest Optional Features / UPN Suffixes / SPN Suffixes' -Collapsable { Panel { Table -HideFooter -DataTable $DataSetForest.ForestOptionalFeatures -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestUPNSuffixes -Verbose } Panel { Table -HideFooter -DataTable $DataSetForest.ForestSPNSuffixes -Verbose } } } }
The 6th function that I've planned is adding Charts. Charts are on their way but they require a bit more work as I need them to be easy to use, and just the way I want them. I am still experimenting with them, so it takes a little more time. I do have some charts that are ready (and you can see them below), but I haven't added all types that I wanted, and I am still struggling on data definitions for ease of use. But to keep you entertained some gallery is below.
Have I got you entertained? Most of those are generated with 1-4 lines of code depending on data you want to display. But like I said above it's not yet ready for Dashimo. Therefore it's only available as part of PSWriteHTML. Ah, that's right, I've almost forgotten. Dashimo is based on PSWriteHTML. PSWriteHTML is a ReportHTML fork that has gone thru a total redesign so if you fancy some experimenting feel free to see what I've cooked up on GitHub. There are about 15 examples to get you started. The thing with PSWriteHTML thou is that I experiment with it a lot. That means changes of parameters, changes of order, changes of naming convention. The idea behind Dashimo and Statusimo is that whenever I change something in PSWriteHTML that impacts those modules, I update them as well so that functionality should change much (still dependant on feedback from YOU). Do you remember how I mentioned Brad in the beginning? Brad's work actually got me into exploring ReportHTML but when I tried to use it I found out small things could break HTML and display things in an unpredictable way. You had to care for opening content, closing content. But if it weren't for ReportHTML this wouldn't happen so you can thank ReportHTML author as well, right after Brad.
Following screenshots are generated with Dashimo, but the data is coming from PSWinDocumentation.AD module. You can install it from PSGallery and if you think you can bring something useful from Active Directory perspective feel free to do PR or merely open an issue and we'll get the ball rolling.
Install-Module PSWinDocumentation.AD -Force
The following data is based on PSWinReporting 2.0. Please keep in mind that what is released in PSGallery as production version doesn't contain that.
Please keep in mind that if you have PSWinDocumentation the old version it may be conflicting with PSWinDocumentation.AD on some commands. I will fix that in the upcoming release of PSWinDocumentation as described above. Until then you can simply use AllowClobber switch if you get complains about conflicting command names. Also, keep in mind that I've not displayed everything in Dashimo. There are many more data available. Also if you're using only some data you may want to limit the output of that command by using RequiredTypes.
So you know how to use Dashimo? Well, this is how you install it.
Install-Module Dashimo -Force
That's it. Notice how I'm using Force. I use force because it actually redownloads Dashimo but also any required modules. You see when I update Dashimo, I often update PSWriteHTML and PSSharedGoods. When you do Update-Module Dashimo it won't auto-update required modules. Also if I will decide to split PSSharedGoods into smaller modules and change something about it Update-Module will also ignore this. That's why I prefer using Force switch and be prepared! You of course, should use it the way you want to!