Working with Linux Permissions in PowerShell 7

With the introduction of PowerShell Core and now PowerShell 7, the cross-platform capabilities of PowerShell have also brought new challenges. System administrators used to working on Windows systems that now operate cross-platform need to be able to handle permissions in similar ways. With that in mind, how does PowerShell 7 handle Linux permissions?

Exploring Permissions

In Windows, to view a set of files and folder permissions, you may run the Get-ChildItem command to see the mode, much like ls -l on Linux works, but this only tells you the directory and file attributes. To see the permission of a particular file or folder, the Get-ACL command must be used.

Image # Expand
Untitled 2020 03 29T113737.993

PowerShell in Linux does not have that command, therefore there is no way to view the folder permissions using traditional PowerShell filesystem providers. For example, running Get-ChildItem on Linux will show the following.

Image # Expand
Untitled 2020 03 29T113751.016

Compare this to ls -l output and you can see how the permissions are missing. Ideally, to make working with permissions in Linux easier, we could add to the Get-ChildItem output and show the permissions, owners, and groups.

Image # Expand
Untitled 2020 03 29T113805.229

Retrieving Permissions

The easiest method to retrieve the permissions is to pull the output from ls -l file.ext and add that as a property to the existing Get-ChildItem output. To start with, lets split the output of ls -l file.ext and return just the permissions and full file name.

Get-ChildItem | ForEach-Object {
  $_.FullName
  ((ls -l $_.FullName) -Split " ")[0]
}
Image # Expand
Untitled 2020 03 29T113823.347

Next, we want to add the owner and group permissions to our output. This is easy to do as all we need is additional values contained within the existing split string.

Get-ChildItem | ForEach-Object {
  $string = ((ls -l $_.FullName) -Split " ")

  $_.FullName
  $String[0]
  $String[2]
  $String[3]
}
Image # Expand
Untitled 2020 03 29T113838.745

This is a great start, but obviously we need to clean this up a bit and make the output more readable and user friendly.

Crafting a Custom Object

A very flexible method for creating one’s own parseable objects is to use the [PSCustomObject] type. This allows one to create an object with any type of properties quickly and easily. For example, taking our above output and putting this in a custom object is easy as shown below.

Get-ChildItem | ForEach-Object {
  $string = ((ls -l $_.FullName) -Split " ")

  [PSCustomObject]@{
    "FullName"    = $_.FullName
    "Permissions" = $String[0]
    "Group"       = $String[2]
    "User"        = $String[3]
  }
}
Image # Expand
Untitled 2020 03 29T113858.583

As you can tell, this is much more akin to the output that one may expect to see. This allows for cmdlets such as Where-Object or Select-Object to be easily used to filter the results. Of course, there are many more properties that Get-ChildItem has that are not exposed here because we are only returning some of the properties.

Expanding Get-ChildItem Output

How do we take what we have learned above and translate that into better Linux permission support for Get-ChildItem. The easiest way is to create a new wrapper function, that in this case, we will call Get-ChildLinuxItem.

Function Get-ChildLinuxItem {
  [CmdletBinding()]

  Param(
	  [Parameter(Position = 0)][String]$Name
	)

	Process {
		($Name ? ($Name | Get-ChildItem) : (Get-ChildItem)) | ForEach-Object {
		  $string = ((ls -l $_.FullName) -Split " ")
		
		  [PSCustomObject]@{
		    "FullName"    = $_.FullName
		    "Permissions" = $String[0]
		    "Group"       = $String[2]
		    "User"        = $String[3]
		  }
		}
	}
}
Image # Expand
Untitled 2020 03 29T113935.081

As it currently stands this function doesn’t work on pipelined input and has a single parameter. Though we are not intending to replicate every aspect of Get-ChildItem, the main goal here is to return most existing Get-ChildItem properties.

Adding Members to Get-ChildItem Objects

The best way to add additional properties to an existing object is through the Add-Member cmdlet. This lets us use the existing object properties and then merely add on the Permissions, Group, and User properties. Just looking at the Process block then, we change this up to use the following Add-Member functionality.

($Name ? ($Name | Get-ChildItem) : (Get-ChildItem)) | ForEach-Object {
  $object = $_
	$string = ((ls -l $object.FullName) -Split " ")
	
	$object | Add-Member -Name 'Permissions' -Value $String[0] -MemberType 'NoteProperty'
	$object | Add-Member -Name 'Group' -Value $String[2] -MemberType 'NoteProperty'
	$object | Add-Member -Name 'User' -Value $String[3] -MemberType 'NoteProperty'
	
	$object
}

Running Get-ChildLinuxItem we get the following:

Image # Expand
Untitled 2020 03 29T113956.101

Wait a minute, what happened to our new properties? Well because we are now returning the default Get-ChildItem objects, they are subject to the default formatter rules and show properties accordingly. So how do we make the same appear but with our added properties? Select just the properties we want to show.

$object | Select-Object Permissions, Group, User, LastWriteTime, Name
Image # Expand
Untitled 2020 03 29T114012.319

To see this in the traditional table view, send the following output to Format-Table -AutoSize.

Image # Expand
Untitled 2020 03 29T114027.019

Limitations

At the end of all that, it seems that we have a roughly equivalent object and display to what ls -l might give us. So what are the limitations, in our admittedly simple fuction?

  • Missing Get-ChildItem parameters
  • Output requires Select-Object and Format-Table

Though there are more, these two are pretty large ones. To address the first, one may need to create a proxy function of Get-ChildItem and add an additional parameter that does the work to add on the extra properties. Due to the complexity of creating a proxy function (though not unreasonable), we don’t cover that in this article. The second problem, regarding the output, could be resolved by creating a new default formatting file that outputs the new properties that don’t rely on a select or format cmdlet. This would generally go hand-in-hand with the proxy function as the System.IO.FileInfo type (output by Get-ChildItem) could be assumed to have those properties.

Conclusion

Though Linux permission PowerShell 7 support is not natively included, there are ways to utilize core functionality and extend the default cmdlets to output relevant information that can then be further used in scripts and other cmdlets.

Related Article: