Shout out to Doug Finke for taking the time to break down classes for me. Doug is a true Developer and an great guy! I truly believe that the right people are placed in our paths when we need them most. Yet only if we remain open-minded and willing to learn can these mentors help us reach new levels of development. I can’t thank you enough Doug, your contribution to the community is plain awesome! You make me want to be a better coder!
Thank you PSDay.uk for this great experience! Til’ next time? 😛
I recently had to make a quick Backup & Restore of ACLs three levels deep. Nothing fancy, just two functions, but that got me thinking…
Why not make a class of this?
And so I did! Here’s the framework for the class:
Here’s a list of the methods:
Backup. Backup SDDL from the property $Folder of the class
Restore. Restore SDDL to the property $Folder of the class
Clone. Clone will take a target Folder and clone the SDDL of $Folder to it
ConvertSDDLToAccess. This will enumerate what the SDDL stands for
The default constructor will evaluate the folder used to instantiate the object. If successful, the SDDL, Owner and Access is retrieved using the Backup() method. All actions are registered for future reference.
Instantiating is pretty straightforward:
This will retrieve the SDDL for the folder and enumerate the Access.
Restore is a bit tricky. For one you need to make sure it isn’t empty. Set-Acl has no problem setting an empty SDDL, blowing everything open (worst case scenario, and that’s why you should test this in a lab first!). The other challenge is having a valid SDDL string. You can change the SDDL string if you want to something gibberish, hence the typecast as a precaution.
The same goes for cloning. In this case we need to test the target path. Alternatively, you could also change the Folder to a new path… It works, you’d just have misleading ActionHistory entries… I wonder if there’s a way to make this read-only, just thinking out loud here… (note to self)
This is just a lil’ something extra. Like I said in a previous blog SDDL really gives more information. For one, the SID will let you know which domain this object belongs to. One thing I ran into with ReACL is that SIDHistory will resolve to the current NTAccount. This had me puzzled for a while until I saw that the SIDs in SDDL where different.
Here’s what the ouput looks like:
Now for those of you that are wondering just what is this AccessMask, wonder no more! 🙂
Remember the RoboCopy ExitCodes blog I did a while back? Well it’s the same principal 🙂 This is why classes & everything related should be on your radar…
Here’s how this works…
Say I wanted to evaluate the AccessMask of a SDDL entry
Here I have the SID & the NTAccount. This is the builtin administrators account but it also works for Domain accounts.
There’s a private function that will translate the SID accordingly.
To see what the account can actually do we can enumerate the AccessMask
This is what we’d see using the advanced Security GUI tab of a folder.
Not bad… Not bad at all…
I can’t state this enough, SDDL is NOT to be be trifled with. Yes you need admin rights and please try this is a testlab first. SDDL is very potent, if used with caution, it could do a whole bit of good!
It all started a year ago… Always wanting to learn anything PowerShell related, classes caught my eye ever since it was introduced in v5. I wanted to try my hand at classes with a real life application… So I got on twitter for some tips…
Doug was kind enough to reach out and point me in the right direction, for which I owe him a great debt! Appreciate it Doug!!!
Like I said, I wanted to try my hand at classes with a real life application… If you’ve read my blogs then you’ll know that I’m a fan of robocopy, seriously, huge fan! . Did I mention how awesome robocopy is? 😛 I think I found my real life application 😉
When I started out with my Robocopy class, it was just about logging initially, but it could be so much more! Classes are native to v5. Now that v5 is mainstream I decided to finish the class. Richard Siddaway’s article was just the spark I needed to get me going (again)!
Here’s what the Robocopy class looks like:
Here a quick rundown on the properties:
The source/destination properties of the class are self explanatory (if you’re familiar with robocopy). The property logdir and JobID will be used to define a valid logfile name (with extension .log). Robocopy has quite a bit of options. I wanted to keep it as generic as possible. The property $this.Options is still a work in progress. The property $this.WildCards is where you’ll define what will be filtered. I’ll get back to rcCMDs and rcExitCodes later on…
These are the methods I came up with (so far, still a work in progress)
Mirror(). Mirrors $this.Source to $this.Destination with some default options
Move(). Moves this.Source to $this.Destination with some default options
RollBack(). Rollback $this.Destination to $this.Source with some default options
Sync(). Sync will synchronize the delta’s from $this.Source to $this.Destination using any additional $this.Options defined (at least that’s the idea). I’ve added a few options by default, mostly to exclude files and folders, think recycle.bin “System Volume Information” and the likes.
VerifyPaths(). This let’s you know if the $this.Source, $this.Destination and $this.LogDir are valid.
GetlistSource(). This will list the content of the $this.Source
GetListDestionation(). This will list the content of $this.Destination
GetLogSummary. This will return a summary of the log file (Hehe). The method is static so that you don’t have to instantiate the class in order to use it. (Thanks again Doug!)
The two methods: StartRCProcess and ExecuteRCCMD are actually helper methods. I just haven’t figured out how that works in classes. Ideally I’d like to have them hidden or as a function if that even makes sense. So here’s where they come in. At first I just executed robocopy with the necessary arguments. If you’re not interested in the exitcode then using ExecuteRCCMD is what you need. I wrote a blog about enumerating RoboCopy Exitcodes. Using $LastExitCode isn’t going to cut it if you decide to run robocopy jobs parallel. That’s where StartRCProcess comes in.Using Start-Process comes with an overhead of say 20 MB, which could add up in the long run. You do need to wait until the process has finished to retrieve the exitcode. If you really need the exitcode then StartRCProcess is what you need. The property $this.rcExitCodes will only be populated if StartRCProcess is used. Both will populate the $this.rcCMDs property.
Ok I think I’ve covered the basics, time to show some code! 😉
Here what’s happening in the List methods:
GetListSource() is using $this.StartRCProcess to generate a list of $this.Source using some default option. While writing I noticed that I forgot to add the wildcards to the parameter. All I had to do was add it!. I added it at the beginning so it lines up accordingly… Robocopy is fickle like that… GetListDestination does the same only it uses ExecuteRCCMD instead.
Here’s what’s going on in StartRCProcess and ExcuteRCCMD
Both StartRCProcess and ExcuteRCCMD will save the robocopy command using Write-Information. I’m loving Write-Information more and more! StartRCProcess saves the exitcode with some extra information. Here’s where the robocopy exitcode enumeration came in handy! ExecuteRCCMD will run robocopy with the specified arguments. Truth be told I’m more partial to the ExecuteRCCMD method. I added the StartRCProcess more for demo purposes and finally getting to use my Robocopy exitcode enumeration!
For Mirror(),Move() and RollBack(), I omitted the Wildcards. These methods all or nothing in my opinion. If omitted, . will be the default.
Sync() had me going for a while. I still have some issues with Options. For now Sync() uses some default switches. Like I said work in progress…
Quite a bit of code, so does it work? Here’s some code to play with. be sure to edit the source,destination and logdir to your liking. Just remember that robocopy is unforgiving so make sure not to use it of production folders!
First I instantiate the class with arguments. I then run the methods Sync(),GetListSource() and GetListDestination(). Up next is retrieve the LogSummaries from the methods. Here’s a screenshot of the Sync LogSummary
I did a select of $rc.rcCMDs to give you an idea what is being stored
The information stream is quite handy! The tags will definitely come in handy when you need to filter on action verb or job ID.
The methods GetListSource() & Mirror() both make use of StartRCProcess(), so let’s see what $rc.rcExitcode has shall we?
This is by far my longest blog, if you made this far then… Congratulations! There’s still so much to discover when it comes to classes.
Classes definitely makes your code look and feel more like a developer 😉 . I feel more comfortable giving a colleague this class than a bunch of scripts. In Richard’s article he’s using both classes and modules. There are sure to be some gotcha’s… Do you go all in with classes or only partial?
I’m hoping that the community can shed some light on the subject. I’d love to hear from you guys on how to improve on this… Let’s make this year, a year of PowerShell Classes! 😛
At our last DuPSUG meeting, Jaap Brassers did a short Demo on the latest addition to PowerShell Streams, Information. How did I miss this?
So what’s so great about the new Information stream? The Information stream solves the Write-Host issue of not sending output to a stream like: error;verbose;warning etc. As such we were told that Write-Host is evil and every time you use it a puppy dies… Who wants that on their conscious right?
Well with the Information stream Write-Host is allowed… no more puppy genocide!
Jaap showed us what the stream output looks like and how to write to the information stream. Here’s what the output looks like
Here are a few properties we can play with: TimeGenerated,Source,User,Computer,Tags and MessageData. You get all of this by writing/redirecting to the information stream. The TimeGenerated property caught my eye, imagine creating a timeline of sorts. While googling (don’t act like it’s just me…) I came across a great article by Jeff Hicks on the subject (Seriously, how did I miss this?) In it he also talked about generating a timeline view… 😉
After I had some time to think about how to make use of this, it dawned on me… You know what else is native to PowerShell v5? Classes! When Classes were introduced in v5, they were another way to write DSC Resources. I’ve always thought that there’s more to classes than meets the eye.
If you’re interested in classes, Richard Siddaway has a great article taking you through each step, worth trying out.
Jaap’s demo gave me the idea to try my hand at a simple logger using classes. Here’s what I came up with…
Here’s a quick rundown:
The idea is to gather output from the Information stream. Constructors are a way of initiating an object for use (Shout out to Doug Finke for breaking this down for me!)
I “borrowed” Jeff Hicks idea of validating the preference first. This way you can toggle between showing Write-Information and/or Write-Host data. You can use Write-Host for a more colorful experience. Did I already mention that Write-Host is cool again? :-P. I also added the possibility to show data in the console if you choose to, a verbose action of sorts…
I also added two methods of exporting: text and xml. That’s where LogDirectory and FileBaseName come in. For the text export, I selected TimeGenerated and MessageData for a timeline view (I know I know Jeff Hicks did it first :-P). Export to xml saves InfoMessages in raw format.
Here’s some code to play with.
Write-Verbose "InformationPreference = $InformationPreference"
$Logger.Preference = $InformationPreference
Get-Process -Name *PowerShell*
$Logger = [WriteLog]::New('psProcess','C:\scripts\temp')
#Run with InformationAction. Information will be saved and not shown
Get-PowerShellProcess -InformationAction Continue -Verbose
#Run without InformationAction with Console set to $true
$Logger.Console = $true
#Run without InformationAction with Console set to $false
$Logger.Console = $false
Here are some screenshots of the output
I initiated the object a FileBaseName value ‘psProcess’ and LogDirectory ‘C:\scripts\temp’. Default Preference is ‘SilentContinue’ and no ouput to the console
First up run with action set to continue
$Logger.InfoMessages has redirected data.
When console is set to true we’ll see the Information message without explicitly setting InformationAction to ‘continue’. Using InformationAction will save the Information data to InfoMessages property.
Setting the console preference back to $false stops displaying output to the console
I hope that you were able to follow it all. 2016 was all about Operation Validation for me. I think classes are going to be mainstream in 2017! PowerShell + Classes will definitely give you an edge and not to mention make you feel more like a developer! 😉