How to update variables in script? - powershell

Script should restart service and save current data and time in file after memory usage reach the limit. But it doesn't save current time and in second if when service is stopped it doesn't do else. It always show the same values in $data and $stat. I am not sure where I made mistake.
$proc = 'process name'
$serv = 'service name*'
$ram = 10MB
$inter = 1
$data = Get-Date -format "yyyy/MM/dd HH:mm:ss"
$log = "c:\log.txt"
$stat = Get-Process $proc -EA SilentlyContinue
while ($true) {
if ((Get-Process $proc -EA SilentlyContinue | Select-Object -Exp ws) -gt $ram) {
Restart-Service $serv
Add-Content -path $log -value ($data + "`t" + "Restarting")
Start-Sleep -m 10000
if ($stat -ne $null) {
Add-Content -path $log -value "Working"
} else {
Start-Service $serv
Add-Content -path $log -value ($data + "`t" + "Starting")
}
}
Start-Sleep -s $inter
}

The statements in your variable assignments are evaluated at the time of assignment, not when the variable is used. To get the latter you could define the statements as scriptblocks:
$data = { Get-Date -format 'yyyy\/MM\/dd HH:mm:ss' }
$stat = { Get-Process mysqld -ea SilentlyContinue }
Use the call operator (&) in an expression (()) or subexpression ($()) to evaluate the scriptblocks at a later point in your script:
if ((&$stat) -ne $null) {
Add-Content -Path $log -Value "Working"
} else {
Start-Service $serv
Add-Content -Path $log -Value "$(&$data)`tStarting"
}
Probably a more convenient way is to define the operations as functions:
function Get-Timestamp { Get-Date -format 'yyyy\/MM\/dd HH:mm:ss' }
function Test-MySQLProcess { [bool](Get-Process mysqld -EA SilentlyContinue) }
and use them like this:
if (Test-MySQLProcess) {
Add-Content -Path $log -Value "Working"
} else {
Start-Service $serv
Add-Content -Path $log -Value "$(Get-Timestamp)`tStarting"
}
As a side note, you should escape forward slashes in date format strings, otherwise Windows substitutes them with whatever date separator character is configured in your system's regional settings.

Related

Why is my if statement not being read properly?

I have a script that backs up a user profile from the local, or remote machine and places it onto a share: $Global:Shared_BackupPath = "\\server\share\". I've been tweaking it a little more and just ended up making some variables into Global variables (not sure if this is the issue - dont see why it would be).
This is the condition:
if(-not (Get-EventSubscriber)){
I tried changing it to -eq $null to see if it would make any difference, but it didn't.
Its just not analyzing the condition properly and goes on to display my message box before all Jobs are done: it's "supposed" to wait till there's no more events and then display the message box:
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
#Start-Sleep -Milliseconds 500
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
if(-not (Get-EventSubscriber)){
$Profile_Sum = Get-ChildItem -Path $Global:BackUp_Path -Recurse |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum
$Size = try{if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } } Catch {}
$Begin_Time = Get-Item -Path $Global:BackUp_Path | Select-Object -ExpandProperty LastWriteTime
$End_Time = Get-Date -Format G
Get-Job | Remove-Job
[System.Windows.MessageBox]::Show("Copying Complete!`nStart Time: $Begin_Time `nEnd Time: $End_Time `nProfile Size copied: $Size")
}
} | Out-Null
}
I feel like I may have an idea due to the event itself being registered as a job but, not too sure how to go about it and have it wait until its done with ALL jobs before displaying the messagebox informing me when the copying is complete. Other than that, the script works just fine and anyone reading this can feel free to use it themselves. Heres the full Script:
Function PFL-UserBackUp{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$false,
ValueFromPipeLine=$true,
ValueFromPipeLineByPropertyName=$true)]
[Alias('CN','Computer','server','node')]
[ValidateLength(1, 15)]
[String[]]$ComputerName = $env:COMPUTERNAME )
Begin{
$Global:Shared_BackupPath = "\\server\share\"
}
Process{
Foreach($Computer in $ComputerName){
Try{
$PSSession = New-PSSession -ComputerName $Computer -ErrorAction Stop
[array]$User_List = Invoke-Command -ScriptBlock {
Get-ChildItem -Path "C:\Users" -Exclude Public, Default* |
Sort-Object -Property LastWriteTime -Descending } -Session $PSSession
$userinfo1 = foreach ($user in $User_List.name) {
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) { "NO DISPLAY NAME in ADUC" }
elseif($LASTEXITCODE -eq 2) { "ACCOUNT NOT in ADUC" }
elseif($LASTEXITCODE -eq 0) { $userinfo }
else { "Error occured" }
}
$(for($i=0; $i -lt $User_List.Count; $i++){
[pscustomobject]#{
'User Display Name ' = "$($i): $($userinfo1[$i])"
' NAME ' = $User_List.name[$i]
'Last Modified' = "$($User_List.LastWriteTime[$i])"
'Profile Size ' = Try{
$ProfilePath = $User_List.FullName[$i]
$Profile_Sum = Invoke-Command -ScriptBlock {
Get-ChildItem -Path $Using:ProfilePath -Recurse |
Where-Object {$_.PSParentPath -match "Documents|Desktop|Music|Videos|Downloads|Links|Pictures|Favorites|Contacts" -and $_.DirectoryName -notmatch "OneDrive" } |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum } -Session $PSSession
if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } #Profile Size
} Catch { "$($Error[0].Exception.Message.Split('.')[2].Trim())!" }
}
} ) | Out-Host
Write-Host "Press 'Q' to quit."
$ii = Read-Host -Prompt "Enter Number of Profile to Back-Up"
$i = $ii.Trim() -split ","
if([String]::IsNullOrEmpty($i) -eq $true) { "Null string"; Break }
elseif($i.ToLower() -like "q*") {"Q was selected. Stopping script."; Break }
<#
" "
" Following Profiles will be Saved:"
" ------------------------------------"
foreach($i in $index) { "$($i.trim()): $($userinfo1[$i])" }
" "
$Confirm = Read-Host -Prompt "Are you sure you want to continue? [Y/N]"
if($Confirm.ToLower().TrimStart() -like "n*" -or $Confirm.ToLower() -like "q*"){Break}
if([String]::IsNullOrEmpty($Confirm.Trim()) -eq $true) { "Null string"; Break }#>
$Profile_Path = "C:\Users\$($User_List.name[$i])"
$Literal_Name = $userinfo1[$i].Replace('/','-')
$Global:BackUp_Path = "$Global:Shared_BackupPath$Literal_Name"
$Test_Path = Test-Path -Path $Global:BackUp_Path
if($Test_Path -eq $false){
New-Item -Path $Global:BackUp_Path -ItemType Directory | Out-Null
Start-Process $Global:BackUp_Path}
elseif($Test_Path -eq $true){
$Find_OldName = Get-ChildItem -Path "$Global:Shared_BackupPath" -Filter "$Literal_Name" -Directory |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -ExpandProperty BaseName -First 1
$New_PathName = $Find_OldName + "1"
New-Item -Path "$Global:Shared_BackupPath" -Name $New_PathName -ItemType Directory -OutVariable Global:BackUp_Path | Out-Null #Global:BackUp_Path variable declared
$Global:BackUp_Path = $Global:BackUp_Path.FullName
Start-Process $Global:BackUp_Path}
$Global:Start_Time = Get-Date -Format G
#Favorites Copy
$FireFox_Favorites = "C:\Users\$($User_List.name[$i])\AppData\Roaming\Mozilla\Firefox\Profiles\*.default\places.sqlite"
$Chrome_Favorites = "C:\Users\$($User_List.name[$i])\AppData\Local\Google\Chrome\User Data\Default\Bookmarks"
$Chrome_Favorites2 = "C:\Users\$($User_List.name[$i])\AppData\Local\Google\Chrome\User Data\Default\Bookmarks.bak"
$Sticky_Notes = "C:\Users\$($User_List.name[$i])\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState\plum.sqlite"
$Favorites_Array = #($FireFox_Favorites,$Chrome_Favorites,$Chrome_Favorites2,$Sticky_Notes)
Foreach($File in $Favorites_Array){
$Test_File = Invoke-Command -ScriptBlock { Test-Path -Path $File }
if($Test_File -eq $true){
Copy-Item -Path $File -Destination $Global:BackUp_Path -Force -Recurse -FromSession $PSSession
}
}
#Folders Copy
$Folders = #('Desktop','Documents','Favorites','Links','Downloads','Music','Videos','Pictures','Contacts')
Foreach($Folder in $Folders){
#Create Arugments for seperate thread
$ArgumentsArray = $null
$ArgumentsArray = #()
$ArgumentsArray += "\\$Computer\c$\Users\$($User_List.name[$i])\$Folder"
$ArgumentsArray += $Global:BackUp_Path
$job = Start-Job -ScriptBlock { Copy-Item -Path $args[0] -Destination $args[1] -Force -Recurse } -Name $Folder -ArgumentList $ArgumentsArray
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
#Start-Sleep -Milliseconds 500
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
if(-not (Get-EventSubscriber)){
$Profile_Sum = Get-ChildItem -Path $Global:BackUp_Path -Recurse |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum
$Size = try{if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } } Catch {}
$Begin_Time = Get-Item -Path $Global:BackUp_Path | Select-Object -ExpandProperty LastWriteTime
$End_Time = Get-Date -Format G
Get-Job | Remove-Job
[System.Windows.MessageBox]::Show("Copying Complete!`nStart Time: $Begin_Time `nEnd Time: $End_Time `nProfile Size copied: $Size")
}
} | Out-Null
}
" "
Write-Output -InputObject "Copying will be done in background."
Write-Output -InputObject "You will be notified when copying is done."
} catch [System.Management.Automation.Remoting.PSRemotingTransportException]{
"Unable to connect to PC: $Computer `nError: $($Error[0].Exception.Message.Split('.')[2].Trim())!"
}
}
}
}
I removed some information that could get me in trouble but, it's all cosmetics.(:
EDIT: I must be on crack but, everything is working now. Only changes I made was clearing the global variables ($Global:var = $null) before assigning it a value. Thank you all for the suggestions. Another change i made was change Copy-Item to Robocopy instead.
To just answer your question:
Why is my if statement not being read properly?
Just before you evaluate Get-EventSubscriber your are cancelling your event subscription and thus deleting your event subscriber. Get-EventSubscriber therefore returns $null which evaluates to $true, when negated. In conclusion, the code after your if statement will always be executed immediately.
Commented code:
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
$eventSubscriber | Unregister-Event # Here you cancel your event subscription
$eventSubscriber.Action | Remove-Job
if (-not (Get-EventSubscriber)) { # Therefore, Get-EventSubscriber returns $null; not $null evaluates to $true
...
The relevant part in the documentation of Unregister-Event:
The Unregister-Event cmdlet cancels an event subscription that was created by using the Register-EngineEvent, Register-ObjectEvent, or Register-WmiEvent cmdlet.
When an event subscription is canceled, the event subscriber is deleted from the session and the subscribed events are no longer added to the event queue. When you cancel a subscription to an event created by using the New-Event cmdlet, the new event is also deleted from the session.

Invoke-Command: Null-value and positional parameter errors

I wanted to start a new thread for this, since I am using a different method in my code now. I have written a script that pings hundreds of devices and logs their online or offline status. It was taking an extremely long time to run, so I am now looking into using Invoke-Command to run the commands remotely on servers for each site (instead of all from the same server). I am receiving the following errors: "A positional parameter cannot be found that accepts argument 'Of'", "You cannot call a method on a null-valued expression", and this is happening for each server as it is iterating through them. Any ideas as to why this is happening? Thank you very much, here is my current code:
<#
.NOTES
===========================================================================
Created on: 11/17/2016 8:06 AM
Created by:
Organization:
Filename: Get-MPOSOfflinePrinters.ps1
===========================================================================
.DESCRIPTION
#>
#Define log file variables and remove any existing logs
$logfile = "D:\Logs\MPOSPrinterPingLog.txt"
$offlineprinters = "D:\Reports\MPOS\MPOSOfflinePrinters.txt"
If (Test-Path $logfile) {Remove-Item $logfile}
If (Test-Path $offlineprinters) {Remove-Item $offlineprinters}
Add-Content $logfile "Gathering server list"
#Compiling list of all MPOS Print Servers
$serverList = (Get-ADComputer -Filter "Name -like 'Q0*P30' -or Name -like 'Q0*P32'" -SearchBase "OU=Print,OU=Prod,OU=POS,DC=COMPANY,DC=NET").name | Sort-Object | Out-File C:\Temp\MPOS\MPOSPrintServers.txt
$serverListPath = "C:\Temp\MPOS\MPOSPrintServers.txt"
Add-Content $logfile "Compiling text file"
#Retrieve a list of MPOS Print servers from text file and set to variable $serverNames
$serverNames = Get-Content -Path $serverListPath
Invoke-Command -ComputerName $serverNames -ScriptBlock {
#Define log file variables and remove any existing logs
$logfile = "C:\Temp\MPOSPrinterPingLog.txt"
$offlineprinters = "C:\Temp\MPOSOfflinePrinters.txt"
$masteroffline = "\\a0345p689\d$\Reports\MPOS\MPOSOfflinePrinters.txt"
If (Test-Path $logfile) {Remove-Item $logfile}
If (Test-Path $offlineprinters) {Remove-Item $offlineprinters}
#process xml file to parse IP addresses for ping
$timestamp2 = (Get-Date -Format g)
Add-Content $logfile "$timestamp2 - Processing xml file from $serverName to parse data to csv"
$xml = [xml](Get-Content C:\ProgramData\Microsoft\Point Of Service\Configuration\Configuration.xml)
$PrinterNames = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{LogicalName=$_.LogicalName.Name}}
$PrinterIPs = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{HardwarePath=$_.HardwarePath}}
foreach ($PrinterIP in $PrinterIPs) {
$pingableIP = $PrinterIP.HardwarePath
If (Test-Connection $pingableIP -Quiet -Count 1) {
$timestamp3 = (Get-Date -Format g)
Add-Content $logfile "$timestamp3 - $serverName, $pingableIP is online and pingable"
}
Else {
$timestamp3 = (Get-Date -Format g)
Add-Content $offlineprinters "$timestamp3 - $serverName, $pingableIP is offline!"
}
Get-Content $offlineprinters | Out-File -FilePath $masteroffline -Append -NoClobber
} #foreach ($PrinterIP in $PrinterIPs) {
} #Invoke-Command -ComputerName $serverNames -ScriptBlock {

Powershell Workflow: Using if/else statement?

I am trying to use a workflow in order to make a script run faster by running a ping on several machines in parallel. Where I am getting stuck is trying to use my if/else statement within the workflow and adding log entries based on the results of the ping. Right now, it is not writing anything to the logs based on the ping results. Any assistance would be greatly appreciated. I am open to suggestions for different methods to do this, as long as they do not involve using a module. I want to stick with just Powershell code (I do not have permissions to install modules on the server where this would be running). Thank you! Here is my current code:
<#
.NOTES
===========================================================================
Created on: 10/6/2016 8:06 AM
Created by:
Organization:
Filename: Get-MPOSOfflinePrinters.ps1
===========================================================================
.DESCRIPTION
#>
$logfile = "D:\Logs\MPOSPrinterPingLog.txt"
$offlineprinters = "D:\Reports\MPOS\MPOSOfflinePrinters.txt"
If (Test-Path $logfile) {Remove-Item $logfile}
If (Test-Path $offlineprinters) {Remove-Item $offlineprinters}
Add-Content $logfile "Setting local path"
$localPath = "C:\Temp\MPOS"
Add-Content $logfile "Gathering server list"
$serverList = (Get-ADComputer -Filter "Name -like 'Q0*P30' -or Name -like 'Q0*P32'" -SearchBase "OU=Print,OU=Prod,OU=POS,DC=COMPANY,DC=NET").name | Sort-Object | Out-File C:\Temp\MPOS\MPOSPrintServers.txt
$serverListPath = "C:\Temp\MPOS\MPOSPrintServers.txt"
#Retrieve a list of MPOS Print servers from text file and set to $serverNames
Add-Content $logfile "Compiling text file"
$serverNames = Get-Content -Path $serverListPath
#Iterate through each of the server names
foreach ($serverName in $serverNames) {
Add-Content $logfile "Processing $serverName"
#Check if the server is online before doing the remote command
If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Add-Content $logfile "$serverName is online"
#copy config file from MPOS print to local server for processing
$timestamp1 = (Get-Date -Format g)
Add-Content $logfile "$timestamp1 - Copying xml file from server to local path"
$configPath = "\\$($serverName)\C$\ProgramData\Microsoft\Point Of Service\Configuration\Configuration.xml"
Copy-Item $configPath $localPath
#process xml file to parse IP addresses for ping
$timestamp2 = (Get-Date -Format g)
Add-Content $logfile "$timestamp2 - Processing xml file from $serverName to parse data to csv"
$xml = [xml](Get-Content C:\Temp\MPOS\Configuration.xml)
$PrinterNames = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{LogicalName=$_.LogicalName.Name}}
$PrinterIPs = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{HardwarePath=$_.HardwarePath}}
workflow Test-WFConnection {
param(
[string[]]$PrinterIPs
)
foreach -parallel ($PrinterIP in $PrinterIPs) {
$pingableIP = $PrinterIP.HardwarePath
If (Test-Connection -ComputerName $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $logfile -Value ("$timestamp3 - $serverName, $pingableIP is online and pingable")
} #If (Test-Connection $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
Else {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $offlineprinters -Value ("$timestamp3 - $serverName, $pingableIP is offline!")
} #Else
} #foreach -parallel ($PrinterIP in $PrinterIPs) {
} #workflow Test-WFConnection {
Test-WFConnection -PSComputerName $PrinterIPs
} #If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Else {
Add-Content $logfile "$serverName is offline!"
} #Else
} #foreach ($serverName in $serverNames) {

Powershell export all detailed logging to csv file

I have inherited a script that is not working. I need to capture everything that would normally output to the console, including Success and Error entries from the script. This is only a small portion of the script, and it only captures errors. Any help would be appreciated on getting all output to the file instead of the console.
An example is the Write-Verbose "VERIFYING contact for $($User.WindowsEmailAddress)"
I know this is writing to the console, but I need it to write to the log that is defined at the very bottom of the script.
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating user: $($Error[0])" -FilePath $LogFilePath -Append}
I hope this makes sense.
### UPDATES
ForEach ($User in $colUpdContact)
{
Write-Verbose "VERIFYING contact for $($User.WindowsEmailAddress)"
#Filter used to find the target contact object(s)
$strFilter = "WindowsEmailAddress -eq `"$($User.WindowsEmailAddress)`""
Try
{$colContacts2 = Invoke-Command -Session $targetSession -ScriptBlock {param ($strFilter) Get-Contact -Filter $strFilter} -ArgumentList $strFilter -ErrorAction Stop}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Find;;;Error getting contact: $($Error[0])" -FilePath $LogFilePath -Append}
ForEach ($Contact in $colContacts2)
{
#initialize update string and cmd string
$strUpdateContact = $null
$updateCmd = $null
$strWriteBack = $null
$writeBackCmd = $null
#Iterate through attributes and append to the strUpdateContact string if the attribute value has changed
ForEach ($Attrib in $arrAttribs)
{
If ($User.$Attrib -ne $Contact.$Attrib)
{
if($ReadOnly){
Add-Content -Path $readOnlyFilePath -Value " Changing $Attrib"
Add-Content -Path $readOnlyFilePath -Value " Before: $($Contact.$Attrib)"
Add-Content -Path $readOnlyFilePath -Value " After: $($User.$Attrib)"
}
$strUpdateContact += " -$($Attrib) `"$($User.$Attrib)`""
Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);CHANGE;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;$($Contact.$Attrib);$($User.$Attrib);" -FilePath $LogFilePath -Append
}
}
#Check if LegacyExchangeDN has been written back to User object
$mailContact = Invoke-Command -Session $targetSession -ScriptBlock {param ($contact) Get-MailContact $($contact.WindowsEmailAddress)} -ArgumentList $Contact -ErrorAction Stop
$x500 = "X500:$($mailContact.LegacyExchangeDN)"
$userRec = Invoke-Command -Session $sourceSession -ScriptBlock {param ($User) Get-Recipient $($User.WindowsEmailAddress)} -ArgumentList $User -ErrorAction Stop
if($UserRec.emailAddresses -notcontains $x500){
$userName = ($user.UserPrincipalName).Split('#')[0]
if($userName -eq "")
{
$userName = $user.SamAccountName
}
$strWriteBack = "Set-ADUser -Identity $userName -Add #{ProxyAddresses=`"$x500`"} -Server $sourceDC -Credential `$sourceDCCredential"
}
#If there is anything to update
If ($strUpdateContact.Length -gt 0)
{
Write-Verbose "Updating attributes for $($User.WindowsEmailAddress)"
#Prepend the command for the contact being modified
$strUpdateContact = "Set-Contact $($User.WindowsEmailAddress) " + $strUpdateContact
If ($ReadOnly)
{Add-Content -Path $readOnlyFilePath -Value $strUpdateContact}
Else
{
Try
{
#Create the complete command and invoke it
$updateCmd = "Invoke-Command -Session `$targetSession -ScriptBlock {$($strUpdateContact)}"
Invoke-Expression $updateCmd -ErrorAction Stop
}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating contact: $($Error[0])" -FilePath $LogFilePath -Append}
}
}
If ($strWriteBack){
Write-Verbose "Updating X500 for $($User.WindowsEmailAddress)"
Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);CHANGE;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;$x500;" -FilePath $LogFilePath -Append
If($ReadOnly){
Add-Content -Path $readOnlyFilePath -Value $strWriteBack
}
else{
Try
{
Invoke-Expression $strWriteBack -ErrorAction Stop
}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating user: $($Error[0])" -FilePath $LogFilePath -Append}
}
}
}
}
Why you not use the Start-Transcript to output all the information into a log file, and then you can manually copy anything you want?
An example for the command:
Start-Transcript -Path $TranscriptOutputFile -Append -Force
#Your script; write-output 'something update';
Stop-Transcript
Everything output by write-output command will be appended into the log file.

List installed applications on remote servers

I have pieced together the following script which lists all installed applications on a local system and writes it into a logfile, but I am unaware how to get this same output when using PSRemoteRegistry, the actual input list I need this against will be all remote targets.
Does anyone have experience with fitting this same code into the cmdlets available through PSRemoteRegistry? Specifically I need it to enumerate the displayname of every installed app found in the key HKLM:\Software\Microsoft\CurrentVersion\Uninstall
The piece I am needing help with getting into the PSRemoteRegistry cmdlets is:
Get-ChildItem 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall' | ForEach-Object {Log (Get-ItemProperty $_.pspath).DisplayName}
and this is the entire script:
clear
#$ErrorActionPreference = "silentlycontinue"
$Logfile = "C:\temp\installed_apps.log"
Function Log
{
param([string]$logstring)
Add-Content $Logfile -Value $logstring
}
$target_list = Get-Content -Path c:\temp\installed_apps_targets.txt
foreach ($system in $target_list){
if (test-connection $system -quiet)
{
Log "---------------Begin $system---------------"
Get-ChildItem 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall' | ForEach-Object {Log (Get-ItemProperty $_.pspath).DisplayName}
Log "---------------End $system---------------"
}
else
{
Log "**************$system was unreachable**************"
}
}
You can adapt something like this:
$Computer = "ComputerName"
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer)
$RegKey= $Reg.OpenSubKey('Software\Microsoft\Windows\CurrentVersion\Uninstall')
$Keys = $RegKey.GetSubKeyNames()
$Keys | ForEach-Object {
$Subkey = $RegKey.OpenSubKey("$_")
Write-Host $Subkey.GetValue('DisplayName')
}
Have you met Invoke-Command?
$Logfile = "C:\temp\installed_apps.log"
Function Log() {
param([string]$logstring)
Add-Content $Logfile -Value $logstring
}
$scriptbock = {Get-ChildItem 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall' | ForEach-Object {Log (Get-ItemProperty $_.pspath).DisplayName}}
Get-Content -Path c:\temp\installed_apps_targets.txt | % {
if (test-connection $_ -quiet) {
Log "---------------Begin $system---------------"
Log $(Invoke-Command -ComputerName $_ -ScriptBlock $scriptblock)
Log "---------------End $system---------------"
}
else
{
Log "**************$system was unreachable**************"
}
}