Use a variable from a previous iteration in a future iteration - powershell

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]

Related

PowerShell Test If a Filename From a List Exists Somewhere In a Directory and Export Missing

I've researched this and haven't been able to come up with a solid solution. Basically, I have a separate hard drive containing thousands of music files. I have a CSV list with the names of all the files that should be in the hard drive. Example:
My List
I want to be able to test if each of the files on my list exist in the hard drive, and if not, export it to a separate "missing files" list. The thing is each of the files in the hard drive exist under multiple folders.
As my script is now, I am trying to test if the path exists by using join-path. Here is my code right now - it's returning all of the files in the directory instead of just the missing files:
$documents = 'C:\Users\Me\Documents\ScriptTest'
$CSVexport = 'C:\Users\Me\Documents\ScriptTest\TestResults.csv'
$obj = #()
Write-host "`n_____Results____`n" #Write the results and export to a CSV file
$NewCsv = Import-CSV -Path 'C:\Users\Me\Documents\ScriptTest\Test.csv' |
Select-Object ID,'File Path' |
ForEach-Object {
if (!(Test-Path (Join-Path $documents $_.'File Path'))){
write-host "`n$($_.'File Path') is missing from the folder.`n"
$ObjectProperties = #{
ID = $_.ID
'File Path' = $_.'File Path'
}
$obj += New-Object PSObject -Property $ObjectProperties
}
}
$obj | export-csv $CSVexport -NoTypeInformation
How do I account for the sub-directories that vary with each file?
Edit - Resolved
$myFolder = 'C:\Users\Me\Documents\ScriptTest'
$CSVexport = 'C:\Users\Me\Documents\ScriptTest\Results.csv'
$csvPath = 'C:\Users\Me\Documents\ScriptTest\Test.csv'
$FileList = Get-ChildItem $myFolder -Recurse *.wav | Select-Object -ExpandProperty Name -Unique
Import-CSV -Path $csvPath |
Where-Object {$FileList -notcontains $_.'File Path'} |
export-csv $CSVexport -NoTypeInformation
You could generate a list of filenames from the recursed folders, then check if the file is in that list.
$documents = 'C:\Users\Me\Documents\ScriptTest'
$CSVexport = 'C:\Users\Me\Documents\ScriptTest\TestResults.csv'
$FileList = Get-ChildItem $documents -Recurse |
Where-Object { -not $_.PSIsContainer } |
Select-Object -ExpandProperty Name -Unique
Import-CSV -Path 'C:\Users\Me\Documents\ScriptTest\Test.csv' |
Where-Object {$FileList -notcontains $_.File} |
Export-CSV $CSVexport -NoTypeInformation
Edit: Answer updated to work with PowerShell 2.0 with suggestions from Bruce Payette and mklement0

Generating array from multiple folder paths

Alright, I am confusing my self on how to get this to work. I have several folder locations that have multiple sub-folders. These sub-folders are all named after hosts on our network. So I am working on a script to validate the contents of the folders for auditing purposes. I can not seem to generate a usable list that has the system name derived from the folder name and the full path name. I.G.
Name Path
---- ----
system1 \\path\rootfolder1\system1
system2 \\path\rootfolder1\system2
system3 \\path\rootfolder2\system3
I get the root folders from a CSV file as the folders are not all in one location and I do not need to use every folder time I run the report.
#Path to folder repository. Folder names must be the systems host name.
$list_paths = (Import-Csv 'C:\CVS\path\Paths.csv').path
#list arrays
$list = #()
$list2= #()
#Counters
$p_count = 0
$l_count = 0
#Generates array (list) of folder paths
Foreach ($p1 in $list_paths){
$l_count ++
$listx1 = Get-ChildItem $p1 | Where-Object {$_.PSIsContainer} | Foreach-object {$_.FullName}
$list += $listx1
}
#Generates array (list) of system names from folder
ForEach ($p2 in $list){
$p_count ++
Write-Host $p2
$listx2 = split-path -path $p2 -leaf
$list2 += $listx2
}
$Output = New-Object PSObject -Property #{
"Name" = $list
"Path" = $list2
}
Write-Host ($Output | Format-table | Out-String)
Write-Host Number of root folders
Write-Host $l_count
Write-Host Number of host folders
Write-Host $p_count'
So when I run the script $output produces this instead of the format I want above.
Name
----
{\\path\rootfolder1\system1, \\path\rootfolder2\system2, \\path\root...}
I know I am doing something wrong but I can seem to figure out what.
You only create one object with every name and every path as the property-values, not one "Name + Path"-object per system-folder like your first example. Also, you're mixing your lists, so the paths ended up in the Name-property.
Try moving the New-Object job inside a foreach-loop that processes the system-folders. I'd also recommend readable variable-names.
#Path to folder repository. Folder names must be the systems host name.
$rootpaths = (Import-Csv 'C:\CVS\path\Paths.csv').path
#Systems found
$systems = #()
#Find system-folders inside each root
Foreach ($root in $rootpaths){
Get-ChildItem $root | Where-Object { $_.PSIsContainer } | Foreach-object {
#Foreach system-folder, generate a result object
$systems += New-Object PSObject -Property #{
#No need to split the path. The object already contains the leaf-name in the Name-property
"Name" = $_.Name
"Path" = $_.FullName
}
}
}
#No need for write-host if you're writing everything as strings anyways
$systems | Format-table | Out-String
"Number of root folders: $($rootpaths.Count)"
"Number of host folders $($systems.Count)"
As mentioned by #TheMadTechnician, if use the pipeline for what it's worth, this can actually be shortened down to:
#Path to folder repository. Folder names must be the systems host name.
$rootpaths = (Import-Csv 'C:\CVS\path\Paths.csv').path
#Find system-folders inside each root
$systems = Get-ChildItem -Path $rootpaths | Where-Object { $_.PSIsContainer } | Select-Object Name, FullName
#Or this if you have Powershell 3.0+
#$systems = Get-ChildItem -Path $rootpaths -Directory | Select-Object Name, FullName
#No need for write-host if you're writing everything as strings anyways
$systems | Format-table | Out-String
"Number of root folders: $($rootpaths.Count)"
"Number of host folders $($systems.Count)"

Why doesn't the ForEach-Object process all of the share names in my folder security analysis PowerShell script?

I am fairly new to PowerShell, and wrote a script to analyze network shares, dump it to CSV and import it to SQL. Our NAS device has several hidden shares, so I specify the (NAS) server name, a string of share names to search, and the folder depth that I want to search. (like 3 or 4 levels for quick testing).
The script tries to convert the security permissions to show simple "List, Read or Modify" access to folders. Can this user/group "list" the files, view the files, or modify them? The user info is put into a comma-separated list for each access type.
I suspect that although the code is functional, it may not be very efficient and I wonder if there are some significant improvements that could be made?
To deal with long pathnames, I use the "File System Security PowerShell Module 3.2.3" which appends a "2" to several modules, like "Get-ChildItem2".
I used to just specify one share folder, and I'm also wondering if my For-Each-Object that processes multiple shares has introduced a bug in how the objects are handled. It seems to use a lot more memory and slows down, and doesn't seem to process the last share in the list properly.
Here is the code: (split into 3 pieces)
# This script reads through the specified shares on the server and creates a CSV file containing the folder information
# The data is written to a SQL server
$Server = '\\MyServer'
$Shares = 'data$,share$'.Split(',')
$Levels = 99 # specify 3 or 4 for faster testing with less info
$ScanDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$CSVFile = 'C:\FolderInfo\' + $ScanDate.Replace(':','-') + '.csv'
Write-Debug "ScanDate will be set to: $ScanDate"
Write-Debug "Data will be written to: $CSVFile"
$Separator = ',' # Separate the AD groups
$ListRights = 'ListDirectory,GenericExecute'.Split(',')
$ReadRights = 'Read,ReadAndExecute,GenericRead'.Split(',')
$ModifyRights = 'CreateFiles,AppendData,Write,Modify,FullControl,GenericAll'.Split(',')
$ErrorPref = 'Continue'
$ErrorActionPreference = $ErrorPref
$DebugPreference = 'Continue'
$DataBase = 'Folders.dbo.FolderInfo'
Function Get-Subs {
Param([String]$Path,[Byte]$Depth)
$CurrentDepth = $Path.Length - $Path.Replace('\','').Length
new-object psobject -property #{Path=$Path} # Object 'Path' is for the pipe output
If ( $CurrentDepth -lt ($Depth + 1) ) {
Get-ChildItem2 -Path $Path -Directory | ForEach {
Get-Subs $PSItem.FullName $Depth }
}
}
The next line has a commented out line of code that I was using to test how it is processing multiple share names, and it works properly, but the remaining code below seems to mess up on the last sharename in the list.
$Shares | ForEach-Object {Get-Subs (Resolve-Path $Server\$_).ProviderPath $Levels} | Get-Item2 | #ForEach-Object { new-object psobject -property #{Path=$_.FullName} } | Select Path
And the remaining code: (I hope this breakage doesn't confuse everyone :)
ForEach-Object {
$ListUsers = #()
$ReadUsers = #()
$ModifyUsers = #()
$Folder = $PSItem.FullName
Write-Debug $Folder
$Inherited = $true
try {$Owner = (Get-NTFSOwner -Path $Folder).Owner.AccountName.Replace('MyDomain\','')
}
catch {Write-Debug "Access denied: $Folder"
$Owner = 'access denied'
$Inherited = $false
}
$Levels = $Folder.Length - $Folder.Replace('\','').Length - 3 # Assuming \\server\share as base = 0
Get-NTFSAccess $Folder | Where { $PSItem.Account -ne 'BUILTIN\Administrators' } | ForEach-Object {
$Account = $PSItem.Account.AccountName.Replace('MyDomain\','')
$Rights = $PSItem.AccessRights -split(',')
If ($PSItem.IsInherited -eq $false) {$Inherited = $false}
IF ($PSItem.InheritanceFlags -eq 'ContainerInherit') { # Folders only or 'ContainerInherit, ObjectInherit' = Folders and Files
If (#(Compare -ExcludeDifferent -IncludeEqual ($ListRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ReadRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ModifyRights)($Rights)).Length -and $Account) {$ListUsers += $Account
Write-Debug "Modify anomaly found on Container only: $Account with $Rights in $Folder"
}
}
Else {
If (#(Compare -ExcludeDifferent -IncludeEqual ($ListRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ReadRights)($Rights)).Length -and $Account) {$ReadUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ModifyRights)($Rights)).Length -and $Account) {$ModifyUsers += $Account}
}
}
$FileCount = Get-ChildItem2 -Path $Folder -File -IncludeHidden -IncludeSystem | Measure-Object -property Length -Sum
If ($FileCount.Sum) {$Size = $FileCount.Sum} else {$Size = 0}
If ($FileCount.Count) {$NumFiles = $FileCount.Count} else {$NumFiles = 0}
$ErrorActionPreference = 'SilentlyContinue'
Remove-Variable FolderInfo
Remove-Variable Created, LastAccessed, LastModified
$ErrorActionPreference = $ErrorPref
$FolderInfo = #{} # create empty hashtable, new properties will be auto-created
$LastModified = Get-ChildItem2 -Path $Folder -File | Measure-Object -property LastWriteTime -Maximum
IF ($LastModified.Maximum) {$FolderInfo.LastModified = $LastModified.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.LastModified = $PSItem.LastWriteTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$LastAccessed = Get-ChildItem2 -Path $Folder -File | Measure-Object -property LastAccessTime -Maximum
IF ($LastAccessed.Maximum) {$FolderInfo.LastAccessed = $LastAccessed.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.LastAccessed = $PSItem.LastAccessTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$Created = Get-ChildItem2 -Path $Folder -File | Measure-Object -Property CreationTime -Maximum
IF ($Created.Maximum) {$FolderInfo.Created = $Created.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.Created = $PSItem.CreationTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$FolderInfo.FolderName = $Folder
$FolderInfo.Levels = $Levels
$FolderInfo.Owner = $Owner
$FolderInfo.ListUsers = $ListUsers -join $Separator
$FolderInfo.ReadUsers = $ReadUsers -join $Separator
$FolderInfo.ModifyUsers = $ModifyUsers -join $Separator
$FolderInfo.Inherited = $InheritedFrom
$FolderInfo.Size = $Size
$FolderInfo.NumFiles = $NumFiles
$FolderInfo.ScanDate = $ScanDate
Write-Debug $Folder
Write-Output (New-Object –Typename PSObject –Prop $FolderInfo)
} | Select FolderName, Levels, Owner, ListUsers, ReadUsers, ModifyUsers, Inherited, Size, NumFiles, Created, LastModified, LastAccessed, ScanDate |
ConvertTo-csv -NoTypeInformation -Delimiter '|' |
ForEach-Object {$PSItem.Replace('"','')} |
Out-File -FilePath $CSVFile -Force
Write-debug 'Starting import...'
$Query = #"
BULK INSERT $DataBase FROM '$CSVFile' WITH (DATAFILETYPE = 'widechar', FIRSTROW = 2, FIELDTERMINATOR = '|', ROWTERMINATOR = '\n')
"#
sqlcmd -S MyComputer\SQLExpress -E -Q $Query
Arrays can be defined by comma separated list. Each element belongs in quotes.
For $Shares = 'data$,share$'.Split(','), try, $Shares = 'data$','share$'
Similarly for most of your arrays where you used .Split(',')
The filename can be done in many, many ways. Here you use a custom format then change it immediately. Recommend you replace
$ScanDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$CSVFile = 'C:\FolderInfo\' + $ScanDate.Replace(':','-') + '.csv'
With
$ScanDate = Get-Date -Format 'yyyy-MM-dd-HH-mm-ss'
$CSVFile = "C:\FolderInfo\$ScanDate.csv"
This uses custom date format to set to what you wanted without the extra operation AND leverages PS way of evaluating variables within strings if the string is in double quotes. YMMV, but I also prefer 'yyyyMMdd-HHmmss' for datestamps.
Why are you defining a variable only to use it to define a second variable?
$ErrorPref = 'Continue'
$ErrorActionPreference = $ErrorPref
Why not $ErrorActionPreference = 'Continue'?
I found it later. Could probably use an explanation of what you're doing and how to do it when you define your preference. e.g. # Set default errorAction to 'Continue' during development, to show errors for debugging. Change this to 'silentlycontinue' to ignore errors during run. This will really help when you come back to this script in 18 months and are like WTF does this do?
Also, research Advanced Functions and CmdletBinding() so that you can build your function like a commandlet, including inputting a -debug switch, so you can write with debugging in mind.
What does Function Get-Subs actually do? It looks like some kind of recursion to get the path using the custom commandlet get-childitem2. Do you need the full path? Get-ChildItem $path -Directory -Recurse | select fullname where path is your UNC path or local path or any other provider, really.
Get-NTFSOwner not sure where this comes from, perhaps your custom module. You can use Get-ACL in Powershell 3 (not sure about 2, I don't remember). $owner = (Get-ACL 'path\file.ext').Owner.Replace('mydomain\','')
No idea what you're doing with all the inheritance stuff. Just keep in mind that you can get paths from Get-ChildItem | select Fullname and owner from Get-ACL. These may allow you to skip the custom module.
For:
$FileCount = Get-ChildItem2 -Path $Folder -File -IncludeHidden -IncludeSystem | Measure-Object -property Length -Sum
If ($FileCount.Sum) {$Size = $FileCount.Sum} else {$Size = 0}
If ($FileCount.Count) {$NumFiles = $FileCount.Count} else {$NumFiles = 0}
Use:
$files = Get-ChildItem -Force -File -Path $folder
-Force shows all files. -File limits it to files only. Then the number of files is $files.count. Works with empty folders, folders with one hidden file, and files with hidden and normal files.
For $folderinfo consider using a custom object. If you create it within the loop it should destroy the previous one. Then you can assign values directly to the object instead of storing them in a variable then inserting the variable into the hash table.
Using Get-ChildItem native will help you maintain this script far more easily than your customized module.
For:
| Select FolderName, Levels, Owner, ListUsers, ReadUsers, ModifyUsers, Inherited, Size, NumFiles, Created, LastModified, LastAccessed, ScanDate |
ConvertTo-csv -NoTypeInformation -Delimiter '|' |
ForEach-Object {$PSItem.Replace('"','')} |
Out-File -FilePath $CSVFile -Force
Try:
| Export-CSV -NoTypeInformation $CSVFile # by default this will overwrite, but you're timestamping down to the second so I don't think this will be an issue.
Overall:
Get rid of the custom module, I think everything you're doing here can be done in native PowerShell.
Consider recursion
Use an advanced function, complete with documentation. Ref: https://technet.microsoft.com/en-us/magazine/hh360993.aspx
Definitely use a custom object to store data for each file/folder. Passing a collection of objects to Export-CSV produces excellent output.
pipe to Select -ExpandProperty when an object contains a hashtable or another object, e.g. Get-ACL 'file.txt' | select -ExpandProperty Access gives a list of the access rule objects in the ACL.
Get-Help and Get-Member are amongst the most powerful commands in PowerShell.
Select-Object and Where-Object are up there too.

read multiple lines from text file and added it to registry

Very very new to PowerShell
First off all I'd like to give the scenario what I'm trying to do.
We have a SharePoint site and users can add their OneNote books to this SharePoint site. The URL's to the SharePoint site is now changing. So I'm exporting the registry entries to a text file, change the part of the path to the new one, and add the new URL's to the registry.
The problem I'm having is when I export the URL's (because of the way I'm exporting for sure) the text file has a string called "Value" and the URL's are under it.
So the first question is how do I write the values to a text file where only the values get written (in this case the URL's)
And second question is how do I write these changed values back to the registry?
Every URL is a new "string" and the names start with 1, 2, 3 and so on.
Thank you everyone for their time in advance.
# Create a new folder if not exist file
$Folder = "C:\backup"
if(-not(Test-Path $Folder)){
New-Item -Path $Folder -ItemType Directory
}
# Start Logging
Start-Transcript -Path "C:\backup\onenote.log"
#Set Variables
$OneNoteBooks = "C:\backup\onenotenotebooks.txt"
$NewPath = '//newpath.com/'
# Delete the existing file if exists
If (Test-Path $OneNoteBooks){Remove-Item $OneNoteBooks}
# Create a new text file
New-Item -Path $OneNoteBooks -ItemType File
# Exporting OneNote SharePoint Notebooks and Correcting them to the new URL
Write-Host "Exporting OneNote SharePoint Notebooks and Correcting them to the new URL"
Push-Location
Set-Location 'HKCU:\Software\Microsoft\Office\14.0\Onenote\opennotebooks'
Get-Item . | Select-Object -ExpandProperty Property | ForEach-Object {
New-Object PSobject -Property #{"property"=$_;
"Value" = (Get-ItemProperty -Path . -Name $_).$_}} | Format-Table Value -AutoSize | Out-File $OneNoteBooks
Pop-Location
$ReplaceURL = Get-Content -Path $OneNoteBooks
ForEach-Object{
$ReplaceURL -replace "`//.*?(`/)", $NewPath | Out-File $OneNoteBooks
}
# Add Changed URL's to the registry
ForEach-Object {
New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion -Name PowerShellPath -PropertyType String -Value $PSHome
# Stop Logging
Stop-Transcript
Where to start? How about with the fact that in your ForEach loop you create an object with two properties, and then use Format-Table (alias FT used), and select one of the two properties making the whole object pointless... instead just get and output the value that you want.
Get-Item . | Select-Object -ExpandProperty Property | ForEach-Object {Get-ItemProperty -Path . -Name $_).$_} | Out-File $OneNoteBooks
Or better yet, use the built in functions of the Item from your Get-Item to get and set your values in a ForEach loop. Try this out...
# Create a new folder if not exist file
$Folder = "C:\backup"
if(-not(Test-Path $Folder)){
New-Item -Path $Folder -ItemType Directory
}
# Start Logging
Start-Transcript -Path "C:\backup\onenote.log"
#Set Variables
$OneNoteBooks = "C:\backup\onenotenotebooks.txt"
$NewPath = '//newpath.com/'
$OldPath = '//oldpath.com/'
$SitesChangingMask = "$([regex]::escape("$oldpath"))(nibremv|finance|sites/procurement|Comm|Departments/NITAS|ro|nitd|cnibr|communities/sig)/?"
# Delete the existing file if exists
If (Test-Path $OneNoteBooks){Remove-Item $OneNoteBooks}
# Create a new text file
New-Item -Path $OneNoteBooks -ItemType File
# Exporting OneNote SharePoint Notebooks and Correcting them to the new URL
"Exporting OneNote SharePoint Notebooks and Correcting them to the new URL"
Push-Location
Set-Location 'HKCU:\Software\Microsoft\Office\14.0\Onenote\opennotebooks'
$OneNoteReg = Get-Item .
$OneNoteReg.Property | ForEach-Object {
$CurrentValue = $OneNoteReg.GetValue($_)
$NewValue = if($CurrentValue.ToString() -match $SitesChangingMask){$CurrentValue.ToString() -replace "$OldPath", $NewPath}else{$CurrentValue.ToString()}
Set-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\Onenote\opennotebooks' -Name $_ -Value $NewValue
[PSCustomObject][Ordered]#{
"Value Name"=$_
"Original Value"=$CurrentValue
"Updated Value"=$NewValue
}
} | FT -AutoSize | Out-File $OneNoteBooks
Pop-Location
# Stop Logging
Stop-Transcript
I also changed your RegEx pattern for your replace since the old one didn't seem right. For example, in HTTP://www.microsoft.com/powershell it would update that string to read http://www.microsoft.com//newsite.com/powershell and I don't think that was your intention.
When saving data to a file, skip using the Format commands. You could use a CSV file here instead:
Get-Item . | Select-Object -Exp Property | ForEach-Object {
New-Object PSCustomObject -Property #{"Property"=$_;"Value"=(Get-ItemProperty . -Name $_).$_}} |
Export-Csv $OneNoteBooks
Modify the file, then re-read it like so:
$props = Import-Csv $OneNoteBooks
Then you can access the property names and values like so:
$props.Property[0]
$props.Value[0]
..
Of course, you don't really need to go to a file to modify the values. You could do it in memory:
$props = Get-Item . | Select-Object -Exp Property | ForEach-Object {
New-Object PSCustomObject -Property #{"Property"=$_;"Value"=(Get-ItemProperty . -Name $_).$_}}
$props = $props | Foreach {$_.Value = $_.Value -replace 'pattern','replacement'}
If you would prefer to create a .reg file that you can then import try this:
"Windows Registry Editor Version 5.00`n" > c:\onenotebooks.reg
"[$($pwd.ProviderPath)]" >> c:\onenotebooks.reg
Get-Item . | Select-Object -Exp Property |
ForEach-Object { "`"$_`"=`"$((Get-ItemProperty . -Name $_).$_)`"" } >> c:\onenotebooks.reg
The problem is in this line of code.
"Value" = (Get-ItemProperty -Path . -Name $_).$_}} | Format-Table Value -AutoSize | Out-File $OneNoteBooks
I'd suggest removing the format-table. If its not showing you everything then try this:
"Value" = (Get-ItemProperty -Path . -Name $_).$_}} | Foreach-Object {$_.Value} | Out-File $OneNoteBooks

PowerShell - exporting variable to csv file

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