My standing recommendation for NTFS security when it comes to Hyper-V is: leave it alone. Unfortunately, most people don’t learn why until they’ve already broken something. It’s not fun to fix. I’ve produced a script for fixing Hyper-V folder security that should make things much easier for you.

Fixing Hyper-V Folder Security – The Script

Here’s the script in all its glory. The straight-through run of it just needs a Path. It will add the necessary privileges for “NT VIRTUAL MACHINE\Virtual Machine” to that path. If you specify -FullReset, then it removes inheritance and sets up a default set of permissions. You can read what they are in the help text. I had toyed around with resetting ownership as well, but doing so in PowerShell is just plain painful. If you want to modify this script to allow it, grab Lee Holmes’ instructions.

The script kind of has WhatIf support. It will only really work if -FullReset is specified. That’s because I didn’t want it over-confirming if you’re using FullReset and I couldn’t find a reliable way to trap if -WhatIf was specified without always triggering the prompt or writing my own WhatIf handler.

If you use -FullReset and it finds anything it thinks is a virtual machine file, it will make you double-confirm that you want to perform the reset. You should really only be using FullReset on the parent folder, not “Virtual Hard Disks” folders or anything like that. If you wind up ripping off the permissions of the VM to its VHD files, well, I warned you. Twice. The script should only operate on the folder and not its files, but, well, scripting NTFS permissions sometimes leads to unexpected results.

All you have to do is copy and paste the contents of this box into your own .ps1 file. I’ve written it with the expectation that you’ll use the name Reset-VMFolderSecurity.


The Discovery Process

The impetus to create this script is not entirely altruistic. Most of my lab work is done without backups. I’m probably not alone in the general attitude that if it was really important, it wouldn’t be in the lab. But then, I suffer some kind of self-inflicted data loss and wish I’d had backups. So, I went about setting up Altaro Hyper-V Backup on all my lab nodes and let it go. I had a few problems, mostly because of situations I created. I sent in an e-mail to Altaro support, and, as always, the initial issues were quickly resolved. But then, I noticed that I was getting some VSS errors. I probably could have dug through the logs, but Altaro support already had them and I’m kind of lazy, so I asked if they could tell me what was wrong.

I get an e-mail back telling me that I’m getting access denied errors and asking if I would check permissions. So, I thought, “I know better than to tinker with the permissions on my VM folders. How could security possibly be wrong?” But, Altaro support is rarely wrong, and checking is pretty easy even for a lazy person like me, so I did. And, sure enough, the “NT VIRTUAL MACHINE\Virtual Machine” account was not present on my virtual machines folder. I’m still not sure how that happened, but in case you’re lucky enough to be one of those people who can learn by reading, don’t ever remove the Virtual Machines account from a security list.

Setting it back is kind of a pain. For a long time, I’ve just been referring people to a blog I found that shows how to do it. The problem I have with that blog is that it sets security on the root of a drive, so I have to always couch my recommendation with a warning against doing that. It’s horrible practice and Windows really doesn’t like it. Furthermore, his instructions grant Full Control to the account. There’s nothing blatantly wrong about that, but it’s not necessary and it’s not what Hyper-V does when it builds out folder ACLs. But, as long as I personally don’t know how to do something, I have to refer everyone to someone that does. So, my goal became to figure out the process on my own and do it the right way.

The first thing I thought was that I could take some screenshots of a properly set folder and tell everyone running a GUI to duplicate those, then find some PowerShell way to set them as well. I learned pretty quickly that the GUI method would be incomplete and the PowerShell method is excessively difficult. So, the first thing to be aware of is that if you use the GUI screen to transcribe properties, it might work, but it won’t be complete.

The issue with PowerShell isn’t directly PowerShell’s fault. It relies on the .Net Framework, and as it just so happens, the .Net Framework’s file and folder security commands, at least up through v4.5, just aren’t quite complete.

What I did first was retrieve the ACL for a working folder, then narrow down its access entries to just the Virtual Machines object. I did that like so:

That produced the following:

The same folder has two separate access rules for the Virtual Machines account. The first is legible. It can be understood and manipulated. As for the second… well… What the heck is “-2147483642”?

To answer that, we need to look at something that will seem scary at first:

This mass of seemingly incomprehensible jumble is called Security Descriptor Definition Language. It’s actually not so bad if you have an SDDL lexicon. To discover what matters, we need to find the items here that represent the “Virtual Machines” account. I’ve seen it enough in the past to recognize its SID: S-1-5-83-0. There are two items here that contain that SID:

  • (A;;0x12008f;;;S-1-5-83-0)
  • (A;CIIO;DCLCGR;;;S-1-5-83-0)

The first one really is unintelligible to the human eye, but the second one isn’t. We’ll start with the first one though. We want to match them both to the more readable entries from the Get-Acl results. They’re actually in the same order, so that’s our first clue. But, from the lexicon, we know that the second grouping (after the first semicolon), references the inheritance model. The first SDDL entry has nothing between the first and second semicolons, which means it has no inheritance, which aligns it with the first object from the Get-Acl pull. We can match the CI and IO of the second item to the Container Inherit and InheritOnly of the second item from Get-Acl. So we know that “0x12008f” means “CreateFiles, AppendData, Read, Synchronize”. That’s easy enough. If I wanted to apply that to a folder, I would do this:

If I do that, the folder will get the same permissions that you find when you look at the security properties of a properly configured folder in Windows Explorer. But, if I run that Get-Acl cmdlet against it, I’m not going to get the strange entry with “-2147483642”.

To solve that, I naturally began with the lexicon. Using that, we find that “(A;CIIO;DCLCGR;;;S-1-5-83-0)” parses out to:

Access Allowed;
Container Inherit, Inherit Only;
Delete Child Objects, List Contents, Generic Read

Next, I looked at the options I had available in System.Security.AccessControl.FileSystemRights:

FileSystemRights OptionsThey’re not all visible in that screenshot, but it doesn’t matter. I tried various combinations from that list that I thought might work, but none did. I actually wasn’t surprised, because if .Net were able to parse that number as a FileSystemRights object, then it wouldn’t show us that number.

My next step was to figure out what the real number was. Obviously, a negative number doesn’t work as a group of flags. In case you’re not familiar with how flags work, here’s a visual representation:

Mailboxes, source

Mailboxes, source

In the picture, we have six mailboxes. All these boxes co-exist in the same structure. Each has its own flag. If it’s up, the postal worker knows that the box contains outbound mail. At a glance, it’s easy to tell which, if any, of the six need attention. Windows uses a similar scheme for flagging various true/false properties on items. Rather than use up a lot of boolean variables which really require more than a single bit to store, it can use a more efficient variable, like an integer, to hold many at once. So, for a six mailbox stretch like this, Windows could use an 8-bit variable to represent them all. It would store them like this:

  • Bit 1 is box 770 – binary value 1
  • Bit 2 is box 768 – binary value 10
  • Bit 3 is box 766 – binary value 100
  • Bit 4 is box 764 – binary value 1000
  • Bit 5 is box 762 – binary value 10000
  • Bit 6 is box 760 – binary value 100000
  • Bit 7 is unused  – binary value 1000000
  • Bit 8 is unused  – binary value 10000000

In the picture, all the flags are up, so that would be represented by 00111111. Converted to decimal, that would be 63; hex would be 3f. If it wants to know if a particular flag is up, it uses a boolean AND against that column. For example, it wants to check whether box 764 has a message waiting. Box 764 is bit 4, so the mask to use puts a 1 in bit 4 and a zero everywhere else: 00001000. It performs a binary AND against the condition of the entire variable, which in this case is 00111111. It then checks the result to see if bit 4 is set. If it is, then it knows that box 764 has something waiting.

If bit 4 had held a 0, the result would have been all 0s, or false, and it would have known there was no message waiting.

To go the other way, that is, to take all the values of the flags and put them together, we use a boolean OR. So, if we need to set that box 768 and 762 have messages waiting, the formula is 10 OR 10000 which is 10010.

So now that we know how flags work, we can decipher the number that represents the access rights. Let’s look at the one we already know, which showed up as 0x12008f. In binary, that’s 100100000000010001111. So, if we knew all the flags, we could visually determine what was set and what wasn’t. We’ve already figured that one out though, so let’s move to the “-2147483642” item. As I said before, that’s not valid because it’s negative. No amount of ORing 0s and 1s will ever result in a negative number. What probably happened here is that a really big unsigned number found its way into a signed number’s slot. Just looking at the contents of memory, you can’t tell if a number is signed or not. That’s decided by how the variable that owns that memory is defined. If it’s defined as a signed number, then when the leftmost bit is set, it’s negative. When it’s empty, the number is positive. If it’s an unsigned number, then that leftmost bit is part of the number. If it’s set, it’s a big number. Anyway, whatever is going on here, that number is not usable.

Knowing that, I set off to find out what the actual number is. For that, I need to find out what numbers represent the letters that we saw in the SDDL (DCLCGR, if you forgot). For that, I found myself on this page. I downloaded the linked SDDL parser and fed it both sets:

ACE 1 is the one we figured out. ACE 2 is the mystery set. As you can see, they only have “ADS_RIGHT_SD_DELETE_CHILD” in common. That by itself doesn’t tell us what the numbers should be, though. To the Internet! This time, we landed in the Active Directory C++ API documentation. With this, we can get the values of the flags:

  • ADS_RIGHT_GENERIC_READ is 0x80000000

If you weren’t aware, the “0x” means that the number is given in hexadecimal. I guess its obvious which one put us over the edge. Converted to binary, 0x80000000 is 10000000000000000000000000000000. Our flag calculation is 0x80000000 OR 0x2 OR 0x4 = 0x80000006. In binary, that’s 10000000000000000000000000000110. So, mostly for academic purposes, I assigned that number to the $ACLAccessRights variable in the small script above and tried to execute, and as expected, the system threw it out as not being a valid value for System.Security.AccessControl.FileSystemRights.

Well, now what? I tried stepping through all the other types I found under the AccessControl .Net class. Most of them just won’t let a programmer create an object. I worked my way up to the root AuthorizationRule, and I was told that there weren’t any constructors; it seems that .Net can make one of those objects but I can’t. Out of the listed rule object types, programmers can make FileSystemRights and RegistrySystemRights, and that’s it. For anything else, such as “2147483654” (the actual decimal representation of the number we want), your only options are SDDL or going deep with API calls. I have the capability of doing C++ API work, but I’m just not going to do it unless I really have to. So, SDDL it is.

As we saw before, we have the ability to set the first part of this using nice, clean, legible PowerShell. But, we do know the entire SDDL that we need. So, since we can’t have legibility across the board, we’ll just go with the super short SDDL and do it all quickly:

That’s really all there is to it. It’s not easy to read, but once you understand the components, it does make sense in its own way. It would be nice if the .Net framework, and by extension PowerShell, caught up to the rest of it, because setting file and folder permissions is definitely in line with what a sysop does every day but what I just showed you most definitely should not be. It’s because of frustrations from complications like this that you find “Everyone/Full Control” being set on critical file shares. It’s also the only thing I still dread doing when I connect to Hyper-V Server or Windows Server in Core mode.