Find IIS Application Pool not linked to an application via Powershell - powershell

I would like to write a powershell script that will find all Application Pool on a server that are not link to an application and then delete the application pool that are not used.
One way I can think of doing this is to retrieve all the Application pool, retrieve all the IIS applications and then cross check the two list. Is there a better way of doing this?

For those interested, here's the code I've written:
$UsedAppPoolList = get-item "IIS:\Sites\*" | foreach { $_.applicationPool; Get-WebApplication -Site $_.Name | foreach { $_.applicationPool } }
$AppPoolExistList = get-item 'IIS:\AppPools\*' | foreach { $_.Name }
foreach ( $AppPool in $AppPoolExistList ){
if ($UsedAppPoolList -notcontains $AppPool){
Remove-WebAppPool $AppPool
write-host "Delete Application Pool $AppPool"
}
}

Related

Pulling dbname from web.config using powershell

Found one part of my answer here, Get dbname from multiple web.config files with powershell
But need it to recurse through the IIS Sites and all the Web Apps inside those 'Sites'. I have multiple IIS 'Sites' with multiple Web Apps in each 'Site' need to check each ones web.config file and pull the db used. Cant figure out how to recurse the code in the link above.
#Code from link above.
Import-Module WebAdministration
Get-WebApplication |
ForEach-Object {
$webConfigFile = [xml](Get-Content "$($_.PhysicalPath)\Web.config")
Write-Host "Web Application: $($_.path)"
foreach($connString in $webConfigFile.configuration.connectionStrings.add)
{
Write-Host "Connection String $($connString.name): $($connString.connectionString)"
$dbRegex = "((Initial\sCatalog)|((Database)))\s*=(?<ic>[a-z\s0-9]+?);"
$found = $connString.connectionString -match $dbRegex
if ($found)
{
Write-Host "Database: $($Matches["ic"])"
}
}
Write-Host " "
}
Would like this to output the database name of each web.config file for each IIS Site and for each Web App in the IIS Site. Currently this only looks at the first web app in a IIS Site, and doesnt look at any others in the IIS Site, also doenst look to see if the IIS Site's web.config has a connection string to a DB.
Here is the code for you, from your reference I have changed looping mechanism not the actual logic to get the db name.
Import-Module WebAdministration
Get-Website | % {
$AppList = Get-WebApplication -Site $_.Name
foreach ( $app in $AppList )
{
$app.PhysicalPath
$webConfigFile = [xml](Get-Content "$($app.PhysicalPath)\Web.config")
# NEED TO CHECK WEATHER THE $webConfigFile FILE EXIST OR NOT
Write-Host "Web Application: $($_.path)"
foreach($connString in $webConfigFile.configuration.connectionStrings.add)
{
Write-Host "Connection String $($connString.name): $($connString.connectionString)"
$dbRegex = "((Initial\sCatalog)|((Database)))\s*=(?<ic>[a-z\s0-9]+?);"
$found = $connString.connectionString -match $dbRegex
if ($found)
{
Write-Host "Database: $($Matches["ic"])"
}
}
} # END OF INNER FOR EACH LOOP
} # END OF OUTER FOR EACH LOOP

Output of Get-content as variable?

I am attempting to run a foreach loop on a get-content and convertfrom-json cmd. Now im aware this potentially has issues being multiple value results in the variable, im wondering how i can continue to pass this info to the rest of the script.
$testconv = Get-device * |select ID
$testid = $testconv.id
$conv = foreach ($id in $testid)
{
get-content "\\HDC-PRTG-03\System Information Database\Services\Device$id.Services" | Convertfrom-json
}
$rpccheck =$conv.message
$snmpcheck = $conv.message
$svcname = $conv.data.displayname
$svcstate=$conv.data.properties.state
if($RPCon = $rpccheck |select-string -pattern RPC -AllMatches){
write-host RPC Not enabled
}else{
write-host No RPC Enabled - Moving to Services List
Now when i run that with out the $conv= making it a variable it returns
kind : Services
recievetime : 29-01-2018 14:43:32
error : 106
Message : SNMP Channels Not Available.
Which is what i expect. However when i define it a variable with $conv= it just starts to say it cannot find the file paths which i find an odd error to throw but hey ho.
Do any of you smart guys have any pointers for how i can keep these fromjson objects in memory so i can continue to run foreach loops against them. The ultiumate function of this script is to query a local .services file for what services are running on the device and then create sensors to monitor them within our PRTG installation. Therefore i need to be able to ref the deviceID and apply things to it.
I suspect i may be using too many foreach loops in the whole script but frankly i am 100% out of my depth
any guidance hugely hugely appreciated
Sam
If i understand correctly you should have json files for all device ID's. If a file with the name of a particular device is missing you will get the 'File not found' error.
As for the code, you can try this:
$testconv = Get-Device * | select ID
$testid = $testconv.id
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
foreach ($id in $testid) {
try {
$conv = Get-Content -Path "\\HDC-PRTG-03\System Information Database\Services\Device$id.Services" -Raw | ConvertFrom-Json
$rpccheck = $conv.message # These look the same to me...
$snmpcheck = $conv.message # These look the same to me...
$svcname = $conv.data.displayname
$svcstate = $conv.data.properties.state
$Matches = ($rpccheck | Select-String -Pattern "RPC*" -AllMatches)
if ($Matches.Matches.Count) {
Write-Host "RPC Not enabled"
}
else {
Write-Host "No RPC Enabled - Moving to Services List "
}
}
catch {
Write-Warning $_.Exception.Message
}
}
$ErrorActionPreference = $oldErrorAction
Instead of the try{}..catch{} you could also first test if a file with that name is present using Test-Path directly before doing the Get-Content.

net files /close does not work as expected

We have many self-written applications which are executed in a network location. Whenever a developer wants to publish a new version of this application, I have to close all opened files on the server where the app is located, by going to:
computer management > Shared Folders > Opened Files
then choose all the files of the application - right click and close.
I want the developers to do that by themselves via PowerShell, so I wrote a Unlock-Files function. This part of the function should close the files, with the net files $id /close call:
$result = Invoke-Command -ComputerName $server -Credential $cred -ArgumentList $workpath, $server {
param($workpath,$server)
$list = New-Object System.Collections.ArrayList
$ErrorActionPreference = "SilentlyContinue"
$adsi = [adsi]"WinNT://./LanmanServer"
$resources = $adsi.psbase.Invoke("resources") | % {
[PSCustomObject] #{
ID = $_.gettype().invokeMember("Name","GetProperty",$null,$_,$null)
Path = $_.gettype().invokeMember("Path","GetProperty",$null,$_,$null)
OpenedBy = $_.gettype().invokeMember("User","GetProperty",$null,$_,$null)
LockCount = $_.gettype().invokeMember("LockCount","GetProperty",$null,$_,$null)
}
}
$resources | ? { $_.Path -like $workpath } | tee -Variable Count | % {
$id = $_.ID
net files $id /close > $null
if ($LASTEXITCODE -eq 0) { $list.add("File successfully unlocked $($_.path)") > $null }
else { $list.add("File not unlocked (maybe still open somewhere): $($_.path)") > $null }
}
if (!( ($count.count) -ge 1 )) { $list.add("No Files to unlock found in $workpath on $server") > $null }
$list
}
I expect net files /close to behave the same way as the manual way I do directly on the server described above, but it doesn't behave like this at all. It sometimes closes the file, sometimes not. But it never closes all the necessary files, so the developer can not publish his application. also, net files almost never finishes with LastExitCode 0, but I can't get my head around why.
Is there a way to force net files to really close all the files?
why would net files behave different than closing the files manually?
Is there a native PowerShell way to do this?
Is there a list of last exit codes, so I can wrap my head around this issue further?
Thank you!

Missing AD module and can't get it, need something similar or something to simulate it

So I'm trying to output a complete KB list for all computers on a server (which works on one computer) but it doesn't recognize Get-ADcomputer as a cmdlet. When checking various sources, it appears that the AD module isn't included. As I'm doing this on a work computer/server I'm hesitant to download anything or anything of that nature.
Is there any way I can achieve the following without using the AD module or someway I might be missing how to import the module (if it exists, which I don't think it does on this system)?
# 1. Define credentials
$cred = Get-Credential
# 2. Define a scriptblock
$sb = {
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object -Process {
$Title = $null
if ($_.Title -match "\(KB\d{6,7}\)") {
# Split returns an array of strings
$Title = ($_.Title -split '.*\((?<KB>KB\d{6,7})\)')[1]
} else {
$Title = $_.Title
}
$Result = $null
switch ($_.ResultCode) {
0 { $Result = 'NotStarted'}
1 { $Result = 'InProgress' }
2 { $Result = 'Succeeded' }
3 { $Result = 'SucceededWithErrors' }
4 { $Result = 'Failed' }
5 { $Result = 'Aborted' }
default { $Result = $_ }
}
New-Object -TypeName PSObject -Property #{
InstalledOn = Get-Date -Date $_.Date;
Title = $Title;
Name = $_.Title;
Status = $Result
}
} | Sort-Object -Descending:$false -Property InstalledOn | Where {
$_.Title -notmatch "^Definition\sUpdate"
}
}
#Get all servers in your AD (if less than 10000)
Get-ADComputer -ResultPageSize 10000 -SearchScope Subtree -Filter {
(OperatingSystem -like "Windows*Server*")
} | ForEach-Object {
# Get the computername from the AD object
$computer = $_.Name
# Create a hash table for splatting
$HT = #{
ComputerName = $computer ;
ScriptBlock = $sb ;
Credential = $cred;
ErrorAction = "Stop";
}
# Execute the code on remote computers
try {
Invoke-Command #HT
} catch {
Write-Warning -Message "Failed to execute on $computer because $($_.Exception.Message)"
}
} | Format-Table PSComputerName,Title,Status,InstalledOn,Name -AutoSize
You've got 3 options:
First is to just install the RSAT feature for AD which will include the AD module. This is probably the best option unless there is something specific preventing it. If you're running your script from a client operating systems you need to install the RSAT first, though.
Option 2 (which should only be used if adding the Windows feature is somehow an issue) is to download and use the Quest AD tools, which give very similar functionality, but it looks like Dell is doing their best to hide these now so that may be difficult to locate...
Option 3 is to use the .NET ADSI classes to access AD directly, which will work without any additional downloads on any system capable of running PowerShell. If you'd like to go this route you should check out the documentation for the interface Here and for the System.DirectoryServices namespace Here.
Edit
Just noticed the last part of your question, what do you mean by "a complete KB list"? Not just Windows updates or things updated manually or whatever? What else would be in a list of Windows updates that was not a Windows update?
You have not mentioned the OSes you are using but in general if you have a server 2008 R2 or above, all you have to do it activate the RSAT feature AD PowerShell Module and you will have the cmdlet you are looking for.
On a client machine, you 'have to' install RSAT, and then activate the features. You can take a look at the technet article for more info: https://technet.microsoft.com/en-us/library/ee449483(v=ws.10).aspx
If you don't want to use that option, then you will have to use .NET ADSI classes. There are tons of examples on how to do this, it basically boils down to a couple of lines really. Technet has examples on this as well: https://technet.microsoft.com/en-us/library/ff730967.aspx

Remote Powershell return value

I want to run a powershell with WinRM on serveral servers. I am using Invoke-Command with a script block.
I am reading from IIS and want to return a AppPool-Object, but I cannot access its properties - always empty.
#--imagine this code in a foreach block
$result = Invoke-Command -ComputerName $line.Servername -ScriptBlock {
Import-Module WebAdministration
$remotePoolName = Get-Item "IIS:\Sites\LeSite" #| Select-Object applictionPool
$pool = dir IIS:\AppPools | Where-Object { $_.Name -eq $remotePoolName.applicationPool }
return $pool
}
write-host $result.managedRuntimeVersion <- empty
Do I have to access it on the remote machine and return it as string ?
The problem here is, that you are referring to a property including get and set functions.
Using these functions outside of your server area results in nothing since the object is no longer in your server environment.
Using these functions inside your script block will work, because you use them on your server directly.
Greetz