I'm new to powershell. Here is a script I am trying to write that, in a nutshell, will pull computers from AD into a variable. Take the list and iterate through each one and do the following:
test connection (online, offline)
test OSArchitecture
test for a subkey
write out the computer, connection status, subkey value to csv file
Every time I try to output to a file, whether out-file or export-csv, it doesn't come out correctly. it either puts 'Length' and value or some long string. I would ideally like to export to a csv so I can manipulate the data. The function 'DoesItemExist' is a funchtion to test if a registry exists. Here's the code:
#find computers names that start with H or D
$computers=Get-ADComputer -Filter {(name -like "H*") -or (name -like "D*")} -Properties
Name | select name
#save path to csv file
$SaveAs = 'c:\msolhelp\edocs.csv'
#loop through the computers collection
foreach($line in $computers)
{
#test if computer is online
if(Test-Connection -Cn $line -BufferSize 16 -Count 1 -ea 0 -quiet)
{
#write computer name to file
#test registry path for 64 bit machine
$OS=((Get-WmiObject Win32_Operatingsystem -ComputerName $line).OSArchitecture)
if ($OS = '64-bit')
#if 64 bit check for correct regkey and grab value and appends to file
{
$regpath = 'SOFTWARE\Wow6432Node\'
#check for subkey and append value to file
$val = (DoesItemExist -path $regpath -regEntry "PatchLevel")
If ($val) {
Get-ItemProperty "HKLM:SOFTWARE\Wow6432Node\" | Select-
Object -Property PatchLevel | Export-Csv -Path $SaveAs -Append -
NoTypeInformation }
else {Get-ItemProperty "HKLM:SOFTWARE\Wow6432Node\" |
Select-Object -Property CurrentVersion | Export-Csv -Path $SaveAs -Append -
NoTypeInformation}
}
#if false, it must be a 32 bit machine (different registry path)
else
{
#set path for 32 bit machine
$regpath = 'HKLM\SOFTWARE\'
#check for correct subkey
$val = (DoesItemExist -path $regpath -regEntry "PatchLevel")
If ($val) {
Get-ItemProperty "HKLM:SOFTWARE\" | Select-Object -
Property PatchLevel | Export-Csv -Path $SaveAs -Append -NoTypeInformation }
else {
Get-ItemProperty "HKLM:SOFTWARE\" | Select-Object -
Property CurrentVersion | Export-Csv -Path $SaveAs -Append -
NoTypeInformation}
}
}
#if test-connect fails, append file with 'offline'
else {
"$line, offline"
continue
}
}
From what I can see you will want to save the variable to a text file, and then import-CSV later. Export-CSV doesn't actually do what you think it will...I suggest you read the link below..Export-CSV treats each item as a new row of data, which is why it doesn't look right. THE CSVs are determined by the object properties.
See documentation here:
http://blogs.technet.com/b/heyscriptingguy/archive/2011/09/23/use-powershell-to-work-with-csv-formatted-text.aspx
Related
I have a Powershell script which I've cobbled together. It uses an external file as a lookup then checks the LastWriteTime of those files.
This was created as a checking procedure. To ensure a set of files had been updated each day.
However, I've noticed that if the files don't exist at run time, they don't show in the list at all. So there's potential for these to be missed.
As well as checking the LastWriteDate, is there a way this can be altered to highlight in some way if any of the files don't exist?
Either a new column saying Exists Y/N?
Or even a total row count VS expected row count?
This is what I've got so far...
#Filelist - This is a simple txt file with various filepaths entered
$filelist = Get-Content "H:\PowerShell\Files_Location_List.txt"
$results = foreach ($file in $filelist) {
Get-Item $file | select -Property fullname, LastWriteTime #| Measure-Object
}
$results | Export-Csv 'H:\PowerShell\File_Modified_Dates.csv' -NoTypeInformation #| Measure-Object
The contents of Files_Location_List.txt is very simple...
\server\folder\file1.csv
\server\folder\file2.csv
\server\folder\file3.csv
etc
you can try using Test-Path
if(Test-Path -Path <file_path>) {
# do stuff
}
You can also use -ErrorAction SilentlyContinue on Get-Item to either get a FileInfo object if the file exists or $null if not:
# Filelist - This is a simple txt file with various filepaths entered
$result = Get-Content "H:\PowerShell\Files_Location_List.txt" | ForEach-Object {
# try and get the FileInfo object for this path. Discard errors
$file = Get-Item -Path $_ -ErrorAction SilentlyContinue
if ($file) {
$file | Select-Object -Property FullName, LastWriteTime, #{Name = 'Exists'; Expression = {$true}}
}
else {
[PsCustomObject]#{
FullName = $_
LastWriteTime = $null
Exists = $false
}
}
}
$result | Export-Csv 'H:\PowerShell\File_Modified_Dates.csv' -NoTypeInformation
I want to create a script which reads all the computernames from a CSV file. And from all of these, I want the description. Also it should be exported in a single CSV.
This is what I tried but...
$path = Split-Path -Parent $MyInvocation.MyCommand.Definition
$path_import_csv = $path + "\" + "Computernamen.csv"
$path_export_csv = $path + "\" + "Alessio.csv"
$computernames = Import-Csv $path_import_csv
foreach ($computername in $computernames) {
Get-ADComputer -SearchBase "OU=1,OU=2,OU=3,DC=my,DC=domain" -Properties * |
Select -Expand description |
Export-Csv -Path $path_export_csv -Append -Delimiter ";" -NoTypeInformation -Force
}
From your comment I gather that file Computernamen.csv is not a CSV file at all, but just a text file with computer names each on a separate line.
In that case, you do not use Import-Csv, but Get-Content to retrieve an array of computer names.
Also (others already made that clear) you are not using the $computername variable in the foreach loop at all AND by adding the -ExpandProperty switch to the Select-Object cmdlet, you are not receiving an object with property Description, but just the description as string .
For outputting a CSV with Export-Csv, you need to have a (series of) objects.
Also, I would recommend using the Join-Path cmdlet for creating file paths instead of contatenating string with + so you don't have to worry about possible missing backslashes.
Instead of $MyInvocation.MyCommand.Definition you can also use the $PSScriptRoot variable to get the current script path.
In Windows PowerShell 2.0, this variable is valid only in script modules (.psm1).
Beginning in Windows PowerShell 3.0, it is valid in all scripts.
$path = Split-Path -Parent $MyInvocation.MyCommand.Definition # or use $PSScriptRoot
$path_import_csv = Join-Path -Path $path -ChildPath 'Computernamen.csv'
$path_export_csv = Join-Path -Path $path -ChildPath 'Alessio.csv'
$computernames = Get-Content $path_import_csv
$searchBase = 'OU=1,OU=2,OU=3,DC=my,DC=domain'
$result = foreach ($computername in $computernames) {
# it is bad practice to use -Properties * if all you need is a small set of properties
$computer = Get-ADComputer -Filter "Name -eq '$computername'" -SearchBase $searchBase -Properties Name, Description -ErrorAction SilentlyContinue
# did we find a computer by that name?
if ($computer) {
# output an object with the two selected properties to get collected in the $result variable
$computer | Select-Object Name, Description
}
else {
Write-Host "A computer with name '$computername' does not exist."
}
}
# output the result on console
$result | Format-Table -AutoSize
# save the result as proper CSV file
$result | Export-Csv -Path $path_export_csv -Delimiter ';' -NoTypeInformation -Force
Is it possible to use $SecretFolder from the else statement in future Iterations if the company is the same. E.g. Multiple users exist on the list from one company but they all need to have a link generated for 1 folder for the company to access.
#Location of original dataset
$csv = Import-Csv c:\export.csv
#loops through every line of the csv
Foreach ($line in $csv){
#Generate random folder name (8 Characters long)
$SecretFolder = -join ((48..57) + (97..122) | Get-Random -Count 8 | % {[char]$_})
#Create URL
$url = "www.website.com.au/2017Rates/$SecretFolder"
#Test: Has the company already had a folder created
if (Get-Variable $line.CompanyName -Scope Global -ErrorAction SilentlyContinue)
{
#Append URL to CSV for a person who already has a company folder
$report =#()
$report += New-Object psobject -Property #{CompanyName=$line.CompanyName;FirstName=$line.FirstName;LastName=$line.LastName;EmailAddress=$line.EmailAddress;'Letter Type'=$line.'Letter Type';URL=$URL}
$report | export-csv testreporting.csv -Append
}
else
{
#Create Folder with Random Cryptic name
mkdir C:\Users\bford\test\$SecretFolder
#Copy item from FileLocation in CSV to SecretFolder Location
Copy-Item -Path $line.FileLocation -Destination c:\users\bford\test\$SecretFolder -Recurse -ErrorAction SilentlyContinue
#Create Variable for Logic test with the Name CompanyName
New-Variable -Name $line.CompanyName
#Append csv with the updated details
$S_report =#()
$S_report += New-Object psobject -Property #{CompanyName=$line.CompanyName;FirstName=$line.FirstName;LastName=$line.LastName;EmailAddress=$line.EmailAddress;'Letter Type'=$line.'Letter Type';URL=$url}
$S_report | export-csv testreporting.csv -Append
}
}
#Cleanup remove all the variables added
Remove-Variable * -ErrorAction SilentlyContinue
Do you have any reason to think it's impossible? Yeah it's possible, you should Google hashtables and find that they do everything you're trying to do with get-variable, only way better.
But your question amounts to "how do I rewrite my script so it works?" and rewriting your script to me means getting rid of the duplicate #()+= triple lines, the mystery numbers, the global variables, and the extra variables and the if/else, and it ends up a completely different script altogether.
A completely different, and mostly untested, script:
# import and group all people in the same company together
# then loop over the groups (companies)
Import-Csv -Path c:\export.csv |
Group-Object -Property CompanyName |
ForEach-Object {
# This loop is once per company, make one secret folder for this company.
$SecretFolder = -join ( [char[]]'abcdefghijklmnopqrstuvwxyz1234567890' | Get-Random -Count 8 )
New-Item -ItemType Directory -Path "C:\Users\bford\test\$SecretFolder"
# Loop through all the people in this company, and copy their files into this company's secret folder
$_.Group | ForEach-Object {
Copy-Item -Path $_.FileLocation -Destination c:\users\bford\test\$SecretFolder -Recurse -ErrorAction SilentlyContinue
}
# Output each person in this company with just the properties needed, and a new one for this company's URL
$_.Group | Select-Object -Property CompanyName , FirstName,
LastName, EmailAddress, 'Letter Type',
#{Name='Url'; Expression={"www.website.com.au/2017Rates/$SecretFolder"}}
} | Export-Csv -Path testreporting.csv -NoTypeInformation
But to edit your script to do what you want, use a hashtable, e.g.
$SecretFolders = #{} #at top of your script, outside loops
# in loops:
if (-not $SecretFolders.ContainsKey($line.CompanyName))
{
$SecretFolders[$line.CompanyName] = -join (random name generation here)
}
$SecretFolder = $SecretFolders[$line.CompanyName]
I need to find out if folder named Backupeventlog exists on the server. If yes then which drive C: or D: drive. Also is this folder empty and if not empty what is the date of latest file that has been created under this folder.
Below is the script that I was working, but it doesn't seem to work. :(
$a = Get-Content C:\Lists\Servers.txt
foreach ($Servers in $a)
{
if (Test-Connection $Servers -Count 1 -ea 0 -Quiet)
{
if ("Test-Path \\$Servers\D$\Backupeventlog\*" -or "Test-Path \\$Servers\C$\Backupeventlog\*")
{
$latest = Get-ChildItem \\$Servers\C$\Backupeventlog -Force |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
Write-Host $Servers - Backupeventlog exists in C: drive and latest file in folder is of date $latest.Lastwritetime
$latest1 = Get-ChildItem \\$Servers\D$\Backupeventlog -Force |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
Write-Host $Servers - Backupeventlog exists in D: drive and latest file in folder is of date $latest1.Lastwritetime
}
else
{
Write-Host $Servers - Backupevent folder does not exist or empty
}
}
Else
{
Write-Host $Servers - not pinging.
}
}
Do individual checks, and don't put the entire command in double quotes (otherwise PowerShell will just echo the command string without actually executing the command):
if (Test-Path \\$Servers\C$\Backupeventlog\*) {
$latest = Get-ChildItem \\$Servers\C$\Backupeventlog -Force |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
Write-Host 'drive C:'
} elseif (Test-Path \\$Servers\D$\Backupeventlog\*) {
$latest = Get-ChildItem \\$Servers\D$\Backupeventlog -Force |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
Write-Host 'drive D:'
} else {
Write-Host 'not found'
}
If the backup folder can only exist on one of the drives and you can neglect information about servers that have neither you could also do a pipeline like this to get just the latest file from each server where the backup folder exists:
Get-Content C:\Lists\Servers.txt |
Where-Object { Test-Connection $_ -Count 1 -Quiet } |
ForEach-Object {
Get-ChildItem "\\$_\C$\Backupeventlog\*", "\\$_\D$\Backupeventlog\*" -EA SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 -Expand FullName
}
Since the full path still contains the drive information you can extract the drive letter later if required.
I'm currently working on a PowerShell script that reads out the default printer on several workstations and write the information in a textfile to a network drive. My last question regarding some text replacements inside the script was successfully solved. But now I work on the second part.
Get-WmiObject -Class Win32_Printer -Filter "Default = $true" | % {
$_.Name -replace '(?:.*)\\NDPS-([^\.]+)(?:.*)', 'PS-$1'
} | Out-File -FilePath "H:\daten\printer\$($env:COMPUTERNAME)_defaultprinter.txt"
Get-WmiObject Win32_Printer -Filter "Default = $true" `
| Select-Object -expandProperty Name `
| Out-File -FilePath "P:\batch\migration\Printer\$($env:COMPUTERNAME)_$($env:USERNAME)_defaultprinter.txt"
The last line of the provided code writes the default printer to the network drive. Now I have there nearly 1500 single txt-files. For better analysis I use the following PowerShell script to merge all the single txt files to one big file.
Get-ChildItem -path \\samplepath\prgs\prgs\batch\migration\Printer -recurse | ? {
! $_.PSIsContainer
} | ? {
($_.name).contains(".txt")
} | % {
Out-File -filepath \\samplepath\prgs\prgs\batch\migration\Printer\gesamt_printer.txt -inputobject (get-content $_.fullname) -Append
}
I receive a file wich contains the default printer information from every txt-file but I need the $($env:USERNAME)-part from the filename as a separate value in addition to the printer information in on line to use the data in Excel. Can someone please provide me a tip how to insert the part from filename in the merged file?
You could extract the username part from the file name like this:
$_.Name -match '^.*?_(.*)_.*?\.txt$'
$username = $matches[1]
The group in the regular expression (accsisible via $matches[1]) contains the text between the first and the last underscore in the filename.
You could use it like this:
$root = "\\samplepath\prgs\prgs\batch\migration\Printer"
$outfile = "$root\gesamt_printer.txt"
Get-ChildItem $root -Recurse | ? {
-not $_.PSIsContainer -and $_.Extension -eq ".txt"
} | % {
$_.Name -match '^.*?_(.*)_.*?\.txt$'
$username = $matches[1]
$content = Get-Content $_.FullName
"{0},{1}" -f ($content, $username) | Out-File $outfile -Append
}
You could also directly create a CSV:
$root = "\\samplepath\prgs\prgs\batch\migration\Printer"
$outfile = "$root\gesamt_printer.txt"
$getPrinter = { Get-Content $_.FullName }
$getUser = { $_.Name -match '^.*?_(.*)_.*?\.txt$' | Out-Null; $matches[1] }
Get-ChildItem $root -Recurse `
| ? { -not $_.PSIsContainer -and $_.Extension -eq ".txt" } `
| select #{n="Username";e=$getUser},#{n="Printer";e=$getPrinter} `
| Export-Csv $outfile -NoTypeInformation
Note that these code sample don't contain any checks to exclude file names that don't have at least two underscores in them.