I’ve written a copy script which i am going to use in an SCCM 2012 R2 task sequence. Would like to know if i have used things in the correct way as part of my learning process…Thanks !
Ah… well, did you have any more specific questions? I mean, if it works, that’s 90% of the battle!
Usually, you can pipe directly from Get-ChildItem to Copy-Item, which would probably run a tiny bit faster than using ForEach object.
I don’t think your Catch block will ever run, because Copy-Item normally doesn’t throw a terminating error. You’d need to add -ErrorAction Stop to have it generate a catchable, terminating exception. That’s true for your earlier Try/Catch, too - you might review “The Big Book of PowerShell Error Handling” (free ebook from our Resources menu) to learn more about that.
Oh, I see - you set $ErrorAction to ‘Stop.’ That won’t work. It’s $ErrorActionPreference, and setting it globally is considered a poor practice in most situations. Certainly where you’re not catching it globally.
Also, I’m not sure your intention, but as-is a single file copy failure will abort all remaining files, once your Try/Catch is working with -ErrorAction. If you were to get the files and then enumerate them, you could have it fail one file and continue trying others. Again, I’m not sure if that’s your intent or not, so this isn’t “wrong,” it’s just an observation.
You don’t technically need PROCESS{} because you’re not accepting pipeline input.
Why are you trying to catch the errors? The reason you would stop doing a command is if your logic would be step X has to occur before you do step y. For instance, in the example below, if the removal fails, then the copy will not start because you are globally stopping the command if an error occurs. In your posted script you are just trying to catch errors just for logging?
I think a best practice is to use variables as much as possible. CommonApplicationData is a Special Folder that resolves to C:\ProgramData. So, in Windows 10, if you they move the directory to C:\ProgramMagicMSRules, your script would still work because you are using a provided variable that will resolve to that location. If you hardcode the path to C:\ProgramData, your script wouldn’t work. Make sense? Also, Powershell will not resolve %VAR%, you need to use $Env:VAR.
For Copy-Item, you don’t need to loop through each file. Run “Get-Help Copy-Item”. You will see that -Path accepts a String array (i.e. [string]). So, you should be able to run the command below and it will perform a Foreach for each column named “Path” in the return from Get-ChildItem
If you are trying to just capture errors, make them useful. If you were sitting at a computer and received an error, “An error occurred whilst attempting to remove…”, what would you do with that? You don’t have an actual error. Make sure if you do capture errors they are useful. See the examples that will show what the command is doing and the returned exception message that Catch captured
The below code was not tested, it’s just an example to illustrate some of the points above:
Thanks for your replies. I’m about 3 weeks into my learning powershell. I try and use it as much as I can. Must admit I’m getting confused when and where I should be using commands, like the try and catch.
Maybe I’m over complicating things. I want to be able to do best practice and people to look and say, good script !
Rob, your variables for destination and startlocation are great. But I’m not sure how it works together. I know what it is doing it’s the understanding. How and why did you come to that command? I’m impressed and would love to get to that level.
By asking the forum to check my work, I want to get into good habits from the start and not bad practice.
I’ve worked it out, by referring to Don’s great book, “Powershell in depth”.
Its quite clever ! In this example, “{0}\temp” -f $env:windir, the {0} is the first place hold. the -f means formatting, so the command after goes in the first {0} displaying C:\windows\temp !
Sorry, can you help explain this line, Write-Verbose -Message (“Error occured removing *.dat from {0}. {1}” -f $Destination, $.Exception.Message)
How come the {0} has a . (dot) after it ? What does the $.Exception.Message do and how is it called ?
{0} is a place holder for $Destination. So, basically the message would be: Error occured removing *.dat from C:\ProgramData\Microsoft\User Account Pictures. Exception Message
As for exceptions, when Powershell generates errors, there is a lot of information you can get which you can explore with Get-Member and other methods. Let’s look at what happens when I try to do something with C:\Windows\Temp when I’m logged in as a standard user. I get an Access Denied and other information like the type of exception, category, etc. when the error is shown. As I said earlier, we need to know what happened when the error occurred but there is also overkill. So, I just want the message “Access to the path ‘C:\Windows\Temp’ is denied.”, which would be $.Exception.Message. If you look at this, it’s 3 parts. The $ is a variable representing the current context of the script, which is what Catch is returning. $_ is an object which contains properties and methods. $.Exception is a property just like CategoryInfo, ErrorDetails, etc. (see below). We can use can use Get-Member to see what properties and methods are available with a returned object. Typically, the $.Exception.Message is descriptive enough to understand what happened with a command. If not, you have all of the information returned in Catch to give you as much information as you would need.
Rob, thank you so much for taking the time to reply. So the lesson here is use get-member as much as possible and play with results of get-member, I guess.
Using things in the right way is important to me. I do get frustrated with my own knowledge, would love to get to your level of understanding. Just need to keep going with it.