Attachments.Add wildcard with Powershell - powershell

I have a ZIP file generated with dynamic information (Report_ PC Name-Date_User). However when I go to attach the file I'm unable to use a wildcard. There is only one ZIP file in this directory so using a wildcard will not attach any other ZIP files.
#Directory storage
$DIR = "$ENV:TEMP"
#Max number of recent screen captures
$MAX = "100"
#Captures Screen Shots from the recording
$SC = "1"
#Turn GUI mode on or off
$GUI = "0"
#Caputres the current computer name
$PCName = "$ENV:COMPUTERNAME"
#Use either the local name or domain name
#$User = "$ENV:UserDomainName"
$User = "$ENV:UserName"
#Timestamp
$Date = Get-Date -UFormat %Y-%b-%d_%H%M
#Computer Information
$MAC = ipconfig /all | Select-String Physical
$IP = ipconfig /all | Select-String IPv4
$DNS = ipconfig /all | Select-String "DNS Servers"
#Needed to add space after user input information
$EMPT = "`n"
#Quick capture of the computer information
$Info = #"
$EMPT
*** COMPUTER INFORMATION ***
$PCName
$IP
$MAC
$DNS
"#
# Used to attach to the outlook program
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -Last 1 -ExpandProperty Fullname
$Start_Click = {
psr.exe /start /output $DIR\$Date-$PCName-$User.zip /maxsc $MAX /sc $SC /gui $GUI
}
$Stop_Click={
psr.exe /stop
}
$Email_Click = {
$Outlook = New-Object -Com Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "deaconf19#gmail.com"
$Mail.Subject = "Capture Report from " + $PCName + " " + $User + " " + $Date
$Mail.Body = $Problem.text + $Info
$Mail.Attachments.Add($File)
$Mail.Send()
}
I no longer get an error but the file will not attach the first time around. The second time it will attach but it does the previous .zip not the most recent. I added my entire code

As per the msdn article it shows what the source needs to be which is.
The source of the attachment. This can be a file (represented by the
full file system path with a file name) or an Outlook item that
constitutes the attachment.
Which mean that it does not accept wildcards. To get around this you should instead use Get-ChildItem to return the name of your zip.
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -First 1 -ExpandProperty Fullname
That should return the full path to the first zip. Since Get-ChildItem returns and object we use -ExpandProperty on the Fullname so that you just return the full path, as a string, to the file. -First 1 is not truly required if you really only have the one file. On the off-chance you do including -First 1 will make sure only one file is attached.
Update from comments
I see that you are having issues with attaching a file still. My code would still stand however you might be having an issue with your .zip file or $dir. After where $file is declared I would suggest something like this:
If (! Test-Path $file){Write-Host "$file is not a valid zip file"}
If you would prefer, since I don't know if you see your console when you are running your code, you could use a popup

Related

Powershell copy files from different directories

I have a script that copies the files from one server to another.
I have basically 3 different server locations, that I want to copy from , and create on another server, a folder for each of the location from the source and the contents inside it.
I made that, but I declared a variable for each source and each folder/destination.
I want to create one variable, and from that it should automatically get each location of the source, and copy everything in the correct location.
Would defining $server= "\path1 , \path2, \path3 " do it and it would go into a foreach loop? and go through each part of the path and copy and paste?
If so, how can I define the destination if I have 1 folder with 3 subfolders each corresponding to one source.
for example \path1 should alwasy put items in the path1destination , \path2 should always put items in the path2destination and so on. basically I want somehow to correlate that for each source path to have a specific path destination and everything should use as less variables as possible.
Anyone can provide and ideas on how to tackle this ? My code works but I had to define $path1 , $path2 , $path3 and so on, and then go for a loop on each, which is great but I need to make it clean and less lines of code .
$server1 = "C:\Users\nicolae.calimanu\Documents\B\"
$server2 = "C:\Users\nicolae.calimanu\Documents\A\" # UNC Path.
$datetime = Get-Date -Format "MMddyyyy-HHmmss"
$server3 = "C:\Users\nicolae.calimanu\Documents\C\" # UNC Path.
foreach ($server1 in gci $server1 -recurse)
{
Copy-Item -Path $server1.FullName -Destination $server2
}
ForEach ( $server2 in $server2 ) {
$curDateTime = Get-Date -Format yyyyMMdd-HHmmss
Get-ChildItem $server2 -Recurse |
Rename-Item -NewName {$_.Basename + '_' + $curDateTime + $_.Extension }
}
foreach ($server2 in gci $server2 -Recurse)
{
Move-Item -path $server2 -destination "C:\Users\nicolae.calimanu\Documents\C"
}
Use a hashtable to create a key-value store for each source and destination. Like so,
# Create entries for each source and destination
$ht = #{}
$o = new-object PSObject -property #{
from = "\\serverA\source"
to = "\\serverB\destination" }
$ht.Add($o.from, $o)
$o = new-object PSObject -property #{
from = "\\serverC\source"
to = "\\serverB\destination2" }
$ht.Add($o.from, $o)
$o = new-object PSObject -property #{
from = "\\servera\source2"
to = "\\serverC\destination" }
$ht.Add($o.from, $o)
# Iterate the collection. For demo, print the copy commands
foreach($server in $ht.keys) { $cmd = $("copy-item {0} {1}" -f $ht.Item($server).from, $ht.Item($server).to); $cmd }
# Sample output
copy-item \\serverA\source \\serverB\destination
copy-item \\servera\source2 \\serverC\destination
copy-item \\serverC\source \\serverB\destination2

How to pull a sentence from a log file on multiple remote computers

I am needing to pull a specific sentence from a log file on multiple remote computers. I have all of the computer names already but I do not know how to go about pulling contents of a file from them and copying all of it to a file so that I can reference the sentence from each computer with its machine name. Basically each machine has a specific number unique to itself that we need.
Before explaining, I assume powershell is the tool to use for this.
There are about 1800 machines and I have a variable for all of those. Then I assume I have to make a loop of some kind that runs on each of those machines.
the loop would pull the text from that file that I need and save it all to a file. I am basically pretty new in my Net Admin position with not a lot of PowerShell experience and I wondered if anyone could help.
$computers = ***list of computers***
$computers | ForEachObject{
Add-Content -Path C:\Users\Public\Activant\Eagle\3log.log -Value "Terminal information for ***need the info that is here***"
}
Get-Content -Path .\TERMINAL NUMBERS.txt
this seems to do what you want. [grin] it builds a scriptblock that does the work, hands that off to Invoke-Command with a list of systems to run it on, gathers the results, creates a list of $Non-Responders, removes unwanted properties added by the I-C cmdlet, and finally shows the two collections.
#requires -RunAsAdministrator
# fake reading in a text file
# in real life, use Get-Content
$ComputerNameList = #'
LocalHost
10.0.0.1
127.0.0.1
BetterNotBeThere
'# -split [System.Environment]::NewLine
$IC_ScriptBlock = {
$TargetFileName = 'C:\Temp\Grouping-Strings-List_2019-07-31.log'
# the " \b\w+\b \b\w+\b " is two words delimited by spaces
# so this will find any line that has two words between the listed phrases
$LinePattern = '^Acid Drum \b\w+\b \b\w+\b Psychedelic$'
# the next line is a no-match patern for testing
#$LinePattern = '^Acid Drum \b\w+\b$'
$Line = (Get-Content -LiteralPath $TargetFileName |
Select-String -Pattern $LinePattern).Line
if ([string]::IsNullOrEmpty($Line))
{
$Line = '__Not Found__'
}
[PSCustomObject]#{
ComputerName = $env:COMPUTERNAME
LineText = $Line
}
}
$IC_Params = #{
ComputerName = $ComputerNameList
ScriptBlock = $IC_ScriptBlock
# comment out the next line to see any errors in the I-C call
ErrorAction = 'SilentlyContinue'
}
$Responders = Invoke-Command #IC_Params
$Non_Responders = $ComputerNameList.Where({$_ -notin $Responders.PSComputerName})
# the next line removes unwated properties added by "Invoke-Command"
$Responders = $Responders |
Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId
$Responders
'=' * 40
$Non_Responders
output ...
ComputerName LineText
------------ --------
[MySysName] Acid Drum Instrumental Live Psychedelic
[MySysName] Acid Drum Instrumental Live Psychedelic
========================================
10.0.0.1
BetterNotBeThere
if needed, you can create a single collection from the two above fairly directly. [grin]
I think what you are trying to do is to READ the line from a file all computers in your list should have, located at C:\Users\Public\Activant\Eagle\3log.log
In that case, something like below should work:
# use UNC naming for the remote file path
$inputFile = 'C$\Users\Public\Activant\Eagle\3log.log' # I'm guessing this is the file you want to read
$outputFile = 'C:\TERMINAL NUMBERS.txt'
$computers = ***list of computers*** # the array of computer names
$result = $computers | ForEach-Object {
# test if the computer is online
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
# create the full UNC path by prepending the common file path with the computer name
$file = '\\{0}\{1}' -f $_, $inputFile
# test if the file can be found or not
if (Test-Path -LiteralPath $file -PathType Leaf) {
# using non regex string search
$line = (Select-String -LiteralPath $file -Pattern "whatever you're looking for" -SimpleMatch).Line
if (!$line) {
# the file is there, but the pattern was not found
$line = "Pattern not found"
}
}
else {
$line = "File '$inputFile' not found."
}
}
else {
$line = 'Computer is Off-Line'
}
# Finally, add this info to your text file
Add-Content -Path $outputFile -Value "$_ --> $line"
# And emit an object for the $result collection. This will display nicely on screen,
# but also allow for the creation of a CSV file which might be better as output.
[PsCustomObject]#{
'Computer' = $_
'LogInfo' = $line
}
}
Afterwards you can read the output text file or (better I think) use the $result collection for output:
On screen:
$result | Format-Table -AutoSize
To CSV file
$result | Export-Csv -Path 'C:\TERMINAL NUMBERS.csv' -NoTypeInformation -Force

Error in creating an if statement in powershell with the hostname as a variable

In the script attached I am trying to rename a PC if the PC has a certain hostname. However, the script is proceeding anyway and bypasses the if/else statement.
What am I doing wrong? I am kind of new with Windows Powershell.
Thanks!
# get current computername
$hostname = hostname.exe
#$env:computername
If ( $hostname = "CLNT3100" )
{
#Get all the computers with CLNT3* and sort them with the 'highest client' on top. Then put them in newlist.txt
Get-ADComputer -Filter 'SamAccountName -like "CLNT3*"' | Select -Exp Name | Sort-Object -Descending >> C:\newlist.txt
#Put the text file in a variable and show the top line
$Text = Get-Content -Path C:\newlist.txt
#$Text[0]
#Trim CLNT for numbering
$Text1 = $Text[0].TrimStart("CLNT")
#Add 1 number to the previous CLNT
$Text2 = 1 + $Text1
#Add CLNT again to the new variable
$Text3 = "CLNT" + $Text2
#Rename the computer
Rename-Computer –computername minint –newname $Text3
}
Else
{
Write-Host "Computernaam is niet minint!!!"
}
To compare if two values are equal in Powershell you have to use the -eqoperator.
Check the Powershell equality operators to see the others like -gt, -lt etc, or type man about_Comparison_Operators in the PS shell.
Also, to learn Powershell I found this free ebook to be very good.

List 'target' of 'links'

I need to get the 'target' inside of a shortcut link to another server.
But… when I use -Recurse, it follows the links, instead of simply just getting the shortcut target.
Here is code that I found online that I edited to serve my purpose. But it goes into the links and tries to find shortcuts within another server:
#Created By Scott
$varLogFile = "E:\Server\GetBriefsTargets.log"
$varCSVFile = "E:\Server\GetBriefsTargets.csv"
function Get-StartMenuShortcuts
{
$Shortcuts = Get-ChildItem -Recurse "F:\Connect" -Include *.lnk
#$Shortcuts = Get-ChildItem -Recurse "D:\Scripts\scott_testing" -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
foreach ($Shortcut in $Shortcuts)
{
$Properties = #{
ShortcutName = $Shortcut.Name;
ShortcutFull = $Shortcut.FullName;
ShortcutPath = $shortcut.DirectoryName
Target = $Shell.CreateShortcut($Shortcut).targetpath
}
New-Object PSObject -Property $Properties
}
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
$Output = Get-StartMenuShortcuts
$Output
ECHO "$Output" >> $varLogFile
ECHO "$Output" >> $varCSVFile
Could someone please offer advice on what I can change so that it still finds all of the shortcut links in all of the folders? and stops going inside of those links?
ie:
F:\Connect\CLIENTA\shortcut.lnk
F:\Connect\CLIENTB\shortcut.lnk
etc.
There's about 100 clients that I have to get their links and I don't want to do it manually (each month).
Here is the error that I get upon trying to run this script:
Get-ChildItem : The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the directory name must
be less than 248 characters.
At D:\Scripts\scott_testing\GetShortcutTarget_edit1.ps1:8 char:18
+ $Shortcuts = Get-ChildItem -Recurse "F:\Connect" -Include *.lnk
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ReadError: (F:\Connect\CLIENTA\briefs\FILENAME:String) [Get-ChildItem], PathTooLongException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
It is going 'inside' of the link and trying to find shortcuts on another server.
I can't reproduce. All the targets are probably found.
The results shown in the console by the $output-line should show everything as it should be. If not, update the question with the actual output, the desired output.
The only error I see here is that you try to save CSV-objects using output (text) redirection. That would only return the string-representation of the objects, which is not CSV. In this situation it doesn't output anything when I try it because $output is an array of objects that return nothing when the ToString() method is called.
Replace:
ECHO "$Output" >> $varCSVFile
With:
$Output | Select ShortcutName, ShortcutFull, ShortcutPath, Target | Export-CSV -Path $varCSVFile -NoTypeInformation
Sample output:
"ShortcutName","ShortcutFull","ShortcutPath","Target"
"WinSystem.lnk","C:\Users\frode\Desktop\test\WinSystem.lnk","C:\Users\frode\Desktop\test","C:\Windows\System"
"Lol.lnk","C:\Users\frode\Desktop\Lol.lnk","C:\Users\frode\Desktop","C:\Windows\System32\notepad.exe"
"Windows.lnk","C:\Users\frode\Desktop\Windows.lnk","C:\Users\frode\Desktop","\\localhost\c$\Windows"
"WinSystem32.lnk","C:\Users\frode\Desktop\WinSystem32.lnk","C:\Users\frode\Desktop","\\127.0.0.1\c$\Windows\System32"
UPDATE If you want it do a recursive search you could try something like this. Be aware that the target of the "second-level" shortcut (ex \\server\share\shortcutb.nk might have c:\temp as a target which means you'll get a local path and not an UNC for the remote computer (see MyEnd.lnk in sample output below).
Warning: This could easily result in a infinite loop (circular referencing) or long running search (because of recursive search).
function Get-StartMenuShortcuts ($path) {
$Shortcuts = Get-ChildItem -Path $path -Recurse -Include *.lnk
#$Shortcuts = Get-ChildItem -Recurse "D:\Scripts\scott_testing" -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
foreach ($Shortcut in $Shortcuts)
{
$Properties = #{
ShortcutName = $Shortcut.Name;
ShortcutFull = $Shortcut.FullName;
ShortcutPath = $shortcut.DirectoryName
Target = $Shell.CreateShortcut($Shortcut).targetpath
}
Get-StartMenuShortcuts -path $Properties.Target
New-Object PSObject -Property $Properties
}
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
$output = Get-StartMenuShortcuts -path "F:\Connect"
Example output:
"ShortcutName","ShortcutFull","ShortcutPath","Target"
"WinSystem.lnk","C:\Users\frode\desktop\test\WinSystem.lnk","C:\Users\frode\desktop\test","C:\Windows\System"
"MyEnd.lnk","\\127.0.0.1\c$\EFI\MyEnd.lnk","\\127.0.0.1\c$\EFI","C:\Users\frode\Desktop\TheEnd"
"EFI.lnk","C:\Users\frode\desktop\EFI.lnk","C:\Users\frode\desktop","\\127.0.0.1\c$\EFI"
"Lol.lnk","C:\Users\frode\desktop\Lol.lnk","C:\Users\frode\desktop","C:\Windows\System32\notepad.exe"
If you want to only get the deepest level, you should add a "level"-counter to each object that increases for each recursion call and then keep only the highest ones etc. It might get complicated depending on you needs so that would require a separate detailed question.
I found a command that will work in command prompt, but never seemed to work with powershell:
DIR /AL /S F:\Connect
It took a while to run... but it did.
Also, I found a program that did it in like 2 seconds:
http://sumtips.com/2012/10/find-symbolic-links-ntfs-junction-points-windows.html
I was trying to find the 'symbolic links' ... which I was stating targets.

Find read-host value in file list

I have now moved on to automating IDRAC. My script below will look at a list of servernames and IDRAC IP's. I would like to have a user enter a single Servername "S0000A01PX' or whatever and have it search in a master list for the server IDRAC IP. Right now, the script as it is opens every server's virtual console in the list. I just need it to select only the user entry. How do I have this search the file, find the IP next to the user entered servername, and open only the IDRAC for that servername?
example of what's in the CSV
computername iPiDRAC
S0000A01PX 10.122.2.11
Script
$machinename = ""
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
$filelength = $file.length
$machine = Read-Host 'What is your Server?'
foreach ($line in $file){
$DRACip = $line.iPiDRAC
$DRACpw=cscript c:\PassGen1.vbs $machinename
$DRACpw=$DRACpw[3]
$DRACip
$machinename
$DRACPW
$openIDRAC="http://"+$DRACip+"/console"
$openIDRAC
start $openIDRAC
write-host "----------"
}
If you are just looking to have a general match and return launch all DRACs that match a simple change can get you there. Where-Object{$_.computername -match $machine}
$machinename = ""
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
$machinename = Read-Host 'What is your Server?'
$file | Where-Object{$_.computername -match $machinename} |ForEach-Object{
$DRACip = $_.iPiDRAC
$DRACpw=cscript c:\PassGen1.vbs $machinename
$DRACpw=$DRACpw[3]
$DRACip
$machinename
$DRACPW
$openIDRAC="http://"+$DRACip+"/console"
$openIDRAC
start $openIDRAC
write-host "----------"
}
You are mixing output and write-host which could get you into trouble so I would remove some of that extra fluff which I will assume is just there for testing. I don't see where you are using $DRACpw or $machinename unless that is somehow what you need to see in order to sign into the DRAC
I think you want a bit of Where-Object.
For this CSV file:
computername,iPiDRAC
S0000A01PX,10.122.2.11
Import the file as you have been:
$file = Import-Csv 'c:\temp\powershell\Branch Server IDRAC.csv'
Ask your question:
$machine = Read-Host 'What is your Server?'
Look for the server name in your $file array, which will output an object representing that line in the CSV, then get its iPiDRAC property:
($file | Where-Object {$_.computername -eq $machine}).iPiDRAC
10.122.2.11