Active Directory Filter memberof - sharepoint

I am trying to get all of the CN's out of active directory in order to populate groups based on that name into Sharepoint Services. I can list the "memberof" section but I can not seem to split it using split(",")
$Dom = 'LDAP://OU=External,OU=Users,OU=HomeOffice,DC=mydoman,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)"
Write-host $person.properties["memberof"]
}
"Total $i"
Now I get everything I need, but I need some way to filter only the CN's out...

As a general rule, write-host is not the best way to generate output. Ideally, you want to emit objects out of your function and let PowerShell do the formatting for you. This is the more "pipeline friendly" way of doing things. In this case, if you had a function Get-GroupMembers you could pipe it to something like
Get-Person | ft CN
The trick is creating a new object and adding properties to it, or just emitting the DirectoryServices object you are pulling already. To create a new custom object you can do the following:
$obj = new-object psobject
$obj | add-member -membertype noteproperty name $PropName -value $valueToStore
People can use your function and pipe it to format-table, format-list, select-object, group-object, sort-object and a variety of other things. Keith Hill's Effective PowerShell has a great chapter on Output that you might find helpful.
There is also an article by Don Jones on using objects instead of text that is quite good as well.

test1.ps1
#Connet using LDAP
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#Integer for the loop
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#Find the Groups
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
#Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)" -foregroundcolor Magenta
$test = $person.properties["memberof"]
ForEach-Object {
$test`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
-replace ",","`r`n"
}
}
test2.ps1
# Lets start with a clean slate :)
Clear
# Lets reference the assembly / GAC that we need for this
#region
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$SPSite = New-Object Microsoft.SharePoint.SPSite("https://myintranetorextranetsite.myfqdn.com")
$OpenWeb = $SpSite.OpenWeb("/")
#endregion
# Add some eye candy :)
# region
# I really wanted some old school thing in here :)
write-host " _ ____ ____ " -foregroundcolor Magenta
write-host " / \ | _ \ / ___| _ _ _ __ ___ " -foregroundcolor Magenta
write-host " / _ \ | | | |____\___ \| | | | '_ \ / __|" -foregroundcolor Magenta
write-host " / ___ \| |_| |_____|__) | |_| | | | | (__ " -foregroundcolor Magenta
write-host "|_/ \_\____/ |____/ \__, |_| |_|\___|" -foregroundcolor Magenta
write-host " |___/ " -foregroundcolor Magenta
Write-Host " Version 2.0" -foregroundcolor Red
Write-Host " Build 2009 09-11 21:30" -foregroundcolor Red
Write-host " Created by Mitchell J. Skurnik" -foregroundcolor Red
#endregion
# Create the stopwatch
#region
[System.Diagnostics.Stopwatch] $sw;
$sw = New-Object System.Diagnostics.StopWatch
$sw.Stop()
$sw.Start()
#endregion
# Function to control Adding groups
function creategroup
{
param ([string] $siteurl = "https://myintranetorextranetsite.myfqdn.com")
$site = New-Object Microsoft.SharePoint.SPSite($siteurl)
$web = $site.RootWeb;
$group = $currentgroup;
$perm = "Read";
$owner = "jdoe";
if ($owner -eq "") { $owner = $web.CurrentUser.LoginName }
$exists = $web.SiteGroups | where { $_.Name -eq $group }
if ($exists -eq $null)
{
# Create group
$web.SiteGroups.Add($group, $web.EnsureUser($owner), $null, "");
# Give permissions to the group
$assign = New-Object Microsoft.SharePoint.SPRoleAssignment($web.SiteGroups[$group]);
$assign.RoleDefinitionBindings.Add($web.RoleDefinitions[$perm])
$web.RoleAssignments.Add($assign)
Write-Host -ForegroundColor green "Creating sharepoint group - " $currentgroup;
}
$site.Dispose();
}
# Function to add users to the specified group
function addUser
{
# Open a connection to the sharepoint site and then select the sub site you want
$themail = $prop.mail
$thedisplay = $prop.displayname
# If there are accounts that dont have some info lets populate it
if ($themail -eq "")
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq "")
{
$thedisplay = "Account, Test"
}
if ($themail -eq $null)
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq $null)
{
$thedisplay = "Account, Test"
}
$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#write-host "Added: " $thedisplay -foregroundcolor Red
}
# Function to remove people - be careful using this script :(
# Also not done
function removeUser
{
#$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
#$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#$TheNewGroup.Remove($LoginToDel)
}
# Now onto the real stuff
Write-host "Searching for Groups" -foregroundcolor Green
# Clear out the existing text file so we have a clean slate
$file = New-Item -type file "C:\location\to\my\folder\allGroups.txt" -Force
# Execute the Group Dump Script
C:\location\to\my\folder\test.ps1 | Out-File -filepath "C:\location\to\my\folder\allGroups.txt" -append
# Clean up the list by removing duplicates and sorting everything
$TextFile = $TextFile = "C:\Powershell\allGroups.txt"
$NewTextFile = "C:\Powershell\allGroups - Sorted.txt"
GC $TextFile | Sort | GU > $NewTextFile
# Use LDAP to connect to Active Directory
#region
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#endregion
# Create a selector and start searching from the Root of AD
#region
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#endregion
# Integer to compare file length
$c=0
# Get the Group text file's length and write to scree and variable
$fileLength = [System.IO.File]::ReadAllText($NewTextFile).Split("`n").Count
Write-Host "Found " $fileLength "Groups in Active Directory" -foregroundcolor Magenta
# Integer for thumbing through 'memberOf' in active directory
$d = 0
# Integer for the amount of of users found
$f = 0
# Start a while loop where we read through the entire groups text file
while ($c -le $fileLength)
{
# Increment the line number for the next pass through
$c++
# Grab the first line of text from the groups file (Really the 0th line) and then tell the user
$currentGroup = (Get-Content $NewTextFile)[$c]
# Create the group
CreateGroup
#Write-Host "Created Group: " $currentGroup -foregroundcolor Red
#
Write-host $c "/" $fileLength "`t" $currentGroup -foregroundcolor Red
# Query Active directory and force some commands
$adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj)
{
# Variable for the different properties to reduce fatigue
$prop=$person.properties
# The Department
$department = $prop.department
# Sir Name
$sn = $prop.sn
# Given Name
$gn = $prop.givenname
$un = $prop.samaccountname
# Assign the really long memberof to a variable
$memberof = $person.properties["memberof"]
# Length of memberof
$memberofcount = $test.Count
# Loop for each group the member is in
while ($d -le $memberof.Count)
{
$blah = ForEach-Object{`
$memberof[$d]`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
}
# Incriment the d
$d++
# Is that user in the group?
if ($blah -eq $currentGroup)
{
# Hey look we found somebody in that group :)
Write-host "`t`t`t" $un -foregroundcolor Magenta
addUser
$f++
}
#elseif ($blah -ne $currentGroup)
#{
# removeUser
#}
else
{
# Oh noes...nobody is in that group...that is strange
}
}
# Are we at the end of what the user has
if ($d -ge $memberofs.Count)
{
# Looks like we are :)
$d=0
}
}
# Display amount of users found
#Write-Host "`t`t`t" $f " user(s) found"
$f = 0
}
# Stop Watch
$sw.Stop()
# Write the compact output to the screen
write-host "Updated in Time: ", $sw.Elapsed.ToString()
#This space is saved for future development

Related

How to add colors to Excel output file in Powershell

I have written a script to export specific registry keys and the sub keys inside it with the server ping response, but my scripts works as expected and I can able to export that to Excel as well.
But I need inputs or some help on how to add the colors to the Excel output column based on the value.
As Ex: in my script I will get ping response as true or false, for True I need to add green colour and for False I need to add Red color in my output, please help me to achieve this with my script.
CODE
## Get full list of servers
$Servers = GC -Path ".\Servers.txt"
## Loop through each server
$Result = foreach ($vm in $Servers) {
## Check the Ping reponse for each server
Write-Host "Pinging Server" $vm
$Ping = Test-Connection -Server $vm -Quiet -Verbose
if ($Ping){Write-host "Server" $vm "is Online" -BackgroundColor Green}
else{Write-host "Unable to ping Server" $vm -BackgroundColor Red}
## Check the Network Share path Accessibility
Write-Host "Checking Share Path on" $vm
$SharePath = Test-Path "\\$vm\E$" -Verbose
if ($SharePath){Write-host "Server" $vm "Share Path is Accessible" -BackgroundColor Green}
else{Write-host "Server" $vm "Share path access failed" -BackgroundColor Red}
Invoke-Command -ComputerName $vm {
## Get ChildItems under HKLM TCPIP Parameter Interface
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces' | ForEach-Object {
Get-ItemProperty -Path $_.PSPath | Where-Object { $_.PsObject.Properties.Name -like 'Dhcp*' }
} | Select-Object -Property #{Name = 'ComputerName'; Expression = {$env:COMPUTERNAME+"."+$env:USERDNSDOMAIN}},
#{Name = 'Ping_Response'; Expression = {if($using:Ping) {'Pinging'} else {'Unable to ping'}}},
#{Name = 'Share_Path_Access'; Expression = {if($using:SharePath) {'Accessible'} else {'Not Accessible'}}},
DhcpIPAddress, #{Name = 'DhcpNameServer'; Expression = {$_.DhcpNameServer -split ' ' -join '; '}},
DhcpServer, #{Name = 'DhcpDefaultGateway'; Expression = {$_.DhcpDefaultGateway -join '; '}}
}}
$Result | Select-Object * -Exclude PS*, RunspaceId | Export-Excel -Path "$PSScriptRoot\TCPIP_Interface_Details.xlsx" -AutoSize -BoldTopRow -FreezeTopRow -TitleBold -WorksheetName TCPIP_Interface_Details
You can use the New-ConditionalText cmdlet to highlight cells containing the specified -Text with the color of our choice. The cmdlet can also take RGB colors. I encourage you to read the documentation on it, there are also many examples:
Get-Help New-ConditionalText
Since I don't have access to your $result object I can only give you an example of how you can do it using a simple example:
$result = 0..10 | ForEach-Object {
[pscustomobject]#{
ComputerName = 'Host' + $_
Ping_Response = ('Not Responding', 'Pinging')[($_ % 2)]
}
}
function RGB ($red, $green, $blue ){
return [System.Double]($red + $green * 256 + $blue * 256 * 256)
}
$fontGreen = RGB 0 97 0
$backGreen = RGB 198 239 206
$condProps = #{
Text = 'Pinging'
ConditionalTextColor = $fontGreen
BackgroundColor = $backGreen
}
$conditionalTrue = New-ConditionalText #condProps
$conditionalFalse = New-ConditionalText -Text 'Not Responding'
$props = #{
AutoSize = $true
InputObject = $result
Path = 'test.xlsx' # => Use your absolute Path here!
TableName = 'myTable'
TableStyle = 'Medium11'
WorksheetName = 'myWorkSheetName'
ConditionalText = $conditionalTrue, $conditionalFalse
}
Export-Excel #props
The end result should look something like this (unfortunately Google Sheets doesn't do it justice):

How to find a word/phrase in a line using PowerShell?

I have been at this for days now but can't seem to figure it out.
I am trying to compare values(PartNumber) from the database to the first line of the text file. If the PartNumber exists(in the first line of the file) display a message "Part Number Matches" if it doesn't send an email.
The issue is that I get messages saying parts do not match when they are clearly the same.
All wrongly mismatched part numbers have a letter in the beginning for ex. D78948, C56234, etc. At first I thought maybe it is because the part number is alphanumeric. But then the parts that are correctly matched are also alphanumeric.
The PartNumber in the terminal is the one from the database.You can clearly see from the picture that the part number from the database and the one in the text file are exactly the same but the check still fails.
Instead of If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches) I have tried
If($eiaContent -like "*$partno*") ,
If($eiaContent | Select-String -Pattern $partno)
But none of them seem to work.
Any help is appriciated!
Code:
$emailFrom = " CNC Data Discrepancy <DoNotReply#outlook.com>"
$emailTo = "ABC#nicholsonmfg.com"
$server = "Sidney.sidney.nii"
$database = "NML_Sidney"
#Query to get MAFN values in the last x hours(ET-Elapsed Time)
$MafnQueryEt = "SELECT [MAFN] As MAFNET FROM [NML_Sidney].[dbo].[MfgAidsDocLibraryTransactions]"
#Invoke Sql
$MafnSqlEt = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $MafnQueryEt
Write-Host $MafnSqlEt.MAFNET
$aidLibPathFolder = "C:\Users\Desktop\CNC_Transfer_Test_Folders\AidLib_Test"
$filterA = "MAZAK INTEGREX E-1550 WFO"
$filterB = "MILLING_MAZAK_INTEGREX_E1550"
#Filter child items to get the .EIA files
Get-ChildItem -Path $aidLibPathFolder -Include *.EIA -Recurse | ForEach-Object{
#Splitting the folder name because the first part of the name is the MAFN in the db(less the zeros). The second part after the dot(.) is the version number
$fullPath = $_.FullName
$baseName = $_.Name
$folderName = $_.Directory.Name
$splitFolderName = $folderName.Split('.')
$firstPart = $splitFolderName[0].TrimStart("0")
$version = "VER"+ " " + $splitFolderName[1]
$ver = $splitFolderName[1].TrimStart("0")
$versionNoZero = "VER"+ " " + $ver
#Filter the EIA files that have the specific line of text.
If(Get-Content -Path $fullPath | Select-String -Pattern $filterA,$filterB){
#Get first line of file.
$eiaContent= Get-Content -Path $fullPath -TotalCount 1
If($MafnSqlEt.MAFNET -eq $firstPart){
$partNoQuery = "SELECT [PartNo] FROM [NML_Sidney].[dbo].[vMADL_EngParts] Where MAFN=$firstPart"
$partNoSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $partNoQuery
$revQuery = "SELECT [MA_Rev] As Rev FROM [NML_Sidney].[dbo].[MADL_EngParts] Where MAFN=$firstPart"
$revSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $revQuery
[System.String]$partNo = $partNoSql.PartNo
$rev = "REV" + " "+ $revSql.Rev
$partNo.GetType()
}
If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches){
Write-Host "PART matches" -BackgroundColor Green -ForegroundColor Black
Write-Host `PartNumberMatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Green -ForegroundColor Black
}
Else{
Write-Host "PART does not match" -BackgroundColor Red -ForegroundColor Black
Send-MailMessage -From $emailFrom -To 'hargul.sidhu#nicholsonmfg.com' -Subject "ALERT! DATA DISCREPANCY - '$folderName'" -Body "Incorrect Part Number in '$baseName' Path -'$fullPath'. **DO NOT REPLY. THIS INBOX IS NOT MONITORED**" -SmtpServer 'XXXX
Write-Host `PartNumberMismatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Red -ForegroundColor Black
}

Powershell - Convert list to UI with Out-Gridview

I have a script that read from excel and let the user to choose a column. The issue is that the list is not readable and I want to show the user the option to choose the version with UI with Out-Gridview
One more thing, I need that the answer will be a number
Here is the script:
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelWorkBook = $ExcelObject.Workbooks.Open($SharePointSiteURL)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("VIP List")
$rowMax = $ExcelWorkSheet.UsedRange.Rows.Count
$colMax = $ExcelWorkSheet.UsedRange.Columns.Count
$columns = [ordered]#{}
for ($col = 1; $col -le $colMax; $col++) {
$name = $ExcelWorkSheet.Cells.Item(1, $col).Value() # assuming the first row has the headers
if ($name -ne $null){
$columns[$name] = $col}
}
$columns.GetEnumerator() | ForEach-Object {
# {0,2} means to write the index number from $_.Value right aligned for two digits
'{0,2}: {1}' -f $_.Value, $_.Name
}
do {
$answer = Read-Host "Please enter the number of the column you want to read from" #. Press Q to exit
# ask this question until the user enters a number or 'Q'
} until ($answer -eq 'Q' -or $answer -match '^\d{1,2}$')
switch ($answer) {
'Q' { break } # user wants to quit
{1..$columns.Count} {
# get the Name from the chosen value
$action = $columns.Keys | Where-Object {$columns["$_"] -eq $answer}
Write-Host "You chose to perform: '$action'" -ForegroundColor Cyan
<# run $action #>
}
}
It looks like this:
To let the user select the tool version using Out-GridView, you need to build an array of objects, like below:
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelWorkBook = $ExcelObject.Workbooks.Open($SharePointSiteURL)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("VIP List")
$rowMax = $ExcelWorkSheet.UsedRange.Rows.Count
$colMax = $ExcelWorkSheet.UsedRange.Columns.Count
# now, have the loop output objects that will be collected in variable $columns
$columns = for ($col = 1; $col -le $colMax; $col++) {
$name = $ExcelWorkSheet.Cells.Item(1, $col).Value() # assuming the first row has the headers
# if $name is not empty or whitespace only
if ($name -match '\S') {
[PsCustomObject]#{
Number = $col
Version = $name
}
}
}
# output to Out-GridView with -PassThru parameter so you can capture the selected item
$answer = ($columns | Out-GridView -Title 'Please select' -PassThru).Number
# if the user did not cancel
if ($answer) {
# get the Name from the chosen value
$action = $columns[$answer -1].Version
Write-Host "You chose to perform: '$action'" -ForegroundColor Cyan
<# run $action #>
}
Please do not forget to remove the used COM objects from memory when the code is done, otherwise they will linger on..
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkSheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkBook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelObject)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Powershell GUI buttons arranged perpendicularly

I wrote a PS function to be run with ISE launch, so I can call it and switch the Azure subscription with a click of a button, but the buttons in GUI are listed horizontally, how can I change it to be shown perpendicularly?
Here's my code:
using namespace System.Management.Automation.Host
$sub = get-azsubscription | where-object -property State -eq 'Enabled'
function Set-Sub {
$Title = "Powershell ISE"
$Question = "Select default subscription"
$0 = [ChoiceDescription]::new($sub.Name[0], $sub.Name[0])
$1 = [ChoiceDescription]::new($sub.Name[1], $sub.Name[1])
$2 = [ChoiceDescription]::new($sub.Name[2], $sub.Name[2])
$3 = [ChoiceDescription]::new($sub.Name[3], $sub.Name[3])
$4 = [ChoiceDescription]::new($sub.Name[4], $sub.Name[4])
$options = [ChoiceDescription[]]($0, $1, $2, $3, $4)
$result = $host.ui.PromptForChoice($Title, $Question, $options, 0)
switch ($result) {
0 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[0])
select-azsubscription -subscriptionname $sub.Name[0]
}
1 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[1])
select-azsubscription -subscriptionname $sub.Name[1]
}
2 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[2])
select-azsubscription -subscriptionname $sub.Name[2]
}
3 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[3])
select-azsubscription -subscriptionname $sub.Name[3]
}
4 {
Write-Host -BackgroundColor Green -ForegroundColor White ("Setting default subscription to "+$sub.Name[4])
select-azsubscription -subscriptionname $sub.Name[4]
}
}
}
$context = Get-AzContext
Write-Host -BackgroundColor Red -ForegroundColor White ("Currently used subscription is: "+$context.Name)
Write-Host -ForegroundColor White (" ")
and that's how GUI looks like:
If you are looking for a way to make it vertical choices, it is not possible at this moment, though you can do differently for prompting every single option so that end user can select it: code would look like below:
$t = "Location"
$msg = "What location?"
$CFT = New-Object System.Management.Automation.Host.ChoiceDescription "&A choice 1", "1"
[Environment]::NewLine
$CON = New-Object System.Management.Automation.Host.ChoiceDescription "&B choice 2", "2"
$ELP = New-Object System.Management.Automation.Host.ChoiceDescription "&C choice 3", "3"
$CFT1 = New-Object System.Management.Automation.Host.ChoiceDescription "&A1 choice 1", "4"
$CON1 = New-Object System.Management.Automation.Host.ChoiceDescription "&B1 choice 2", "5"
$ELP1 = New-Object System.Management.Automation.Host.ChoiceDescription "&C1 choice 3", "6"
[int]$defaultchoice = 2
$options = [System.Management.Automation.Host.ChoiceDescription[]]($CFT, $CON, $ELP,$CFT1, $CON1, $ELP1)
$result = $host.ui.PromptForChoice($t, $msg, $options[0], 0)
$result = $host.ui.PromptForChoice($t, $msg, $options[1], 0)
Main problem with this approach as it will present one menu at a time with different prompt box.
But for nicer PromptForChoice for the PowerShell Console Host , you can use it like below:
An alternative to the built-in PromptForChoice providing a consistent UI across different hosts
function Get-Choice {
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,Position=0)]
$Title,
[Parameter(Mandatory=$true,Position=1)]
[String[]]
$Options,
[Parameter(Position=2)]
$DefaultChoice = -1
)
if ($DefaultChoice -ne -1 -and ($DefaultChoice -gt $Options.Count -or $DefaultChoice -lt 1)){
Write-Warning "DefaultChoice needs to be a value between 1 and $($Options.Count) or -1 (for none)"
exit
}
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
$script:result = ""
$form = New-Object System.Windows.Forms.Form
$form.FormBorderStyle = [Windows.Forms.FormBorderStyle]::FixedDialog
$form.BackColor = [Drawing.Color]::White
$form.TopMost = $True
$form.Text = $Title
$form.ControlBox = $False
$form.StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen
#calculate width required based on longest option text and form title
$minFormWidth = 100
$formHeight = 44
$minButtonWidth = 70
$buttonHeight = 23
$buttonY = 12
$spacing = 10
$buttonWidth = [Windows.Forms.TextRenderer]::MeasureText((($Options | sort Length)[-1]),$form.Font).Width + 1
$buttonWidth = [Math]::Max($minButtonWidth, $buttonWidth)
$formWidth = [Windows.Forms.TextRenderer]::MeasureText($Title,$form.Font).Width
$spaceWidth = ($options.Count+1) * $spacing
$formWidth = ($formWidth, $minFormWidth, ($buttonWidth * $Options.Count + $spaceWidth) | Measure-Object -Maximum).Maximum
$form.ClientSize = New-Object System.Drawing.Size($formWidth,$formHeight)
$index = 0
#create the buttons dynamically based on the options
foreach ($option in $Options){
Set-Variable "button$index" -Value (New-Object System.Windows.Forms.Button)
$temp = Get-Variable "button$index" -ValueOnly
$temp.Size = New-Object System.Drawing.Size($buttonWidth,$buttonHeight)
$temp.UseVisualStyleBackColor = $True
$temp.Text = $option
$buttonX = ($index + 1) * $spacing + $index * $buttonWidth
$temp.Add_Click({
$script:result = $this.Text; $form.Close()
})
$temp.Location = New-Object System.Drawing.Point($buttonX,$buttonY)
$form.Controls.Add($temp)
$index++
}
$shownString = '$this.Activate();'
if ($DefaultChoice -ne -1){
$shownString += '(Get-Variable "button$($DefaultChoice-1)" -ValueOnly).Focus()'
}
$shownSB = [ScriptBlock]::Create($shownString)
$form.Add_Shown($shownSB)
[void]$form.ShowDialog()
$result
}
By this way, you have a better control of the form UI, you can put a blank textblock to make a new line and and in the loop you can create different button control.
Additional reference:
https://gist.github.com/DBremen/73d7999094e7ac342ad6#file-get-choice-ps1
Hope it helps.

PowerShell: retrieve number of applications in AppPool

How to retrieve the number of applications associated with a specific IIS AppPool via PowerShell command?
We can see the associated applications manually using:
Get-Item IIS:\AppPools\AppPoolName
However, if we manually want to select the Applications column, it is not possible. Also, the Applications column is not listed within | Get-Member *.
Why is the column not listed?
How to find the number of applications associated with a specific IIS AppPool using PowerShell?
The trick is: PowerShell established so-called "view definition files" which tell PowerShell how to format objects (e.g. whether the object is formatted as a a list or a table, which columns are displayed, etc.). Those files can be found at C:\Windows\System32\WindowsPowerShell\v1.0 and are all ending in .format.ps1xml.
To answer the original question: The file C:\Windows\System32\WindowsPowerShell\v1.0\Modules\WebAdministration\iisprovider.format.ps1xml contains the view definition for the AppPool type which defines a calculated column looking like this:
<TableColumnItem>
<ScriptBlock>
$pn = $_.Name
$sites = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path='/']/parent::*" machine/webroot/apphost -name name
$apps = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path!='/']" machine/webroot/apphost -name path
$arr = #()
if ($sites -ne $null) {$arr += $sites}
if ($apps -ne $null) {$arr += $apps}
if ($arr.Length -gt 0) {
$out = ""
foreach ($s in $arr) {$out += $s.Value + "`n"}
$out.Substring(0, $out.Length - 1)
}
</ScriptBlock>
</TableColumnItem>
This answers why the column itself is not a member of the AppPool type. The second question can be easily answered now extracting the necessary code from the "scriptlet" above:
$applicationsInAppPoolCount = #(Get-WebConfigurationProperty `"/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`'and #path!='/']"` "machine/webroot/apphost" -name path).Count
I dealt with this same issue for many hours until finally arriving at the solution. The answer from D.R. was very helpful but it was not working for me. After some tweaks, I came up with the code below which retrieves the number of applications in an app pool.
I noticed that this part of the code nd #path!='/' threw off the count.
$appPool = "REPLACE ME with a value from your app pool"
#(Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`']" "machine/webroot/apphost" -name path).Count
I ended up with the following Code (basically the same as above, but differently formatted)
$appPools = Get-ChildItem –Path IIS:\AppPools
foreach ($apppool in $apppools) {
$appoolName = $apppool.Name
[string] $NumberOfApplications = (Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool='$appoolName']" "machine/webroot/apphost" -name path).Count
Write-Output "AppPool name: $appoolName has $NumberOfApplications applications"
}
I recently came across this post searching for ways to get the active Application Pools. The information provided above was great, but I kept digging to see if there was another way get this information. I was able to find a way to do this through Get-IISSite, which I used the following:
Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
I tested this on a server that only had one website, but if there are multiple sites on the server, you could also add VirtualDirectories for the Select.
I also had a need to just get a unique list of the Application Pools being used, so I did the following:
$appPoolInfo = Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
$appPoolInfo | Select-Object -Unique ApplicationPoolName
This gives what you are looking in an array.
Import-Module WebAdministration;
Get-ChildItem IIS:\AppPools >> AppPoolDetails.txt;
$appPoolDetails = Get-Content .\AppPoolDetails.txt;
$w = ($appPoolDetails |Select-String 'State').ToString().IndexOf("State");
$w = $w -1;
$res1 = $appPoolDetails | Foreach {
$i=0;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "First Column---";
$res1.Trim();
$j = $w + 1;
$w = ($appPoolDetails |Select-String 'Applications').ToString().IndexOf("Applications");
$w = $w -$j;
$res2 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Second Column---";
$res2.Trim();
$lineLength=0
$appPoolDetails | Foreach {
if($lineLength -lt $_.TrimEnd().Length )
{
$lineLength = $_.TrimEnd().Length;
#Write-Host $lineLength;
}
}
$j = ($appPoolDetails | Select-String 'Applications').ToString().IndexOf("Applications");
$w = $lineLength;
$w = $w -$j;
#Write-Host $j $w;
$res3 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Third Column---";
$res3;

Resources