I can't figure out why Install-WindowsFeature is timing out - powershell

Title. I can't seem to get this to work and I'm miffed as to why. Windows 2012R2 Data Center. The below source is from the install.wim file directly from an install CD. It errors out with the following error:
The server could not update the provided feature files in the time allowed
Below is the code, note it shows which features I'm trying to install. In theory this shouldn't be reaching out to windows update (I think). OTOH, I'm a developer and not a windows admin so I honestly don't know what the underlying behavior is supposed to be. It's the reason why I'm here, at this point I've tried all I can think of.
Under what circumstances does this error occur and how do I go about solving it?
$features = Get-Features
$source = "wim:C:\WindowsCore\install.wim:4"
Install-WindowsFeature -Name $features -IncludeManagementTools -Source $source -WarningAction SilentlyContinue | Out-Null
function Get-Features {
return [string[]] #(
'FileAndStorage-Services'
'Storage-Services'
'Web-Server'
'Web-WebServer'
'Web-Common-Http'
'Web-Default-Doc'
'Web-Dir-Browsing'
'Web-Http-Errors'
'Web-Static-Content'
'Web-Http-Redirect'
'Web-Health'
'Web-Http-Logging'
'Web-Log-Libraries'
'Web-ODBC-Logging'
'Web-Request-Monitor'
'Web-Http-Tracing'
'Web-Performance'
'Web-Stat-Compression'
'Web-Dyn-Compression'
'Web-Security'
'Web-Filtering'
'Web-Basic-Auth'
'Web-Client-Auth'
'Web-Digest-Auth'
'Web-Cert-Auth'
'Web-IP-Security'
'Web-Url-Auth'
'Web-Windows-Auth'
'Web-App-Dev'
'Web-Net-Ext'
'Web-Net-Ext45'
'Web-ASP'
'Web-Asp-Net'
'Web-Asp-Net45'
'Web-CGI'
'Web-ISAPI-Ext'
'Web-ISAPI-Filter'
'Web-Includes'
'Web-WebSockets'
'Web-Ftp-Server'
'Web-Ftp-Service'
'Web-Mgmt-Tools'
'Web-Mgmt-Console'
'Web-Mgmt-Compat'
'Web-Metabase'
'Web-Lgcy-Mgmt-Console'
'Web-Lgcy-Scripting'
'Web-WMI'
'Web-Scripting-Tools'
'Web-Mgmt-Service'
'NET-Framework-Features'
'NET-Framework-Core'
'NET-Framework-45-Features'
'NET-Framework-45-Core'
'NET-Framework-45-ASPNET'
'NET-WCF-Services45'
'NET-WCF-HTTP-Activation45'
'NET-WCF-TCP-PortSharing45'
'RSAT'
'RSAT-Feature-Tools'
'RSAT-SMTP'
'RSAT-SNMP'
'FS-SMB1'
'SMTP-Server'
'SNMP-Service'
'User-Interfaces-Infra'
'Server-Gui-Mgmt-Infra'
'Server-Gui-Shell'
'PowerShellRoot'
'PowerShell'
'PowerShell-V2'
'PowerShell-ISE'
'WAS'
'WAS-Process-Model'
'WAS-Config-APIs'
'WoW64-Support'
)
}

I also tryed to use the Install-WindowsFeature cmdlet to install several features in a setup. I ended using dism because it seems to be less error-prone. You could at least give it a try and maybe get a more detailed error message. Here is the script:
$featuresToInstall = ('FileAndStorage-Services','Storage-Services','Web-Server','Web-WebServer','Web-Common-Http','Web-Default-Doc','Web-Dir-Browsing','Web-Http-Errors',
'Web-Static-Content','Web-Http-Redirect','Web-Health','Web-Http-Logging','Web-Log-Libraries','Web-ODBC-Logging','Web-Request-Monitor',
'Web-Http-Tracing','Web-Performance','Web-Stat-Compression','Web-Dyn-Compression','Web-Security','Web-Filtering','Web-Basic-Auth',
'Web-Client-Auth','Web-Digest-Auth','Web-Cert-Auth','Web-IP-Security','Web-Url-Auth','Web-Windows-Auth','Web-App-Dev','Web-Net-Ext',
'Web-Net-Ext45','Web-ASP','Web-Asp-Net','Web-Asp-Net45','Web-CGI','Web-ISAPI-Ext','Web-ISAPI-Filter','Web-Includes','Web-WebSockets',
'Web-Ftp-Server','Web-Ftp-Service','Web-Mgmt-Tools','Web-Mgmt-Console','Web-Mgmt-Compat','Web-Metabase','Web-Lgcy-Mgmt-Console','Web-Lgcy-Scripting',
'Web-WMI','Web-Scripting-Tools','Web-Mgmt-Service','NET-Framework-Features','NET-Framework-Core','NET-Framework-45-Features','NET-Framework-45-Core',
'NET-Framework-45-ASPNET','NET-WCF-Services45','NET-WCF-HTTP-Activation45','NET-WCF-TCP-PortSharing45','RSAT','RSAT-Feature-Tools','RSAT-SMTP',
'RSAT-SNMP','FS-SMB1','SMTP-Server','SNMP-Service','User-Interfaces-Infra','Server-Gui-Mgmt-Infra','Server-Gui-Shell','PowerShellRoot','PowerShell',
'PowerShell-V2','PowerShell-ISE','WAS','WAS-Process-Model','WAS-Config-APIs','WoW64-Support')
# Remove any feature that is not available to prevent dism failures. MSMQ-Container for example is only available
# on Windows 7 but not on Windows Server 2008 R2.
$availableFeatures = dism /online /Get-Features
$featuresToRemove = #()
$featuresToInstall | % { if (-not ($availableFeatures | Select-String ('{0}$' -f $_) -Quiet)) { $featuresToRemove += $_} }
$featuresToInstall = Compare-Object -ReferenceObject $featuresToInstall -DifferenceObject $featuresToRemove | select -ExpandProperty InputObject
$dismParameter = #('/online', '/Enable-Feature', ($featuresToInstall | % { '/FeatureName:{0}' -f $_ }), '/NoRestart', '/all')
$output = dism #dismParameter
# throw an error if dism wasn't successful
if ($global:LastExitCode -ne 0)
{
throw 'Error while installing Windows Features. {0}' -f ($output | Select-String '\.log$')
}
The Script also validates whether the feature is available to install using dism and removes them in that case (you can check $featuresToRemove).

In a non core installation of Windows server 2 features are not present, one of them is net-framework-core (i forgot which other one it is), thats why you point to the source in the wim like the MS kb states, but this fails most of the times.
Instead of pointing the source to the wimfile you should point it to the \sources\sxs location of your installation medium, this always does the trick for me (you can also point to a network location with the files)

Related

Error in PowerShell one liner for browser process management

I'm trying to run a code in PowerShell in one line. This code is a loop that's used for surveillance. If Microsoft Edge is opened the process has to close Chrome.
My code it works well if Edge is not opened, it goes right by the if condition, but if Edge is opened it returns me an error in the else condition.
System is Windows 10 with PowerShell ISE.
$a = 1 ;DO { 'Starting Loop' ; $vischk = get-process | where-object {$_.mainwindowhandle -ne 0 -and $_.MainWindowTitle -eq 'Start - Microsoft Edge'} | select-object name, mainwindowtitle ; if (!($vischk)) {Write-Warning 'Microsoft Edge is off'}else{Write-Warning 'Closing Chrome' Stop-Process -name chrome} ; Write-Warning 'Active surveillance' ; Start-Sleep -s 15} While ($a -le 2)
I need to run the surveillance window and when Microsoft Edge is opened, close another browser like chrome or another process name.
This is also not a One-Liner, it's a long script all put on one line. ;-},
hence your use of the semicolon. Semicolon means what is before it and after it
are independent code blocks.
I get that use case in an interactive consolehost thing, but in a script, well,
that's an entirely different thing.
Yet, doing this. the way you have it is a choice. Dev in the ISE, save the file,
run from the console directly or shell out to it from the ISE.
Also the way you are checking for the MS Edge instance is not correct. The
MainWindowTitle is only 'Microsoft Edge'
Get-Process -Name MicrosoftEdge | select *
<#
Name : MicrosoftEdge
Id : 6388
PriorityClass : Normal
...
ProductVersion : 11.00.17763.529
Description : Microsoft Edge
Product : Microsoft Edge
__NounName : Process
...
SafeHandle : Microsoft.Win32.SafeHandles.SafeProcessHandle
MachineName : .
MainWindowHandle : 132100
MainWindowTitle : Microsoft Edge
MainModule : System.Diagnostics.ProcessModule (MicrosoftEdge.exe)
MaxWorkingSet : 1413120
MinWorkingSet : 204800
Modules : {System....
#>
$Code = #'
$a = 1
DO {
'Starting Loop'
$vischk = get-process |
where-object {
$_.mainwindowhandle -ne 0 -and
$_.MainWindowTitle -eq 'Microsoft Edge'
} | select-object name, mainwindowtitle
if (!($vischk))
{Write-Warning 'Microsoft Edge is off'}
else{
Write-Warning 'Closing Chrome'
Stop-Process -name chrome
}
Write-Warning 'Active surveillance'
Start-Sleep -s 3}
While ($a -le 2)
'#
You can stay in the ISE to test code, but as Olaf points out, your user may not be the same. So, you need to validate both environments. You can stay in the ISE and test your code there as well as in the consolehost.
So, to test code using a consolehost instance from the ISE/VSCode without typing in the console.
Start-Process powershell -ArgumentList "-NoExit","-Command &{ $Code }" -Wait
Or
Start-Process pwsh -ArgumentList "-NoExit","-Command &{ $Code }" -Wait
Or just open the consolehost and run the script
The above works, as designed based on your defined case.
I agree with Olaf here as well, you need to add more error checking for what is and is not running for this to be more operationally sound. Don't run code you don't need to run if a target does not exist.

Why order is not maintained while reading a text file from Powershell?

Below is my Powershell script:
$server_file = 'serverlist.txt'
$servers = #{}
Get-Content $server_file | foreach-object -process {$current = $_.split(":"); $servers.add($current[0].trim(), $current[1].trim())}
foreach($server in $servers.keys){
write-host "Deploying $service on $server..." -foregroundcolor green
}
My serverlist.txt looks like this:
DRAKE : x64
SDT: x64
IMPERIUS : x64
Vwebservice2012 : x64
Every time I run this script, I get IMPERIUS as my server name. I would like loop through the servers in the order they are written in serverlist.txt.
Am I missing anything in Get-Content call?
Don't store servers in a temporary variable.
The iteration order of hashtables (#{}) is not guaranteed by the .NET framework. Avoid using them if you want to maintain input order.
Simply do:
$server_file = 'serverlist.txt'
Get-Content $server_file | ForEach-Object {
$current = $_.split(":")
$server = $current[0].trim()
$architecture = $current[1].trim()
Write-Host "Deploying $service on $server..." -ForegroundColor Green
}
Note: Even if it probably won't make much of a difference in this particular case, in general you always should explicitly define the file encoding when you use Get-Content to avoid garbled data. Get-Content does not have sophisticated auto-detection for file encodings, and the default it uses can always be wrong for your input file.

how to use PowerShell to inventory Scheduled Tasks

Does anyone have a link or script that uses PowerShell to inventory the Scheduled Tasks on a server, including the Action?
I am able to get the Scheduled Service com object and what I would call "top level" properties (name, state, lastruntime), but would like to also get information from the "Actions" part of the Schedule Tasks (essentially, the name of Scheduled Task and its commandline).
For example:
$schedule = new-object -com("Schedule.Service")
$schedule.connect()
$tasks = $schedule.getfolder("\").gettasks(0)
$tasks | select Name, LastRunTime
foreach ($t in $tasks)
{
foreach ($a in $t.Actions)
{
$a.Path
}
}
The above snippet of code works in terms of listing the tasks; but the loop on the Actions simply does not seem to do anything, no error, no output whatsoever.
Any help would be appreciated.
This is probably very similar to current answers, but I wrote a quick script to get you going. The problem with your current script is that there is no Actions property in a task. You need to extract it from the xml task-definition that the comobject provides. The following script will return an array of objects, one per scheduled task. It includes the action if the action is to run one or more command. It's just to get you going, so you need to modify it to include more of the properties if you need them.
function getTasks($path) {
$out = #()
# Get root tasks
$schedule.GetFolder($path).GetTasks(0) | % {
$xml = [xml]$_.xml
$out += New-Object psobject -Property #{
"Name" = $_.Name
"Path" = $_.Path
"LastRunTime" = $_.LastRunTime
"NextRunTime" = $_.NextRunTime
"Actions" = ($xml.Task.Actions.Exec | % { "$($_.Command) $($_.Arguments)" }) -join "`n"
}
}
# Get tasks from subfolders
$schedule.GetFolder($path).GetFolders(0) | % {
$out += getTasks($_.Path)
}
#Output
$out
}
$tasks = #()
$schedule = New-Object -ComObject "Schedule.Service"
$schedule.Connect()
# Start inventory
$tasks += getTasks("\")
# Close com
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($schedule) | Out-Null
Remove-Variable schedule
# Output all tasks
$tasks
Ex. of output
PS > .\Untitled1.ps1 | ? { $_.Name -eq "test" }
Actions : notepad.exe c:\test.txt
calc.exe
Path : \test
Name : test
LastRunTime : 30.12.1899 00:00:00
NextRunTime : 17.03.2013 13:36:38
Get the PowerShellPack from the W7 RK, and try get-scheduledtask
http://archive.msdn.microsoft.com/PowerShellPack
Excerpt From MSDN:
The Windows 7 Resource Kit PowerShell Pack contains 10 modules to do all sorts of interesting things with PowerShell. Import-Module PowerShellPack actually imports 10 modules for you to use. Here’s a brief overview of each of the modules.
WPK Create rich user interfaces quick and easily from Windows PowerShell. Think HTA, but easy. Over 600 scripts to help you build quick user interfaces
TaskScheduler List scheduled tasks, create or delete tasks
FileSystem Monitor files and folders, check for duplicate files, and check disk space
IsePack Supercharge your scripting in the Integrated Scripting Environment with over 35 shortcuts
DotNet Explore loaded types, find commands that can work with a type, and explore how you can use PowerShell, DotNet and COM together
PSImageTools Convert, rotate, scale, and crop images and get image metadata
PSRSS Harness the FeedStore from PowerShell
PSSystemTools Get Operating System or Hardware Information
PSUserTools Get the users on a system, check for elevation, and start-processaadministrator
PSCodeGen Generates PowerShell scripts, C# code, and P/Invoke
Another way would be a script I wrote called Get-ScheduledTask.ps1, available in this article:
How-To: Use PowerShell to Report on Scheduled Tasks
In this way you only need this single script and you don't need to download or install anything else.
Bill
I know I'm late to the party, but the answer provided by #Frode F., while it works, is technically not correct.
You can access items of the Actions collection of a scheduled task via PowerShell, it's just not immediately obvious. I had to figure this out myself today as well.
Here's the code to do this all in PowerShell, without having to muck around with XML:
# I'm assuming that you have a scheduled task object in the variable $task:
$taskAction = $task.Definition.Actions.Item.Invoke(1) # Collections are 1-based
That's all there is to getting a single item out of the collection without using foreach.
Because the Actions property is a collection which contains a parameterized property Item (e.g. in C# you would write myTask.Actions[0] or in VB myTask.Actions.Item(1)), PowerShell represents the Item property as a PSParameterizedProperty object. To call the methods associated with the property, you use the Invoke method (for the getter) and InvokeSet method (for the setter).
I ran a quick test running the OP's code and it worked for me (I'm running PowerShell 4.0, however, so maybe that has something to do with it):
$schedule = new-object -com("Schedule.Service")
$schedule.connect()
$tasks = $schedule.getfolder("\").gettasks(0)
$tasks | select Name, LastRunTime
foreach ($t in $tasks)
{
foreach ($a in $t.Actions)
{
Write-Host "Task Action Path: $($a.Path)" # This worked
Write-Host "Task Action Working Dir: $($a.workingDirectory)" # This also worked
}
$firstAction = $t.Actions.Item.Invoke(1)
Write-Host "1st Action Path: $($firstAction.Path)"
Write-Host "1st Action Working Dir: $($firstAction.WorkingDirectory)"
}
HTH.
here a quick one based on: https://blogs.technet.microsoft.com/heyscriptingguy/2015/01/17/weekend-scripter-use-powershell-to-document-scheduled-tasks/
Uses Powershell: Get-ScheduledTask and Get-ScheduledTaskInfo
### run like >> Invoke-Command -ComputerName localhost, server1, server2 -FilePath C:\tmp\Get_WinTasks.ps1
$taskPath = "\"
$outcsv = "c:\$env:COMPUTERNAME-WinSchTaskDef.csv"
Get-ScheduledTask -TaskPath $taskPath |
ForEach-Object { [pscustomobject]#{
Server = $env:COMPUTERNAME
Name = $_.TaskName
Path = $_.TaskPath
Description = $_.Description
Author = $_.Author
RunAsUser = $_.Principal.userid
LastRunTime = $(($_ | Get-ScheduledTaskInfo).LastRunTime)
LastResult = $(($_ | Get-ScheduledTaskInfo).LastTaskResult)
NextRun = $(($_ | Get-ScheduledTaskInfo).NextRunTime)
Status = $_.State
Command = $_.Actions.execute
Arguments = $_.Actions.Arguments }} |
Export-Csv -Path $outcsv -NoTypeInformation

How to find out what version of webdeploy/msdeploy is currently installed?

I'm looking for something like a Powershell script to check if msdeploy is installed and if it is, what version
I've considered checking "c:\Program Files\IIS" and checking for MSDeploy installations there, but is this always guaranteed to be the install location?
I need this to work on any given server machine
When msdeploy is installed (no matter where in the file system), it will add its install path to the registry at;
HKLM\Software\Microsoft\IIS Extensions\MSDeploy\<version>\InstallPath
and its version information to;
HKLM\Software\Microsoft\IIS Extensions\MSDeploy\<version>\Version
...where <version> is currently 1, 2 or 3 depending on the WebDeploy version you have installed.
Depends on what you consider "version". By the folder name "c:\Program Files\IIS\Microsoft Web Deploy V3", the version is 3, but if you run msdeploy.exe, the version is 7.X
This is what I did in my PowerShell script:
$WebDeployInstalled = Get-WmiObject Win32_Product | ? {$_.Name -like '*Microsoft Web Deploy*'}
if ($WebDeployInstalled -eq $null)
{
$msg = "Microsoft Web Deploy is not found on this machine."
Write-host -BackgroundColor Red -ForegroundColor White $msg
return
}
else
{
$MSDeployPath = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\*" | Select-Object InstallPath
$MSDeployPath = $MSDeployPath.InstallPath
}
HTH
You can use the following PowerShell snippet:
$installPath = $env:msdeployinstallpath
if(!$installPath){
$keysToCheck = #('hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1')
foreach($keyToCheck in $keysToCheck) {
if(Test-Path $keyToCheck){
$installPath = (Get-itemproperty $keyToCheck -Name InstallPath -ErrorAction SilentlyContinue | select -ExpandProperty InstallPath -ErrorAction SilentlyContinue)
}
if($installPath) {
break;
}
}
}
If you wrap it into script block then you can call it in remote session.

Powershell script cannot get applications list data from windows 7 machine

Recently, I made a script to list all the installed applications in local & remote machine & give the output in a structured manner in an excelsheet.
It looks like this:
$a = Read-Host "Enter machine name" | Out-File -filepath C:\machine.txt
$computerName = Get-Content C:\machine.txt
$a = New-Object -comobject Excel.Application
$a.visible = $True
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Cells.Item(1,1) = "Name"
$c.Cells.Item(1,2) = "Publisher"
$c.Cells.Item(1,3) = "InstalledDate"
$c.Cells.Item(1,4) = "Version"
$c.Cells.Item(1,5) = "UninstallString"
$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True
$i = 2
function Get-InstalledAppReg ([string]$ComputerName) {
$RegPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
$BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $ComputerName)
$OpenSubKey = $BaseKey.OpenSubKey($RegPath)
$i =2
$OpenSubKey.GetSubKeyNames() | ForEach {
$Path = "$RegPath\$_"
$BaseKey.OpenSubKey($Path).GetValue("DisplayName")
$BaseKey.OpenSubKey($Path).GetValue("Publisher")
$BaseKey.OpenSubKey($Path).GetValue("InstalledDate")
$BaseKey.OpenSubKey($Path).GetValue("Version")
$BaseKey.OpenSubKey($Path).GetValue("UninstallString")
$c.Cells.Item($i,1) = $BaseKey.OpenSubKey($Path).GetValue("DisplayName")
$c.Cells.Item($i,2) = $BaseKey.OpenSubKey($Path).GetValue("Publisher")
$c.Cells.Item($i,3) = $BaseKey.OpenSubKey($Path).GetValue("InstalledDate")
$c.Cells.Item($i,4) = $BaseKey.OpenSubKey($Path).GetValue("Version")
$c.Cells.Item($i,5) = $BaseKey.OpenSubKey($Path).GetValue("UninstallString")
$i ++
}
}
Get-InstalledAppReg($computerName)
$d.EntireColumn.AutoFit()
$b.SaveAs("c:\softhive.xlsx")
$b.Close()
$a.Quit()
Get-Process | Where { $_.Name -Eq "Excel" } | Kill
This script ran perfectly for all remote machines which has XP as a OS.
Problem started when I started running it in windows & machines remotely.
Initially it gave wrong path error, when I realized that for windows 7, I probably have to use
"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" instead of
"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall".
With this different path, when I run the same script again, I get an error:
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found.
"
At :line:24 char:62
$BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey( <<<< "LocalMachine", $ComputerName)
Probably, I need to change other things too in the script?
My machine, from where I run the script, is a windows XP SP3 machine.
Unfortunately the WMI Win32_Product class does not report all apps found in Control Panel's "Add or Remove Programs"...
The registry walk seems to be unavoidable, see:
http://powergui.org/thread.jspa?threadID=17068
http://learningpcs.blogspot.fr/2011/10/powershell-get-installed-software.html
Rather than comb the registry, I would use WMI for this. See Win32_Product and friends e.g.:
Get-WmiObject Win32_Product
Note that if I run this on my Windows 7 x64 system in a 64bit PowerShell prompt it shows all installed apps (32-bit and 64-bit):
Get-WmiObject Win32_Product| sort Vendor | Format-Table Name,InstallDate,Vendor
To see all the properties available execute:
Get-WmiObject Win32_Product | Select -First 1 | Format-List *
I remember a while back I did something like this at an IT firm and we simply searched the C: directory for the names of all programs ending in .exe, in order to optimize we would hone in on specific apps that we were looking for. We set up a batch that would pass or fail based on if what we wanted. Keep in mind this is a batch file, however the idea is similar.
echo ================= >>Software_Scan.txt
echo Below is a list of all wireless networks. Saved networks will be found in the Wireless Profiles folder
set filePath=
for /R "C:\Program Files (x86)" /D %%a in (*) do if exist "%%a\YahooMessenger.exe" set filePath=%%a& goto continue
:continue
if defined filePath echo %COMPUTERNAME% FAIL Yahoo Messenger >> Software_Scan.txt
if NOT defined filePath echo %COMPUTERNAME% PASS Yahoo Messenger >> Software_Scan.txt