Combining Powershell Commands in a Foreach Loop - powershell

I'm pretty new to powershell, and am wondering why this isn't working. Basically, I have a foreach loop that checks if IIS is running and reports the OS version if so and writes this out to a file. However, it's not reporting correctly. It seems like it gets the values for the first item (IIS state and OS) and then populates it through the other items in my loop.
#Get List of Servers to Scan
$servers = (Get-Content e:\servers.txt)
#Determine if IIS is Running
foreach($server in $servers){
$iis = get-wmiobject Win32_Service -ComputerName $server -Filter "name='IISADMIN'";
if($iis.State -eq "Running")
{$OSVer= [environment]::OSVersion.Version}
Write-Host "$server,$IISRun,$OSVer" | Out-File -FilePath "E:\results.txt"
}
The Results:
Server1,true,5.2.3790.131072 <---- Correct
Server2,true,5.2.3790.131072 <---- Correct
Server3,true,5.2.3790.131072 <---- Wrong OS reported (windows 2008)
Server4,true,5.2.3790.131072 <---- Wrong OS reported (windows 2008)
Server5,true,5.2.3790.131072 <---- IIS isn't installed here at all
Server6,true,5.2.3790.131072 <---- IIS isn't installed here at all
Thanks for any advice...

[environment]::OSVersion.Version gets you your local OS version. Use Get-WmiObject Win32_OperatingSystem -ComputerName $server instead to get target computer's OS information, e.g.:
foreach ($server in $servers)
{
$iis = Get-WmiObject Win32_Service -ComputerName $server -Filter "name='IISADMIN'"
$os = Get-WmiObject Win32_OperatingSystem -ComputerName $server
$iisRunning = $iis.State -eq "Running"
$osVersion = $os.Version
Write-Host "$server, $iisRunning, $osVersion"
}

Related

Powershell and TPM how to manage bitlocker?

I am trying to script a powershell function manage-bde.exe (bitlocker) to add a key protector to systems without TPM. For some reason GPO is not working. I have not had any luck getting powershell to add the protector remotely. I can log on to the endpoint and use the built in wizard to encrypt and save the key to our repository but for some reason remote automated scripting eludes me. My question is really more of guidance. Can powershell only be used, to remotely manage systems with TPM? I have bitlocker enabled and encrypted on systems without but I have had to do it manually.
Start-Transcript -Path ".\bitlockertranscript.txt" -Force
foreach ($Computer in $List) {
if (test-Connection -ComputerName $Computer -Count 1 -Quiet ) {
Get-ADComputer -Identity $Computer -Property * | Select Name,OperatingSystem
Get-WmiObject -class Win32_Tpm -namespace root\CIMV2\Security\MicrosoftTpm -computername $Computer | fl IsActivated_InitialValue, IsEnabled_InitialValue, IsOwned_InitialValue
$BitLocker = Get-WmiObject -ComputerName $Computer -Namespace Root\cimv2\Security\MicrosoftVolumeEncryption -Class Win32_EncryptableVolume
$id = $BitLocker.GetKeyProtectors(3).volumekeyprotectorid | Select -First 1
manage-bde.exe -cn $Computer -protectors -adbackup c:
manage-bde.exe -on C: -cn $Computer
Invoke-GPUpdate -Target $computer
} else
{"No Connection to $Computer"
}
}
Stop-Transcript

how to output the FQDN of a CNAME?

Suppose I have a server
$Server = "server1"
how do i write output the FQDN of this server?
I tried the following
write-host "$([System.Net.Dns]::GetHostByName($Server).HostName)"
but it doesnt output anything
i tried this
(resolve-dnsname $server).name
it just outputs back server1, not the actual name
I got this to work >>
$server = "test server"
write-output ([System.Net.Dns]::GetHostByName(($server))).Hostname
used this website for assistance
https://www.powershellbros.com/powershell-tip-of-the-week-get-fqdn/
one document against write-host ..
https://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
You could use WMI for this:
$computer = Get-WmiObject Win32_ComputerSystem -ComputerName "Server1"
'{0}.{1}' -f $computer.DNSHostName, $computer.Domain
or the same using CIM:
$computer = Get-CimInstance Win32_ComputerSystem -ComputerName "Server1"
'{0}.{1}' -f $computer.DNSHostName, $computer.Domain

PowerShell Script to Uninstall multiple programs from a list of servers

I have written a basic PS script in order to uninstall any programs defined in a text file ($appname) on all servers defined in a text file ($servers).
Running this command manually without the variables it works fine, however running the script via Jenkins or from PS command line it just hangs so I can't even debug, anyone have any ideas?
[array]$servers= Get-Content "D:\Jenkins\BuildUtilities\Citrix\CitrixServerList.txt"
[array]$appname= Get-Content "D:\Jenkins\BuildUtilities\Citrix\ProgramsList.txt"
ForEach($server in $servers) {
$prod=gwmi -ComputerName $server Win32_product | ?{$_.name -eq $appname}
$prod.uninstall()
}
To clarify: By running manually I mean running the following:
gwmi -ComputerName CTX-12 Win32_product | ?{_.Name -eq "Microsoft Word"}
Microsoft Word is an example.
It should have been easy to follow Matts hints
[array]$servers= Get-Content "D:\Jenkins\BuildUtilities\Citrix\CitrixServerList.txt"
[array]$appname= Get-Content "D:\Jenkins\BuildUtilities\Citrix\ProgramsList.txt"
ForEach($server in $servers) {
$prod=gwmi -ComputerName $server Win32_product | ?{ $appname -contains $_.name}
$prod.uninstall()
}
You are doing an comparison of two arrays, what you want to do is check for every instance of matching applications in the prod array you fetch from the server, so using the pipeline how you have it is not ideal.
[array]$servers= Get-Content "D:\Jenkins\BuildUtilities\Citrix\CitrixServerList.txt"
[array]$appname= Get-Content "D:\Jenkins\BuildUtilities\Citrix\ProgramsList.txt"
$DebugPreference= 'Continue'
foreach($server in $servers)
{
Write-Debug "Getting all installed applications on server $server"
$prod= Invoke-Command -ComputerName $server -Scriptblock {gwmi Win32_product}
Write-Debug "Installed applications collected, there are $($prod.Count) items in the array."
foreach($p in $prod)
{
Write-Debug "Searching apps array for the name $($p.Name)."
if($appname -contains $p.Name)
{
Write-Verbose -Message "$($p.Name) found on server $server, uninstalling."
$p.uninstall()
}
else
{
Write-Verbose -Message "$($p.Name) was not found on server $server."
}
}
}
EDIT: Since you brought up the speed issue, using Win32_product is very slow. So the faster method would be to get a list of installed applications from the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall (details how to do that are here). Build a new array of applications found that need uninstalled and then call on Win32_product. Just know that this only speeds up one portion of your script, and if any application is found, the slow down will still occur. The major speed gains will come from when you find a server that doesn't have any applications to uninstall.
EDIT2: Did some experimenting, using a simple Invoke-Command greatly speeds up the Get-WMIObject command. I did it on Services, using the command alone took 9 seconds, using the Invoke-Command took 1 second. Test:
$firstStart = Get-Date
$service1 = Get-WmiObject win32_service -ComputerName $ServerName
$firstEnd = Get-Date
$firstTime = New-TimeSpan -Start $firstStart -End $firstEnd
Write-Host "The first collection completed in $($firstTime.TotalSeconds) seconds." -ForegroundColor Green
$secondStart = Get-Date
$service2 = Invoke-Command -ComputerName $ServerName -ScriptBlock {Get-WmiObject win32_service}
$secondEnd = Get-Date
$secondTime = New-TimeSpan -Start $secondStart -End $secondEnd
Write-Host "The second collection completed in $($secondTime.TotalSeconds) seconds." -ForegroundColor Green

PowerShell remote PC shutdown, single PC

OU=_ is a private company name. I know it's restart, this is only for testing before it goes into the real hutdown process.
function Get-LastBootUpTime {
param (
$ComputerName
)
$OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName
[Management.ManagementDateTimeConverter]::ToDateTime($OperatingSystem.LastBootUpTime)
}
$Days = -0
$ShutdownDate = (Get-Date).adddays($days)
$ComputerList = Get-ADComputer -SearchBase 'OU=TEST-OU,OU=_,DC=_,DC=_' ` -Filter '*' | Select -EXP Name
$ComputerList | foreach {
$Bootup = Get-LastBootUpTime -ComputerName $_
Write-Host "$_ last booted: $Bootup"
if ($ShutdownDate -gt $Bootup) {
Write-Host "Rebooting Computer: $_" -ForegroundColor Red
restart-Computer $Computer -Force
}
else {
Write-Host "No need to reboot: $_" -ForegroundColor Green
}
}
I'm trying to shutdown all of the PCs in my company that run longer than 2 days. The script is kind of done, but it shows an error when it comes to the point:
restart-Computer $Computer -Force
If I type instead of $Computer, $ComputerList the script shuts down every PC in that OU, even if they didnt run longer than 2 days.
So it only takes one PC to run longer than 2 days to shut down the entire company, and that's not what I want.
How can I tell the script to only turn the PCs off, when they have already run more than 2 days?
Your $Computer is not defined. You should use:
Restart-Computer $_ -Force
But the better approach would be to collect all of the computers that should restart in a variable and then restart them altogether. Would work much faster:
$toBeRestarted = $ComputerList | Where-Object { $ShutdownDate -gt (Get-LastBootUpTime -ComputerName $_) }
Restart-Computer $toBeRestarted -Force
You may add some more logging around if you like

Getting back TIME information by Powershell

I have the below code that looks at OS system and based on the build type it returns the NTP server they point to.
However, it works fine on a match of 7601 (Windows Server 2008 R2) - but, for Windows Server 2003 servers I always get an error. For Windows Server 2003 servers you need to read a registry key.
If I did the same query on the registry key locally on a server it works fine...although it reports back other stuff aswell as the registry key..
here is the error:
NTPSource Server
--------- ------
The following error occurred: The procedure number is ou... SERV1
Here is the code:
$servers = #('SERV1','SERV2')
$version = Get-WmiObject Win32_OperatingSystem -computer $servers | select buildnumber
foreach ($server in $servers){
if ($version -match '7601')
{
$ntps = w32tm /query /computer:$server /source
new-object psobject -property #{
Server = $Server
NTPSource = $ntps
}
}
elseif($version -match '3790')
{
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters' -Name Type
}
}
Personally I use this to retrieve NTP server (Windows Server 2000/2003/2008/2008r2):
$Servers = (gc Computers.txt) # list of servers name
$pw = Get-Credential
$HKLM = 2147483650
foreach( $Server in $Servers )
{
$reg = GWMI -list -namespace root\default -computername $server -Credential $pw |
where-object { $_.name -eq "StdRegProv" }
$key = $reg.GetStringValue($HKLM,"SYSTEM\CurrentControlSet\Services\W32Time\Parameters","NtpServer")
write-host "$server `t$($key.svalue)"
}