IIS 7.5 Application Pools / IIS Manager GUI column: Applications - powershell

When looking in the IIS 7.5 manager > application pools, The last column lists "Applications". This column shows the number of application pools / websites this appPool is associated with.
I am trying to figure out how to query this column / information using Powershell. The end goal here is to have a script that I could run that would tell me if any applicaiton pool is being used for more than 1 website or app.
I am unable to find how to query this information, when running:
get-itemproperty IIS:\AppPools\(AppPoolName) | format-list *
I dont see this property. Im not sure that this column is a property, if not, is there a best way to check if AppPools are being used for more than 1 website / applicaiton?

The Applications property is defined in the format file, its code reside in the iisprovider.format.ps1xml file (in the webadmin module folder).
<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>
You can take the code out and use it outside the format file, just assign $pn the apppool name you want to query. Here's a simplified version of the code:
$pn = 'pool1'
$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
$sites,$apps | foreach {$_.value}

I went with this:
Import-Module WebAdministration
function Get-WebAppPoolApplications($webAppPoolName) {
$result = #()
$webAppPool = Get-Item ( Join-Path 'IIS:\AppPools' $webAppPoolName )
if ( $webAppPool -ne $null ) {
$webSites = Get-ChildItem 'IIS:\Sites'
$webSites | % {
$webApplications = Get-ChildItem ( Join-Path 'IIS:\Sites' $_.Name ) |
where { $_.NodeType -eq 'application' }
$result += $webApplications |
where { $_.applicationPool -eq $webAppPoolName }
}
}
$result
}

Wish I would have seen your post earlier, this is what I eventually came up with:
$SiteApps = get-item IIS:\Sites* $arraySize = ($SiteApps.count -1)
$i = 0
$t = 0
for ($i=0; $i -le $arraySize; $i ++) # start at the beg of the array
{
for ($t=($i+1); $t -le $arraySize; $t++)
{
if ($siteApps[$i].applicationpool -eq $siteApps[$t].applicationpool)
{
$web1 = $siteApps[$i].name
$webappPool = $siteApps[$i].applicationpool
$web2 = $siteApps[$t].name $answer = $answer + "The website "$web1" is sharing the AppPool "webAppPool" with website "$web2". "
}
}
}

Related

Powershell - Exchange JSON output without needing to write to a file

EDIT: Added Setupconfigfiles.ps1
I'm a bit new to detailed scripting so please bear with me.
I have two Powershell scripts
Setupconfigfiles.ps1 generates JSON output to be fed to an API.
Script2 uses that JSON data to execute API commands.
Script 2 can call setupconfigfiles.ps1 as indicated below and use the output data.
.\SetupConfigFiles.ps1 -type $Type -outfile .\Templist.json
$servers = Get-Content -Raw -Path .\templist.json | ConvertFrom-Json
setupconfigfiles.ps1:
param (
# If this parameter is set, format the output as csv.
# If this parameter is not set, just return the output so that the calling program can use the info
[string]$outfile,
# this parameter can be 'production', 'development' or 'all'
[string]$type
)
enum MachineTypes {
production = 1
development = 2
all = 3
}
$Servers = Get-ADObject -Filter 'ObjectClass -eq "computer"' -SearchBase 'Obfuscated DSN' | Select-Object Name
$output = #()
$count = 0
# Set this to [MachineTypes]::production or [MachineTypes]::development or [MachineTypes]::all
if ($type -eq "all") {
$server_types = [MachineTypes]::all
}
ElseIf ($type -eq "production") {
$server_types = [MachineTypes]::production
}
else {
$server_types = [MachineTypes]::development
}
ForEach ($Server in $Servers)
{
$count = $count + 1
$this_server = #{}
$this_server.hostname = $Server.Name
$this_server.id = $count
$this_server."site code" = $this_server.hostname.substring(1,3)
$this_server."location code" = $this_server.hostname.substring(4,2)
if ($this_server.hostname.substring(7,1) -eq "P") {
$this_server.environment = "Production"
}
ElseIf ($this_server.hostname.substring(7,1) -eq "D") {
$this_server.environment = "Development"
}
Else {
$this_server.environment = "Unknown"
}
if (($server_types -eq [MachineTypes]::production ) -and ($this_server.environment -eq "Production")) {
$output += $this_server
}
ElseIf (($server_types -eq [MachineTypes]::development ) -and ($this_server.environment -eq "Development")) {
$output += $this_server
}
Else {
if ($server_types -eq [MachineTypes]::all ) {
$output += $this_server
}
}
}
if ($outfile -eq "")
{
ConvertTo-Json $output
}
else {
ConvertTo-Json $output | Out-File $outfile
}
How can I do it without needing to write to the Templist.json file?
I've called this many different ways. The one I thought would work is .\SetupConfigFiles.ps1 $servers
Y'all are great. #Zett42 pointed me in a direction and #Mathias rounded it out.
The solution was to change:
"ConvertTo-Json $output" to "Write-Output $output"
Then it's handled in the calling script.
thanks!

Powershell - Make a menu out of text file

In my adventure trying to learn Powershell, I am working on an extension on a script I have made. The idea is to make script there by adding ".iso" files into a folder. It will use that content in a menu so that I later can use it to select an iso file for a WM in Hyper-V
This is my version of how it will get the content in the first place
Get-ChildItem -Path C:\iso/*.iso -Name > C:\iso/nummer-temp.txt
Add-Content -Path C:\iso/nummer.txt ""
Get-Content -Path C:\iso/nummer-temp.txt | Add-Content -Path C:\iso/nummer.txt
When this code is run it will send an output like what i want. But my question is how do I use this output in a menu?
This is the best practice way to do so in powershell :
#lets say your .txt files gets this list after running get-content
$my_isos = $('win7.iso','win8.iso','win10.iso')
$user_choice = $my_isos | Out-GridView -Title 'Select the ISO File you want' -PassThru
#waiting till you choose the item you want from the grid view
Write-Host "$user_choice is going to be the VM"
I wouldn't try to make it with System.windows.forms utilities as i mentioned in my comment, unless you want to present the form more "good looking".
If you don't want to go for a graphical menu, but rather a console menu, you could use this function below:
function Show-Menu {
Param(
[Parameter(Position=0, Mandatory=$True)]
[string[]]$MenuItems,
[string] $Title
)
$header = $null
if (![string]::IsNullOrWhiteSpace($Title)) {
$len = [math]::Max(($MenuItems | Measure-Object -Maximum -Property Length).Maximum, $Title.Length)
$header = '{0}{1}{2}' -f $Title, [Environment]::NewLine, ('-' * $len)
}
# possible choices: digits 1 to 9, characters A to Z
$choices = (49..57) + (65..90) | ForEach-Object { [char]$_ }
$i = 0
$items = ($MenuItems | ForEach-Object { '{0} {1}' -f $choices[$i++], $_ }) -join [Environment]::NewLine
# display the menu and return the chosen option
while ($true) {
cls
if ($header) { Write-Host $header -ForegroundColor Yellow }
Write-Host $items
Write-Host
$answer = (Read-Host -Prompt 'Please make your choice').ToUpper()
$index = $choices.IndexOf($answer[0])
if ($index -ge 0 -and $index -lt $MenuItems.Count) {
return $MenuItems[$index]
}
else {
Write-Warning "Invalid choice.. Please try again."
Start-Sleep -Seconds 2
}
}
}
Having that in place, you call it like:
# get a list if iso files (file names for the menu and full path names for later handling)
$isoFiles = Get-ChildItem -Path 'D:\IsoFiles' -Filter '*.iso' -File | Select-Object Name, FullName
$selected = Show-Menu -MenuItems $isoFiles.Name -Title 'Please select the ISO file to use'
# get the full path name for the chosen file from the $isoFiles array
$isoToUse = ($isoFiles | Where-Object { $_.Name -eq $selected }).FullName
Write-Host "`r`nYou have selected file '$isoToUse'"
Example:
Please select the ISO file to use
---------------------------------
1 Win10.iso
2 Win7.iso
3 Win8.iso
Please make your choice: 3
You have selected file 'D:\IsoFiles\Win8.iso'

Get local group members: version agnostic

I found this thread that offers two basic approaches to getting local group members.
This works for me in all versions of powershell, but depends on using the old NET command line utility.
function Get-LocalGroupMembers() {
param ([string]$groupName = $(throw "Need a name") )
$lines = net localgroup $groupName
$found = $false
for ($i = 0; $i -lt $lines.Length; $i++ ) {
if ( $found ) {
if ( -not $lines[$i].StartsWith("The command completed")) {
$lines[$i]
}
} elseif ( $lines[$i] -match "^----" ) {
$found = $true;
}
}
}
This works for me in PowerShell 2.0, but barfs in PS5.0 with Error while invoking GetType. Could not find member.
It only barfs on some groups, including Administrators, which has me thinking it's some sort of security feature, like requiring elevated privileges to REALLY have admin rights in a script.
Function Get-LocalGroupMembers
{
Param(
[string]
$server = "."
)
Try
{
$computer = [ADSI]"WinNT://$( $Server ),computer"
$computer.psbase.children |
where {
$_.psbase.schemaClassName -eq 'group'
} |
ForEach {
$GroupName = $_.Name.ToString()
$group =[ADSI]$_.psbase.Path
$group.psbase.Invoke("Members") |
foreach {
$memberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "WinNT:", ""
$props = #{
"LocalGroup" = $GroupName
"MemberName" = $memberName
}
$obj = New-Object -TypeName psobject -Property $props
Write-Output $obj
} # foreach members
} # foreach group
}
Catch
{
Throw
}
}
I think I read somewhere that PS5.1 has a native CMDlet finally. But I can't depend on a particular version of PS, I need to support everything from PS2.0 in Win7 up. That said, is there a single version agnostic solution that doesn't depend on a command line utility kludge? Or do I need to have code that uses the old hack or the new CMDlet depending on PS version I am running on?
So, I am having good luck with this solution.
$hostname = (Get-WmiObject -computerName:'.' -class:Win32_ComputerSystem).name
$wmiQuery = Get-WmiObject -computerName:'.' -query:"SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$Hostname',Name='$group'`""
if ($wmiQuery -ne $null) {
:searchLoop foreach ($item in $wmiQuery) {
if (((($item.PartComponent -split "\,")[1] -split "=")[1]).trim('"') -eq $user) {
$localGroup = $true
break :searchLoop
}
}
}
I'm not sure yet if I like that overly complex IF vs some variables, but the functionality is there and working across all versions of PS without resorting to command line kludges, which was the goal.
Note that this just returns true if the user is a member of the group, which is all I need. The other code I posted would provide a list of members, which is the basis of doing a check, and I just hadn't modified it to show the real end goal, since the problem manifested without that.
Instead this :
$memberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "WinNT:", ""
You can try this :
$memberName = ([ADSI]$_).InvokeGet("Name")

powershell script that pulls WMIC data from remote workstations

I am looking for a Powershell script that queries remote workstations for installed Windows updates looking for specific HotFixes (6 of them) and then reports back which ones (if any) are installed.
Query would get the remote host's name from a text file.
I found this script on Microsoft's site and tried to alter it, but I have no where near the skills needed to it.
Function Get-MSHotfix
{
$outputs = Invoke-Expression "wmic qfe list"
$outputs = $outputs[1..($outputs.length)]
foreach ($output in $Outputs) {
if ($output) {
$output = $output -replace 'y U','y-U'
$output = $output -replace 'NT A','NT-A'
$output = $output -replace '\s+',' '
$parts = $output -split ' '
if ($parts[5] -like "*/*/*") {
$Dateis = [datetime]::ParseExact($parts[5], '%M/%d/yyyy',[Globalization.cultureinfo]::GetCultureInfo("en-US").DateTimeFormat)
} elseif (($parts[5] -eq $null) -or ($parts[5] -eq ''))
{
$Dateis = [datetime]1700
}
else {
$Dateis = get-date([DateTime][Convert]::ToInt64("$parts[5]", 16))-Format '%M/%d/yyyy'
}
New-Object -Type PSObject -Property #{
KBArticle = [string]$parts[0]
Computername = [string]$parts[1]
Description = [string]$parts[2]
FixComments = [string]$parts[6]
HotFixID = [string]$parts[3]
InstalledOn = Get-Date($Dateis)-format "dddd d MMMM yyyy"
InstalledBy = [string]$parts[4]
InstallDate = [string]$parts[7]
Name = [string]$parts[8]
ServicePackInEffect = [string]$parts[9]
Status = [string]$parts[10]
}
}
}
}
Here's something I wrote up for a very similar situation:
ForEach($Server in $ServerList){
$QFE=Get-WmiObject Win32_QuickFixEngineering -ComputerName $Server
$IDRX = 3188732,3188743,3192392 -join '|'
$QFE|?{$_.HotFixId -match $IDRX}
}
Mind you, I was looking for QFEs 3188732, 3188743, and 3192392, but you could easily modify that line for the ones you are looking for.

Get IIS log location via powershell?

I'm writing a script that I'd like to be able to easily move between IIS servers to analyze logs, but these servers store the logs in different places. Some on C:/ some on D:/ some in W3SVC1, some in W3SVC3. I'd like to be able to have powershell look this information up itself rather than having to manually edit this on each server. (Yeah, I'm a lazy sysadmin. #automateallthethings.)
Is this information available to PowerShell if I maybe pass the domain to it or something?
I found this to work for me since I want to know all of the sites log directory.
Import-Module WebAdministration
foreach($WebSite in $(get-website))
{
$logFile="$($Website.logFile.directory)\w3svc$($website.id)".replace("%SystemDrive%",$env:SystemDrive)
Write-host "$($WebSite.name) [$logfile]"
}
Import-Module WebAdministration
$sitename = "mysite.com"
$site = Get-Item IIS:\Sites\$sitename
$id = $site.id
$logdir = $site.logfile.directory + "\w3svc" + $id
Thanks for Chris Harris for putting the website ID idea in my head. I was able to search around better after that and it led me to the WebAdministration module and examples of its use.
Nice... I updated your script a little bit to Ask IIS for the log file location.
param($website = 'yourSite')
Import-Module WebAdministration
$site = Get-Item IIS:\Sites\$website
$id = $site.id
$logdir = $site.logfile.directory + "\w3svc" + $id
$time = (Get-Date -Format "HH:mm:ss"(Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "$logdir\u_ex$((get-date).ToString("yyMMdd")).log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select #{n="DateTime"; e={Get-Date ("$($_.date) $($_.time)")}},csuristem,scstatus | ? { $_.DateTime -ge $time }