Monday, March 26, 2018

Consolidate Dell Drivers - Part 1

Goal


I am trying to compare Dell driver packs that were extracted into a folder ( a very large list)
The goal is to filter by operating system, model, Dell CAB version (A01, A02, etc), and architecture.
I wrote a PowerShell script that will parse this folder structure (a small subset of the actual list):

\\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x64
\\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x86
\\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x64
\\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x86
\\share\drivers\E7470-win10-A05-6K6HR\E7470\win10\x64
\\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x64
\\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x86
\\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x64
\\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x86
\\share\drivers\E7470-win10-A09-PY2N2\E7470\win10\x64
\\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x64
\\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x86
\\share\drivers\E7470-win8.1-A01-DCD7V\E7470\win8.1\x64
\\share\drivers\9343-Win8.1-A04-HXWH4\9343\Win8.1\x64
\\share\drivers\E7470-win10-A07-WXGDV\E7470\win10\x64

Script Breakdown

First grab all the folders and add it to a collection:
$scriptRoot = '\\share\drivers'
#grab only root folders
$RootDriverCabs = Get-ChildItem $AllDriversPath | ?{ $_.PSIsContainer }

#create hashtable
$CabCollection = @()

#loop through folder within root folder and break it apart into an hashtable
ForEach ($Driver in $RootDriverCabs){
    #$FullDir = $Driver.FullName
    $Model = ($Driver.Name -split '-')[0]
    $OS = ($Driver.Name -split '-')[1]
    $ver = ($Driver.Name -split '-')[2]

    #combine root driver path with model and OS sub-folders
    $FullPath = Join-Path -Path $Driver.FullName -ChildPath "$Model\$OS"

 #if the full path exists get architecture
    If (Test-Path $FullPath){ 
        $arch = Get-ChildItem $FullPath | ForEach-Object {
            $FullDir = "$FullPath\$($_.Name)"
   
   #build object
            $CabTable = New-Object -TypeName PSObject -Property ([ordered]@{
                    Model    = $Model
                    OS       = $OS
                    Ver      = $ver
                    Arch     = $_.Name
                    FullPath = $FullDir
                })

    #add to object
                $CabCollection += $CabTable
            }
    } 
 Else{
        Write-host "$FullPath is not found, drivers must have proper naming convention" -ForegroundColor White -BackgroundColor Red
        break
    }   
}

Write-host "Collected $($CabCollection.Count) drivers packages"
the Cab Collection result is:
Model    : E6530
OS       : win10
Ver      : A01
Arch     : x64
FullPath : \\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x64

Model    : E6530
OS       : win10
Ver      : A01
Arch     : x86
FullPath : \\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x86

Model    : E7450
OS       : win10
Ver      : A04
Arch     : x64
FullPath : \\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x64

Model    : E7450
OS       : win10
Ver      : A04
Arch     : x86
FullPath : \\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x86

Model    : E7470
OS       : win10
Ver      : A05
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A05-6K6HR\E7470\win10\x64

Model    : E6540
OS       : win10
Ver      : A03
Arch     : x64
FullPath : \\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x64

Model    : E6540
OS       : win10
Ver      : A03
Arch     : x86
FullPath : \\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x86

Model    : M6700
OS       : win10
Ver      : A02
Arch     : x64
FullPath : \\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x64

Model    : M6700
OS       : win10
Ver      : A02
Arch     : x86
FullPath : \\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x86

Model    : E7470
OS       : win10
Ver      : A09
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A09-PY2N2\E7470\win10\x64

Model    : E6500
OS       : win7
Ver      : A05
Arch     : x64
FullPath : \\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x64

Model    : E6500
OS       : win7
Ver      : A05
Arch     : x86
FullPath : \\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x86

Model    : E7470
OS       : win8.1
Ver      : A01
Arch     : x64
FullPath : \\share\drivers\E7470-win8.1-A01-DCD7V\E7470\win8.1\x64

Model    : 9343
OS       : Win8.1
Ver      : A04
Arch     : x64
FullPath : \\share\drivers\9343-Win8.1-A04-HXWH4\9343\Win8.1\x64

Model    : E7470
OS       : win10
Ver      : A07
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A07-WXGDV\E7470\win10\x64
The count is 15 with all architectures
Now I need to filter them. I am then comparing them with other arrays (set as constants:
#Specify which type of folders to look for based on Operating System.
#Supported Values: Win7, Win8, Win8.1, Win10
$OSCheck = @('Win10')

#Specify which type of folders to look for based on architecture.
#Supported Values: x86,x64
$archCheck = @('x64')

#Break it down even further based on model.
$FilterByModel = $true 
$DellModels = @('E7470','E7450','E6530','E6540')

#search driver types
$FilterbyDriverCategory = $true
$DriverCategory = @('audio','chipset','communication','input','network','security','storage')
Based on theses filters I should get:
5 models with x86 drivers
2 unsupported drivers: E6500, M6700
3 unsupported OS: E6500(Win7), 9343(Win8.1), E7470(Win8.1)
3 drivers in conflict: E7470(A01), E7470(A07), E7470(A09)
With filters in place, it should leave me with:
\\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x64
\\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x86
\\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x64
\\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x86
\\share\drivers\E7470-win10-A05-6K6HR\E7470\win10\x64
\\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x64
\\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x86
\\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x64
\\share\drivers\M6700-win10-A02-T08R2\M6700\win10\x86
\\share\drivers\E7470-win10-A09-PY2N2\E7470\win10\x64
\\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x64
\\share\drivers\E6500-win7-A05-JJ15G\E6500\win7\x86
\\share\drivers\E7470-win8.1-A01-DCD7V\E7470\win8.1\x64
\\share\drivers\9343-Win8.1-A04-HXWH4\9343\Win8.1\x64
\\share\drivers\E7470-win10-A07-WXGDV\E7470\win10\x64
I am able to filter OS, Arch and Model:
#Create new hashtable for temporary use
$CabCollection_oschk = @()
Foreach ($driverSet in $CabCollection){ 
    #filter by Operating System
    $OSFound = Compare-Object $driverSet.OS $OSCheck -IncludeEqual | Where-Object {$_.SideIndicator -eq "=="}
    If($OSFound){
        #write-host $driverSet.FullPath
        $CabTable_oschk = New-Object -TypeName PSObject -Property ([ordered]@{
                Model    = $driverSet.Model
                OS       = $driverSet.OS
                Ver      = $driverSet.Ver
                Arch     = $driverSet.Arch
                FullPath = $driverSet.FullPath
            })

            $CabCollection_oschk += $CabTable_oschk
    }  
        
}
#Rebuild original collection
$CabCollection = $CabCollection_oschk
Write-host "$($CabCollection.Count) drivers filtered for specified operating systems" -ForegroundColor Cyan

#Create new hashtable for temporary use
$CabCollection_archchk = @()
Foreach ($driverSet in $CabCollection){ 
    #filter by architecture
    $ArchFound = Compare-Object $driverSet.Arch $archCheck -IncludeEqual | Where-Object {$_.SideIndicator -eq "=="}
    If($ArchFound){
        #write-host $driverSet.FullPath
        $CabTable_archchk = New-Object -TypeName PSObject -Property ([ordered]@{
                Model    = $driverSet.Model
                OS       = $driverSet.OS
                Ver      = $driverSet.Ver
                Arch     = $driverSet.Arch
                FullPath = $driverSet.FullPath
            })

            $CabCollection_archchk += $CabTable_archchk
    }  
        
}
#Rebuild original collection
$CabCollection = $CabCollection_archchk
Write-host "$($CabCollection.Count) drivers filtered for specified architecture" -ForegroundColor Cyan

If ($FilterByModel){
    #Create new hashtable for temporary use
    $CabCollection_modelchk = @()
    Foreach ($driverSet in $CabCollection){ 
        #filter by model
        $ModelFound = Compare-Object $driverSet.Model $DellModels -IncludeEqual | Where-Object {$_.SideIndicator -eq "=="}
        If($ModelFound){
            #write-host $driverSet.FullPath
            $CabTable_modelchk = New-Object -TypeName PSObject -Property ([ordered]@{
                    Model    = $driverSet.Model
                    OS       = $driverSet.OS
                    Ver      = $driverSet.Ver
                    Arch     = $driverSet.Arch
                    FullPath = $driverSet.FullPath
                })

                $CabCollection_modelchk += $CabTable_modelchk
        }  
        
    }
    #Rebuild original collection
    $CabCollection = $CabCollection_modelchk
    Write-host "$($CabCollection.Count) drivers filtered for specified models" -ForegroundColor Cyan
}
This leaves me with 6 models, 1 of them having multiple versions of drivers.
Model    : E6530
OS       : win10
Ver      : A01
Arch     : x64
FullPath : \\share\drivers\E6530-win10-A01-PXT6R\E6530\win10\x64

Model    : E7450
OS       : win10
Ver      : A04
Arch     : x64
FullPath : \\share\drivers\E7450-win10-A04-DNY52\E7450\win10\x64

Model    : E7470
OS       : win10
Ver      : A05
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A05-6K6HR\E7470\win10\x64

Model    : E6540
OS       : win10
Ver      : A03
Arch     : x64
FullPath : \\share\drivers\E6540-win10-A03-FHWDF\E6540\win10\x64

Model    : E7470
OS       : win10
Ver      : A09
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A09-PY2N2\E7470\win10\x64

Model    : E7470
OS       : win10
Ver      : A07
Arch     : x64
FullPath : \\share\drivers\E7470-win10-A07-WXGDV\E7470\win10\x64
After many of hours trying to figure out how to filter the cab versions, i have figured it out, but felt there could be an easier way. here is my code to filter them:
#Create new hashtable for temporary use
$CabCollection_verchk = @()

Foreach ($driverSet in ($CabCollection | Group-object Model | Where-Object {$_.Count -eq 1}) ){
    $CabTable_verchk = New-Object -TypeName PSObject -Property ([ordered]@{
        Model    = $driverSet.Group.Model
        OS       = $driverSet.Group.OS
        Ver      = $driverSet.Group.Ver
        Arch     = $driverSet.Group.Arch
        FullPath = $driverSet.Group.FullPath
    })

    $CabCollection_verchk += $CabTable_verchk
}

Foreach ($driverSet in ($CabCollection | Group-object Model | Where-Object {$_.Count -gt 1}) ){
    $SimilarModels = $driverSet | Foreach {$_.Group}
    $LatestVersion = ($SimilarModels.ver | measure -Maximum | Select -First 1).Maximum
    Foreach ($Model in ($SimilarModels | Where-Object {$_.Ver -eq $LatestVersion}) ){
        $CabCollection_verchk += $Model
    }
}

#Rebuild original collection
$CabCollection = $CabCollection_verchk
Write-host "$($CabCollection.Count) drivers filtered for latest version" -ForegroundColor Cyan
Not I only see 4 drivers in my collection. The only next thing to do is grab those drivers and copy them to another folder. This should drop the size down dramatically, eepecially if you don't include video drivers (which you should exclude becuse you should download jsut the three catalyst drivers from (ati,intel,nvidia)
mine went down from 13gb to 3gb.

You can find the code here: DriverConsolidator

Now the next thing to do is filter the drivers inf even further to grab only the latest....coming soon

Wednesday, September 20, 2017

Installing PowerCLI on a disconnected network

It is a constant battle working on multiple secured disconnected networks when everything is going to the cloud. What's worse its now XAAS (Everything-as-a-Service) is the new norm and I guess this means Powershell modules as well...specifically the most important one for our environment, PowerCLI.

We upgrade out vCenter environment to 6.5.2 and I figured I wanted the same PowerCLI version

I guess starting with 6.5.1 it's now a module you can get only from Microsoft PowerShell Gallery. But I want to use 6.5.2...

HINT: You can still use the 6.5.0 installer if you like, but I like using the latest greatest and starting with

There is an article that states how to do this, but I found this does not work entirely.
https://blogs.vmware.com/PowerCLI/2017/08/new-release-powercli-652.html


I have downloaded the PowerCLI using the command

Save-Module -Name VMware.PowerCLI -Path <path\to\folder>

Copied it an external drive, moved it over and copied it into my profile path (C:\Users\Username\Documents\WindowsPowerShell\Modules\). Opened a new instance of powershell and its not loaded nor is it recognized as a module.

$env:PSModulePath does state my path is one of the locations. Hmmm...

I first try to import the module manually, but I kept getting the error when loading its first dependency, VMWare.VimAutomation.Sdk:

The log4net assembly is explicitly loaded by the PowerShell because it is in the RequiredAssemblies list of the VMware.VimAutomation.Sdk.psd1 module manifest (see VMware.VimAutomation.Sdk.psd1).

Huh? 

After troubleshooting this over and over and reading online forums about it and getting nowhere, for shits and giggles, I tried running this command on my disconnected system, thinking it would find the modules on my profile or at lease an error I could follow:

Install-Module -Name VMware.PowerCLI

Instead I get an message saying I need to get that latest nuget. What?


I was curious, I decided to click Yes knowingly it will not work, but thought it would give me an error code I could work with:


No file information but it did give me a link....

http://go.microsoft.com/fwlink/?LinkID=627338

I go to my internet connected PC type in that link. It takes me down the rabbit hole of the azure cloud

I get redirected to: https://az818661.vo.msecnd.net/providers/providers.masterList.feed.swidtag

Which then I find the link for Nuget: https://oneget.org/nuget-2.8.5.208.package.swidtag

That link takes me to the actual dll: https://oneget.org/Microsoft.PackageManagement.NuGetProvider-2.8.5.208.dll

Down the rabbit hole I go....

Based on this I search where this dll's get installed at on my connected system:
C:\Program Files\PackageManagement\ProviderAssemblies\nuget\2.8.5.208\Microsoft.PackageManagement.NuGetProvider.dll

Cool. I copied the dll over to my disconnect system (using the same folder structure). Then I re-ran just to see if I get prompted again:

Install-Module -Name VMware.PowerCLI -Verbose

No nuget message but now all I get is it can't find the repository. Great....not



Obviously I'm not connected to the internet so the repository its looking for is not available. What now?

Lets install PowerCLI manually.  After trying to manually importing the VMware.PowerCLI.psd1;

Install-Module $home\Documents\WindowsPowershell\Modules\VMware.PowerCLI\6.5.2.6268016\VMware.PowerCLI.psd1

It came back with dependencies that weren't install.

I import the module VMWare.VimAutomation.Sdk, success!

Run the import again...another dependencies....there are 17 more to go....

The PowerCLI does have 18 other modules that are required to be loaded first. But which one first? If you try to import the each dependencies one by one (alphabetically it will not work), they too have dependency errors. If you open the VMware.PowerCLI.psd1 file in notepad (or ISE) you will see the dependencies with their version numbers:


I thought that by running this it would auto install the modules for me...Nope

Install-Module $home\Documents\WindowsPowerShell\Modules\VMware.PowerCLI\6.5.2.6268016\VMware.PowerCLI.psd1

If I run through each module in the order it specifies, they all install just fine calling each psd1 file. However Vmware.VIMAutomation.PSCloud dependency is Vmware.VIMAutomation.Cloud and Vmware.VIMAutomation.Storage requires Vmware.VIMAutomation.SotrageUtility, yet they are not in that order. I could change the order in the psd1 file, but I didn't want to mess with what VMware release.

Instead I wrote my own installer ps1 file. I wrote it to parse the required module in VMware.PowerCLI.psd1, then add it to a hashtable with its version, then find those folders and look in their psd1 file for sub dependency, then install in the appropriate order.

These are the features added to the script:
  • Find the latest downloaded PowerCLI version folder, and copy the files to the users profile Powershell directory
  • Include a progress bar for coping
  • Copy Nuget folder (located in PowerCLI version folder) to C:\Program Files\PackageManagement\ProviderAssemblies
  • Disable CEIP
  • Copy a modified version of Initialize-PowerCLIEnvironment.ps1 to the users profile PowerCLI directory
  • Copy a icon to the users profile PowerCLI directory
  • Create PowerCLI desktop shortcuts, x86 and x64 (like the installer did, pointing to Initialize-PowerCLIEnvironment.ps1  and using the icon)
  • Load PowerCLI when done. 
The difference between old installer, it used to copy the modules to the C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Modules, add Initialize-PowerCLIEnvironment.ps1 to theC:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts folder, a a startmenu and desktop shortcuts to that scripts for 32-bit and 64-bit Powershell and add an entry into the PSModulePath system environment variable. 


You will also need to have a copy of nuget with the saved version of PowerCLI. The folder structure should look like this:


If there is a later version that comes out, the script does detect that folder or you can update the variable (line 4)

$LatestModuleVersion = '6.5.2.6268016'


You can get the PowerShell script on github (updated 2/8/2019): PowerCLIInstaller

Coming soon...version 10




Monday, September 11, 2017

OSD Computer Name Prompt

I work with a lot of Microsoft products, one of my favorite is Microsoft Deployment Toolkit and System Center Configuration Manager, otherwise known as MDT and SCCM (Sick-em boy). My favorite part is customizing the Windows Operating System. In my company, I work with the latest. Windows 10/SCCM 2012 R2 (1511)...yes there is a later one.

What I enjoy is customizing Task Sequences. Writing PowerShell GUI's to control variables. Yes I know SCCM with MDT integrated and using its built in UDI wizard does all this for you, but what fun is that?

Here is a PowerShell I wrote to prompt for a computer name.



I have tested this against most of our Dell Systems and it does come back with the correct information. The cool thing is I loaded all the images as binary so it will load the proper image when prompted. Obviously as newer models come out; they too would need to be added.

Here are some things it checks
  • Chassis types
  • Check current name if MININT (prompt)
  • Name Validation check

To get SCCM to prompt the computer name correctly, you will need a few prerequisites.
  • If your using the SCCM (1511) and its latest ADK, be sure to provision a new Boot image so that the WinPE is matched and apply the hotfix: 3143760
  • To allow this script to be interactive, copy the ServiceUI.exe and TSProgressUI.exe from the MDT's <DeploymentShare>\tools\x64 folder (can also be found under the MDT installation folder in Templates). Add it as part of the script package. 
  • Add Run Command line in Task sequence. Add the package to is and run the command like this: ServiceUI.exe -process:TSProgressUI.exe %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File Set-OSDComputerName.ps1

The script can be found here: Set-OSDComputerName.ps1

This is very basic script but it has potential to be more...

Friday, September 8, 2017

PowerCLI Datastore Uploader GUI

Since VMware is forcing the webgui for their vSphere client; you will require the vSphere plugin to be able to upload files to the datastore. however the plugin doesn't work on every browser; Chrome and IE are the only ones I found that works...sometimes.

However, there are other ways to accomplish this: PowerCLI. The task isn't to hard; VMware has a KB on how to do it.

Basically you need to connect to the vcenter server, get the datastore and folder (if needed), map a PSDrive to that datastore, then run the Copy-DatastoreItem command. Simple right? It is for an experienced administrator. But I like to make things even simpler; why not build a UI for it using XAML and PowerShell?

Here we go again....





This simple form will check if PowerCLI is installed, if not, then either check a specified network directory for the module or get the latest from https://www.powershellgallery.com using NuGet 

Since I work on disconnected networks; the network share is useful for our environments

It will also store the last used vCenter/IP in a file under your temp directory. I thought about making a dropdown list for it for multiple vCenter scenarios.  

The progress bar in the UI does not work yet. I am still working on a runspace for it so it doesn't lock up the menu; however if you will see the progess in the Powershell CLI or ISE

This form can be found on https://github.com

Oh the fun things you can do with powershell....




Thursday, November 17, 2016

Admin Run-As Menu

I found that over the years accessing tools can be time consuming especially when multiple accounts are used for different tools. I developed a PowerShell driven menu (using the latest WPF technology and Mahapps theme) that is highly customizable.

This tool isn't perfect and I'm sure there are many coding flaws with it, but I have tested and implemented it in my IT environment and everyone loves it especially the helpdesk. I designed this tool with one thing in mind: to make a simple push button menu that will launch the tools needed with the appropriate permissions.




When launched you will be presented with 6 tabs (I originally had just one, but found the other shops liked it as well), each tab menu supports a different section. In this screenshot, I have the system admins, Enterprise Admins, SCCM / Automation Team (that's me), Exchange Team and Infrastructure Team. I've added the 6 menu as an Extra Tab for additional tools that are used almost daily.




How it works is when the main PowerShell (AdminMenu.ps1) is called (link from a desktop shortcut), it loads 2 primary functions PowerShell scripts (AdminMenuExtensions.ps1 and ImageBase64Resources.ps1, then it loads 6 more PowerShell scripts for the Tabs (AdminMenu-Tab#.ps1).

However I have added more functionality so in reality it loads a total of 14 ps1 files!

I wrote the UI using Visual Studio 2015 Community Edition so as the Admin Menu launches it loads the XAML form with a XML configuration file.

A new element I've added is, it loads a notification icon and a quick access (always on top) button in the bottom right corner that when clicked it will either hide or open the menu in front of any other window (works most of the time)


Now each button is displayed using a configuration file written in XML. There are also options in the configure file I will get over later. The great thing is this menu is highly customizable because each tab (with exception of Tab 6) can hold 26 configurable buttons.
 


Some of the main buttons on each tab (left column) launches a console specified in the AdminMenu-Tab#.ps1 file. The menu in the right column are scripts or other useful tools an admin uses (mostly scripts). Almost 100% of the scripts it uses are written in PowerShell and are all menu driven scripts. Most of them I got from https://gallery.technet.microsoft.com, others I have found on the net or I write myself. I was only interested in GUI driven menus.

The real drive for this menu was because at work I use three different accounts to access different consoles. I needed a way to temporary store my credentials securely so I can launch any console on the fly. The Credential Management section on the far right does just that. It will store up to three credentials that are selectable. This only stores it for the time the menu is open, however if the menu is closed and reopened it does remember that username and domain entered, but the password has to be re-entered for it to be selectable again.

How this credential system works: when you enter alternate credentials it stores them in a global variable and to an encrypted file. When selected and the user presses a button on a tab, it actually calls a function that launches the exe or bat or vbs or whatnot, with that credential variable using -Credential switch.

...hence the name Admin Run-As Menu

I used FIPS compliance algorithms with encrypted cred file to ensure it was as secure as possible.

Now to make this script easy to use and easy to configure, obviously a lot of complicated scripts must be written to do this, but ultimately there are only a few things to edit to make it customized; the configuration file along with the corresponding tab

Like I mentioned before the Admin Run-As Menu, when launched, reads a configuration file to display the buttons color and name along with other settings:
  • Theme color
  • Accent color
  • Require Privilege Administrator access (UAC)
  • RSAT Check
  • Debug Mode
  • Hide all buttons not used (instead of a gray outline.
  • Hide app if not top window
  • Hide Credential Management feature
  • Load additional PowerShell modules
  • Load additional PowerShell Extension scripts
  • Use remote confit file
Now to control what the button do after editing the xml file:


The corresponding Tab PowerShell script has to be modified to match the process its calling like this:


As you see I designated function section for each button name (eg: Call-btnTab1_01,Call-btnTab1_02, Call-btnTab1_03, etc.)

In each of these function this is where you write the PowerShell code you want it to do. I made this simpler by just using the built-in function: Start-ButtonProcess. This function has many parameters. here is what is wrote as the guide:


USE INTGRATED FUNCTION (EASIEST)
- to log to output box in tab, use -OutputTab switch
- to identify process alias, use -Alias switch, otherwise the button name will be used
- to specify a custom run message use the -CustomRunMsg switch
- to specify a custom error message use the -CustomErrMsg switch (this will only display if errors)
- Auto use credentials feature if selected in menu, you can force it not to by adding -NeverRunAs switch

EXAMPLE (EXE):
Start-ButtonProcess -Alias "$ButtonClicked" -Path "$PSHOME\PowerShell.exe" -WorkingDirectory "$envWinDir\System32" -OutputTab tab1 -WindowStyle Normal

EXAMPLE (EXE) with Parameters:
Start-ButtonProcess -Alias "$ButtonClicked" -Path "$envWinDir\system32\mstsc.exe" -Parameters ("/v:" + $WPFtxtTab1Name1.Text) -OutputTab tab1 -WindowStyle Normal

EXAMPLE (PS1):
Start-ButtonProcess -ProcessCall ps1 -File "Start-PoshPAIG.ps1" -WorkingDirectory "$($scriptRoot + "\Scripts\PoshPAIG_2_1_5")" -CreateNoWindow



Nothing is flawless, there are parts of the code that could have written better. I thought about compiling this into an full executable, but I wanted it highly customizable and easy to update if needed, however I did compile an executable wrapper to call the AdminMenu.ps1 file (source code is below).

If you want to review the source, you can download it from here (GitHub):
Source: Admin Run-As Menu 3.9.9.1 (source)

The installer version is simply a compressed version of the source code that extracts and registers to a windows system that its installed. It will also place a shortcut on the desktop. There are two versions I have, one that used the exe wrapper (not signed) as the entry point, the other is just a shortcut to PowerShell calling a file.

Installer: Admin Run-As Menu 3.9.9.1 (Installer)
(the installer is written with Inno Setup, its source is on GitHub as well).

Installer with no EXE Wrapper: Admin Run-As Menu 3.9.9.1 (No Wrapper)

EXE Source Code: EXE Wrapper

*I also have a full version (it contains 16mb of useful PowerShell driven GUI downloaded all over the internet that are added to the menu which I used to screenshot the above pics. I don't post the full installer due to the sources of the script and copyrights, if you'd like a download link send me a message or email.)


I want to thank all the people on Gallery TechNet for writing wonderful PowerShell GUI scripts. My inspiration comes from here:
https://gallery.technet.microsoft.com/
https://blog.netnerds.net/2016/01/adding-toolbar-icons-to-your-powershell-wpf-guis/
https://foxdeploy.com/2015/04/16/part-ii-deploying-powershell-guis-in-minutes-using-visual-studio/
http://www.systanddeploy.com/2016/01/powershell-gui-add-mahapps-metro-theme.html?m=0
https://psappdeploytoolkit.codeplex.com/

If you work in IT, this menu will help you out. Enjoy 👍

Features I would like to add are (and working on):
  • Run space menu and buttons instead of single threaded
  • Disable buttons if software not installed (prereq check utility)
  • Main option menu (controls paths, themes and other main settings)
  • Quick Menu Option (5 immediate access buttons always on top of screen)