A couple of you (Shay especially!) helped me yesterday and I have managed to come up with multiple scripts and am now merging them into one.
The problem is that I have put them all into "Functions" and one function relies on the successful check on a hotfix - if the hotfix is not installed then the script has to stop right there and then - that is the first check the script does. How do I get the next function to call the successful output of the hotfix function?
Also I have no idea how to call the functions - ie at the bottom I put the name of the functions one line after each other but ended up looping over and over!
hope someone can assist.
Write-Host "=================================="
Write-Host "Pre-Staging Script for DFSR Server"
Write-Host "=================================="
Function Service
{
Write-Host "=================================="
Write-Host "Checking Service Installation"
Write-Host "=================================="
write-host "This will check if Hotfix KB979808 is installed." -ForegroundColor Black -BackgroundColor Cyan
write-host "This is required for Windows Server 2008 R2 Robocopying" -ForegroundColor Black -BackgroundColor Cyan
Write-Host ""
$hotfix1 = Get-HotFix -id KB979808 -ErrorAction SilentlyContinue
If($hotfix1)
{
Write-Host "Hotfix is installed you may proceed" -foregroundcolor "green"
Write-Host ""
}
else
{
Write-Host "Hotfix is NOT installed - Please ensure you install this hotfix BEFORE" -ForegroundColor "red"
Write-host "Copying any data" -foregroundcolor "red"
Write-Host ""
}
}
Function Robocopy ($hotfix1)
{
Write-Host "============="
Write-Host "Robocopy Data"
Write-Host "============="
$Source = Read-Host "Please enter path of SOURCE"
$Destination = Read-Host "Please enter path of TARGET"
$Output = Read-Host "Please enter where to place output file eg c:\temp\COPY.log"
robocopy $Source $Target /b /e /copyall /r:1 /xd dfsrprivate /log:$Output /tee
}
Function Comparision
{
Write-Host "==============================================="
Write-Host "Checking Directory Count and Folder comparision" -ErrorAction SilentlyContinue -BackgroundColor Cyan -ForegroundColor Black
Write-Host "==============================================="
Write-Host ""
$Source = Read-Host "Please enter Source directory to check"
$Target = Read-Host "Please enter Target directory to check"
Write-Host ""
If($source -and (Test-Path -Path $source -PathType Container))
{
"There are $(#(Get-ChildItem $Source).Count) items in the '$Source' directory"
}
Else
{
Write-Host "Please enter a directory"
}
If($source -and (Test-Path -Path $Target -PathType Container))
{
"There are $(#(Get-ChildItem $Target).Count) items in the '$Target' directory"
}
Else
{
Write-Host "Please enter a directory"
}
Write-Host ""
$child1 = Get-ChildItem -Path $Source -Recurse -Force
$child2 = Get-ChildItem -Path $Target -Recurse -Force
Compare-Object $child1 -DifferenceObject $child2 -Property Name
Write-Host ""
Write-Host "NOTE:" -BackgroundColor Cyan -ForegroundColor Black
Write-Host "Any symbols with '=>' mean that the file Does NOT exist in SOURCE but is in the Target" -BackgroundColor Cyan -ForegroundColor Black
Write-Host "Any symbols with '<=' mean that the file Does NOT exist in TARGET but is in the Source" -BackgroundColor Cyan -ForegroundColor Black
}
To use the hotfix found in the function Service, you should return the hotfix to a variable. The function would look like this:
Function Service
{
Write-Host "=================================="
Write-Host "Checking Service Installation"
Write-Host "=================================="
write-host "This will check if Hotfix KB979808 is installed." -ForegroundColor Black -BackgroundColor Cyan
write-host "This is required for Windows Server 2008 R2 Robocopying" -ForegroundColor Black -BackgroundColor Cyan
Write-Host ""
# This will return any output.
Get-HotFix -id KB979808 -ErrorAction SilentlyContinue
}
Then the function calls would be:
$hotfix = Service
if($hotfix) {
Robocopy
}
else {
# This will exit the script.
return
}
Function Robocopy doesn't need $hotfix1 as a parameter since it's not used anywhere in the function.
The Robocopy function is probably looping because the call to robocopy.exe is the same as your Robocopy function; try adding ".exe" to the robocopy.exe call. It is important to name functions to accurately reflect their purpose. Service could be Get-HotFixKB979808 and Robocopy could be Start-MyRobocopy
Having said all that, since your functions do very specific things, they really don't need to be their own functions. You could change them to be more reusable by having them accept parameters.
Related
I am trying to clear browser data from default location C:\Users\\AppData\Local\Microsoft\Windows\INetCache. I am trying to clear Cookies, Cache or any type files located in that path but I am not sure what am I missing here
screenshot
Write-Host -ForegroundColor yellow "#######################################################"
""
Write-Host -ForegroundColor Green "Powershell commands to delete cache & cookies in Firefox, Chrome & IE browsers"
Write-Host -ForegroundColor Green "By Cesar Silva"
Write-Host -ForegroundColor Green "VERSION: 3"
""
Write-Host -ForegroundColor yellow "#######################################################"
""
Write-Host -ForegroundColor Green "CHANGE_LOG:
v2.4: - Resolved *.default issue, issue was with the file path name not with *.default, but issue resolved
v2.3: - Added Cache2 to Mozilla directories but found that *.default is not working
v2.2: - Added Cyan colour to verbose output
v2.1: - Added the location 'C:\Windows\Temp\*' and 'C:\`$recycle.bin\'
v2: - Changed the retrieval of user list to dir the c:\users folder and export to csv
v1: - Compiled script"
""
Write-Host -ForegroundColor yellow "#######################################################"
""
#########################
"-------------------"
Write-Host -ForegroundColor Green "SECTION 1: Getting the list of users"
"-------------------"
# Write Information to the screen
Write-Host -ForegroundColor yellow "Exporting the list of users to c:\users\%username%\users.csv"
# List the users in c:\users and export to the local profile for calling later
dir C:\Users | select Name | Export-Csv -Path C:\users\$env:USERNAME\users.csv -NoTypeInformation
$list=Test-Path C:\users\$env:USERNAME\users.csv
""
#########################
"-------------------"
Write-Host -ForegroundColor Green "SECTION 2: Beginning Script..."
"-------------------"
if ($list) {
"-------------------"
# Clear Internet Explorer
Write-Host -ForegroundColor Green "SECTION 5: Clearing Internet Explorer Caches"
"-------------------"
Write-Host -ForegroundColor yellow "Clearing Google caches"
Write-Host -ForegroundColor cyan
Import-CSV -Path C:\users\$env:USERNAME\users.csv | foreach {
Remove-Item -path "C:\Users\$($_.Name)\AppData\Local\Microsoft\Windows\INetCache\*.*" -Recurse -Force -EA SilentlyContinue -Verbose
Remove-Item -path "C:\Users\$($_.Name)\AppData\Local\Microsoft\Windows\WER\*" -Recurse -Force -EA SilentlyContinue -Verbose
Remove-Item -path "C:\Users\$($_.Name)\AppData\Local\Temp\*" -Recurse -Force -EA SilentlyContinue -Verbose
Remove-Item -path "C:\Windows\Temp\*" -Recurse -Force -EA SilentlyContinue -Verbose
Remove-Item -path "C:\`$recycle.bin\" -Recurse -Force -EA SilentlyContinue -Verbose
}
Write-Host -ForegroundColor yellow "Done..."
""
Write-Host -ForegroundColor Green "All Tasks Done!"
} else {
Write-Host -ForegroundColor Yellow "Session Cancelled"
Exit
}
Well if any errors are occurring, you are suppressing them with the -EA SilentlyContinue flag. I would remove that from your Remove-Item calls and see if any sneaky errors are hiding there.
I'm not sure if you intended this, but in your first Remove-Item call under INetCache:
Remove-Item -path "C:\Users\$($_.Name)\AppData\Local\Microsoft\Windows\INetCache\*.*"
You are removing only files with a . in the filename, basically only files with extensions. If you want to remove everything from that directory regardless of whether it has a file extension, just use * instead of *.* as you do for your other paths.
I am needing to create a logfile for every action that is performed by this script.
I have attempted to use redirection(>>) however, cannot get the output to actually write to the file.
while($true){
$FromMC = Get-ChildItem -Path "\\x\From MC" -Filter *.mpg
Write-Host "Checking '\\x\From MC' for files" -ForegroundColor Cyan
Write-Host $FromMC.count"Files Found" -ForegroundColor Cyan
Write-Host ""
ForEach($file in $FromMC){
try{
Move-Item -Filter 7ST* -Path $file.Fullname -Destination "\\x\programs\7TH STREET THEATER" -Verbose -Force -ErrorAction Stop
}
catch{...}
Write-Host "Pausing for"$ts.Minutes"minutes..." -ForegroundColor Cyan
Write-Host "Ctrl+C to Stop"
Write-Host ""
Start-Sleep -Seconds $ts.TotalSeconds
}
I expect the output to be exactly as "-verbose" outputs into the shell. Types of output: Write-Host, Verbose, Write-Warning
I feel the solution is extremely simple, and I am just overlooking it.
To log everything that would normally be written to the console, like -verbose you can use Start-Transcript and when you are finished Stop-Transcript.
Example:
Start-Transcript -Path "C:\logs.txt"
#run code you want to capture
Stop-Transcript
I am trying to write a script which takes a source-path as input and copies all files that have been changed since a given date, including their directory structure:
param([string]$source,[string]$datum)
(Get-ChildItem $source -Recurse | Where-Object { $_.LastWriteTime -ge $datum }) | Copy-Item -Destination C:\tmp -Recurse
It works. The problem is, that it copies the files, including their directory structure, BUT it also copies all files from the source directories to the base path of the destination folder.
Where is the error?
First and foremost you should be using robocopy for something like this. It has switches that likely handle your query:
/e Copies subdirectories. Note that this option includes empty directories. For additional information, see Remarks.
/maxage: Specifies the maximum file age (to exclude files older than N days or date).
/l Specifies that files are to be listed only (and not copied, deleted, or time stamped).
So knowing that I would start experimenting with robocopy $source $destination /e /maxage:30 /l which should show all files in the directory tree that were modified in the last 30 days. /maxage might not be the switch you are looking for but its a fair guess.
The issue with your current logic is likely that Get-ChildItem $source -Recurse will be returning folders as well as files. So you could be passing a folder to Copy-Item which is where all the extra files are coming from. You should be able to mitigate that with the -File switch.
Get-ChildItem $source -Recurse -File
Here is an overly complicated solution. It has a lot of error handling with 5 retry attempts. You should be able to apply it toward your needs.
I would look at Line 21 to add your Where-Object and test it.
Example Use: Copy-Files -Source "C:\Path\" -Destination "C:\Path2\"
Function Retry-Command {
$Script:Counter = $Script:Counter + 1
Write-Host "Attempt #" $Script:Counter "out of 5"
if ($Script:Counter -ge 5) {
PAUSE
}
else {
Start-Sleep -Seconds 5 -Verbose
}
}
Function Copy-Files ([string]$Source, [string]$Destination ) {
[System.Collections.ArrayList]$Folder_Content = #()
[string]$Folder_Name = Split-Path $Source -Leaf
[string]$Folder_Path = ($Source -replace [regex]::Escape($Source), ($Destination + $Folder_Name + "\"))
[int]$Script:Counter = 0
while (($Folder_Content.Count -eq "0") -and ($Counter -ne 5)) {
try {
Write-Host ""
Write-Host "------------------------------" -ForegroundColor Cyan
Write-Host "Getting Content of" $Folder_Name -ForegroundColor Yellow
[System.Collections.ArrayList]$Folder_Content = #(Get-ChildItem -Path $Source -Recurse -Force -ErrorVariable Child_Error)
if ((-Not $Child_Error) -and ($Folder_Content.Count -ne 0) ) {
Write-Host "Finished Getting Content of" $Folder_Name -ForegroundColor Green
[int]$Script:Counter = 0
}
else {
Write-Host "Failed to get Content of" $Folder_Name -ForegroundColor Red
Retry-Command
}
}
catch {
Write-Host "Unexpected Error getting Content of" $Folder_Name -ForegroundColor Red
Retry-Command
}
}
if (-Not $Folder_Content.Count -eq 0) {
while ((Test-Path -Path $Folder_Path) -eq $false) {
try {
Write-Host ""
Write-Host "Creating" $Folder_Name "Folder" -ForegroundColor Yellow
Copy-Item -Path $Source -Destination $Folder_Path -Force -ErrorVariable Folder_Error
if (-Not $Folder_Error) {
Write-Host "Finished Creating" $Folder_Name "Folder" -ForegroundColor Green
[int]$Script:Counter = 0
}
else {
Write-Host "Failed to create" $Folder_Name "Folder" -ForegroundColor Red
Retry-Command
}
}
catch {
Write-Host "Unexpected Error Creating" $Folder_Name "Folder" -ForegroundColor Red
Retry-Command
}
}
foreach ($Index in $Folder_Content) {
[string]$Software_File_Name = Split-Path $Index.FullName -Leaf
[string]$Software_File_Path = ($Index.FullName -replace [regex]::Escape($Source), ($Destination + (Split-Path $Source -Leaf) + "\"))
while ((Test-Path -Path $Software_File_Path) -eq $false) {
try {
Write-Host ""
Write-Host "Copying" $Index.FullName -ForegroundColor Yellow
Copy-Item -Path $Index.FullName -Destination $Software_File_Path -Force -ErrorVariable File_Error
if (-Not $File_Error) {
Write-Host "Finished Copying" $Index.FullName -ForegroundColor Green
[int]$Script:Counter = 0
}
else {
Write-Host "Failed to Copy" $Index.FullName -ForegroundColor Red
Retry-Command
}
}
catch {
Write-Host "Unexpected Error Copying" $Index.FullName -ForegroundColor Red
Retry-Command
}
}
}
}
}
I'm writing a script that will modify some registry settings on Windows machines.
I'm using an If to determine if the key/property exist before writing them. It should fail and write an error if it does. This chunk is working:
$currentPath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Ext'
$pathTest = Test-Path $currentPath
Write-Host -ForegroundColor Yellow 'Testing for: ' $currentPath
If($pathTest -ne 'True')
{
New-Item -Path $currentPath -Force | Out-Null
Write-Host -ForegroundColor Green 'Created Path: '$currentPath
}
Else{
Write-Host -ForegroundColor Red $currentPath ' already exists.'
}
Write-Host -ForegroundColor Yellow 'Disabling IE Add-On Performance Notifications (DisableAddonLoadTimePerformanceNotifications):'
If((Get-ItemProperty $currentPath -Name 'DisableAddonLoadTimePerformanceNotifications' -ea 0).'DisableAddonLoadTimePerformanceNotifications')
{
Write-Host -ForegroundColor Red 'DisableAddonLoadTimePerformanceNotifications already exists'
}
Else {
New-ItemProperty -Path $currentPath -Name DisableAddonLoadTimePerformanceNotifications -PropertyType DWord -Value '1' | Out-Null
Write-Host -ForegroundColor Green 'DisableAddonLoadTimePerformanceNotifications created and set to 1'
}
It gives me this output if the key/property exists:
Testing for: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Ext
HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Ext already exists.
Disabling IE Add-On Performance Notifications (DisableAddonLoadTimePerformanceNotifications):
DisableAddonLoadTimePerformanceNotifications already exists
However, this chunk does not work:
$currentPath = 'HKLM:\Software\Policies\Microsoft\Internet Explorer\Main'
$pathTest = Test-Path $currentPath
Write-Host -ForegroundColor Yellow 'Testing for: ' $currentPath
If($pathTest -ne 'True')
{
New-Item -Path $currentPath -Force | Out-Null
Write-Host -ForegroundColor Green 'Created Path: '$currentPath
}
Else{
Write-Host -ForegroundColor Red $currentPath ' already exists.'
}
If((Get-ItemProperty $currentPath -Name 'EnableAutoUpgrade' -ea 0).'EnableAutoUpgrade')
{
Write-Host -ForegroundColor Red 'EnableAutoUpgrade already exists'
}
Else{
New-ItemProperty -Path $currentPath -Name 'EnableAutoUpgrade' -PropertyType DWord -Value '0' | Out-Null
Write-Host -ForegroundColor Green 'EnableAutoUpgrade created and set to 0'
}
Output if the key/property exists:
Testing for: HKLM:\Software\Policies\Microsoft\Internet Explorer\Main
HKLM:\Software\Policies\Microsoft\Internet Explorer\Main already exists.
New-ItemProperty : The property already exists.
At C:\ps\setup\2.ps1:42 char:10
+ New-ItemProperty -Path $currentPath -Name 'EnableAutoUpgrade' -Property ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceExists: (HKEY_LOCAL_MACH...t Explorer\Main:String) [New-ItemProperty], IOException
+ FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.NewItemPropertyCommand
EnableAutoUpgrade created and set to 0
Output of the key/property does not exist:
Testing for: HKLM:\Software\Policies\Microsoft\Internet Explorer\Main
Created Path: HKLM:\Software\Policies\Microsoft\Internet Explorer\Main
Disabling IE Auto Upgrade (EnableAutoUpgrade):
EnableAutoUpgrade created and set to 0
So, it'll create the property no problem, but can't see if it already exists.It looks to me like the If is failing on this one, but I can't figure out why. It's the same as one above it. In the full script I run a total of 14 registry entries. The ones above EnableAutoUpgrade all work. The ones below EnableAutoUpgrade do not and fail with the same error.
This is happening on multiple machines all running Win8.1 and PowerShell v4.
This is a real head scratcher. Any help would be appreciated.
Try this:
$currentPath = "HKLM:\Software\Policies\Microsoft\Internet Explorer\Main"
Write-Host -ForegroundColor Yellow "Testing for: " $currentPath
If(Test-Path "HKLM:\Software\Policies\Microsoft\Internet Explorer\Main")
{
Write-Host $currentPath " already exists." -ForegroundColor 'Red'
}
Else{
New-Item -Path $currentPath | Out-Null
Write-Host "Created Path: "$currentPath -ForegroundColor 'Green'
}
I have the below two functions and I want the SOURCE and TARGET of the ROBOCOPY function used within the Comparision function. Can anyone tell me how to do this please. Thanks.
Write-Host "==================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host "Pre-Staging Script for DFSR Server" -ForegroundColor Magenta -BackgroundColor White
Write-Host "==================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host ""
Function GetHot-Fix
{
Write-Host "==================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host "Checking Service Installation" -ForegroundColor Magenta -BackgroundColor White
Write-Host "==================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host ""
write-host "This will check if Hotfix KB979808 is installed." -ForegroundColor Black -BackgroundColor Cyan
write-host "This is required for Windows Server 2008 R2 Robocopying" -ForegroundColor Black -BackgroundColor Cyan
Write-Host ""
Get-HotFix -id KB979808 -ErrorAction SilentlyContinue
}
Function Start-MyRobocopy($source,$Target)
{
Write-Host "=============" -ForegroundColor Magenta -BackgroundColor White
Write-Host "Robocopy Data" -ForegroundColor Magenta -BackgroundColor White
Write-Host "=============" -ForegroundColor Magenta -BackgroundColor White
Write-Host ""
$Source = Read-Host "Please enter path of SOURCE"
If ($Source -and (Test-Path -Path $Source -PathType Container))
{
$Target = Read-Host "Please enter path of TARGET"
}
Else
{
Write-Host "Please enter a directory"
}
If ($Target -and (Test-Path -Path $Target -PathType Container))
{
$Output = Read-Host "Please enter where to place output file eg c:\temp\COPY.log"
}
Else
{
Write-Host "Please enter a directory"
}
robocopy.exe $Source $Target /b /e /copyall /r:1 /xd dfsrprivate /log:$Output /tee
}
Function Comparision
{
Write-Host ""
Write-Host ""
Write-Host "===============================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host "Checking Directory Count and Folder comparision" -ErrorAction SilentlyContinue -ForegroundColor Magenta -BackgroundColor White
Write-Host "===============================================" -ForegroundColor Magenta -BackgroundColor White
Write-Host ""
#$Source = Read-Host "Please enter Source directory to check"
#$Target = Read-Host "Please enter Target directory to check"
Write-Host ""
If($source -and (Test-Path -Path $source -PathType Container))
{
"There are $(#(Get-ChildItem $Source).Count) items in the '$Source' directory"
}
Else
{
Write-Host "Please enter a directory"
}
If($source -and (Test-Path -Path $Target -PathType Container))
{
"There are $(#(Get-ChildItem $Target).Count) items in the '$Target' directory"
}
Else
{
Write-Host "Please enter a directory"
}
Write-Host ""
$child1 = Get-ChildItem -Path $Source -Recurse -Force
$child2 = Get-ChildItem -Path $Target -Recurse -Force
Compare-Object $child1 -DifferenceObject $child2 -Property Name
Write-Host ""
Write-Host "NOTE:" -BackgroundColor Cyan -ForegroundColor Black
Write-Host "Any symbols with '=>' mean that the file Does NOT exist in SOURCE but is in the Target" -BackgroundColor Cyan -ForegroundColor Black
Write-Host "Any symbols with '<=' mean that the file Does NOT exist in TARGET but is in the Source" -BackgroundColor Cyan -ForegroundColor Black
}
$hotfix = GetHot-Fix
If ($hotfix) {
Write-Host "Hotfix installed" -BackgroundColor Green -ForegroundColor Black
Write-Host ""
Write-Host "Proceeding with Robocopy...."
Write-Host "............................"
Write-Host ""
Start-MyRobocopy
}
else {
Write-Host "Hotfix is NOT installed - Please ensure you install this hotfix BEFORE" -ForegroundColor "red"
Write-host "Copying any data" -foregroundcolor "red"
Write-Host ""
return
}
Comparision
Variables in powershell are context sensitive. If I define a function like:
$bar = "Hi"
function foo {
$bar = "Hey!"
}
$bar <-- returns "Hi"
Then the $bar variable is not available to me outside that function. To make variables available outside of functions then you can control the scope of a function. If I set a variable in a function using the script or global prefix then the variable will be available for the whole script or globally in powershell runspace. See here:
function foo {
$script:fooVar = "world"
}
function bar {
foo
$global:barVar = "Hello " + $fooVar
}
The variable $fooVar in the foo function will be available to all other functions within the script due to the scope prefix script of the variable $fooVar. The barVar function will be available globally in the runspace. I.e. when your script has finished the variable is still present back at the command line and even to other scripts.
As you can see in the bar function I first call foo and then use the foovVar variable. When I use the $fooVar variable I don't have to specify the $script:fooVar, I can if I want to but it's not necessary.
These are all valid variable assignments:
$aaa = 123
$script:bbb = 123
$global:ccc = 123
So in your case use $script:source and $script:target or $global:source and $global:target. For more info run the following command:
Help About_Scope
the hackish way is to add this to the top:
$Gobal:source = ""
$Gobal:target = ""
and search and replace $source with $Gobal:source and $target with $Gobal:target - then you can use these new global variables at any point in the script.
As suggested you could protect them in another function but for a simple job\automated task that might be overkill. Depends what it's for.
You have a few choices. Two of those are global state (have $source and $target declared at the file scope and both functions use them) and have Comparison take those as parameters. Given that your robocopy function queries the user for the source and target if they are null, then global state is the simplest solution. Personally, I would probably write a third function called something like Get-SourceAndTarget that handles that part and outputs the source and target so you can pass those into both Start-MyRobocopy and Comparison. I don't hack powershell all that well, so I'm a little unclear on the syntax, but that should probably get you started.