Get free space of a supplied drive path in powershell - powershell

I am trying to create a Function to return the amount of free drive space in MB. The function takes the path name as a parameter and must cope with mount points. My drives are set up like this:
C:\ - Disk0
G:\ - Disk1
G:\Data - Disk2
So I want to do something like:
function Get-FreeSpace {
param (
$path
);
# iterate through and find the volume, detect if in a mount point and return free space
#
return [int]$freeSpace;
}
I have looked at using this array as a starting point in my function, but I am getting stuck.
$vols = Get-WMIObject Win32_Volume -filter "DriveType=3" -computer $computerName | Select Caption,DriveLetter,Label,#{Name="DiskSize(GB)";Expression={[decimal]("{0:N1}" -f($_.capacity/1gb))}},#{Name="PercentFree(%)";Expression={"{0:P2}" -f(($_.freespace/1mb)/($_.capacity/1mb))}}
$vols is returning a System.Array of PSCustomObject types. So if I pass the function the following paths:
G:\Data\My\Test\Path
G:\Data
It will find the free space of the G:\Data mount point.
If I pass it G:\Some\Other\Path, it will return the free space of the G:\ drive. I want to use it like so: $freeSpace = Get-FreeSpace "G:\Some\Other\Path"
I would be grateful for any help.

Try this:
function Get-FreeSpace {
Param(
$path
);
if ($path[-1] -ne '\') { $path += '\' }
$filter = "DriveType=3 And Name='$($path -replace '\\', '\\')'"
$free = Get-WmiObject Win32_Volume -Filter $filter |
Select-Object -Expand FreeSpace
return ($free / (1024*1024))
}
You need to double the backslashes in $path, because they must be escaped for WMI queries. For that (confusing as it may seem) you have to replace '\\' with '\\', because the first occurrence is a regular expression where the backslash must be escaped, whereas the second occurrence is a string literal with a double backslash.
Update:
To match a partial path you could use the -like operator:
function Get-FreeSpace {
Param(
$path
);
$free = Get-WmiObject Win32_Volume -Filter "DriveType=3" |
Where-Object { $path -like "$($_.Name)*" } |
Sort-Object Name -Desc |
Select-Object -First 1 FreeSpace |
ForEach-Object { $_.FreeSpace / (1024*1024) }
return ([int]$free)
}
The Where-Object clause selects all mount points with a partial path matching $path, and the subsequent Sort-Object | Select-Object selects the one with the longest match.

A simpler variant:
function Get-FreeSpace {
param ([string]$path);
$space = (Get-Volume -FilePath $path).SizeRemaining;
return [int64]($space / (1024 * 1024)); # this would otherwise be a float
}
Get-Volume -FilePath retrieves the volume associated with the provided path; from that volume, we extract SizeRemaining and divide out 1MB. The cast to int64 is because the operation would otherwise return float.

Related

powershell winform searchbox shows results incorrect [duplicate]

I'm using Powershell to set up IIS bindings on a web server, and having a problem with the following code:
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if ($serverIps.length -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
$primaryIp = $serverIps[0]
$secondaryIp = $serverIps[1]
If there's 2+ IPs on the server, fine - Powershell returns an array, and I can query the array length and extract the first and second addresses just fine.
Problem is - if there's only one IP, Powershell doesn't return a one-element array, it returns the IP address (as a string, like "192.168.0.100") - the string has a .length property, it's greater than 1, so the test passes, and I end up with the first two characters in the string, instead of the first two IP addresses in the collection.
How can I either force Powershell to return a one-element collection, or alternatively determine whether the returned "thing" is an object rather than a collection?
Define the variable as an array in one of two ways...
Wrap your piped commands in parentheses with an # at the beginning:
$serverIps = #(gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort)
Specify the data type of the variable as an array:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
Or, check the data type of the variable...
IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
Force the result to an Array so you could have a Count property. Single objects (scalar) do not have a Count property. Strings have a length property so you might get false results, use the Count property:
if (#($serverIps).Count -le 1)...
By the way, instead of using a wildcard that can also match strings, use the -as operator:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}
You can either add a comma(,) before return list like return ,$list or cast it [Array] or [YourType[]] at where you tend to use the list.
If you declare the variable as an array ahead of time, you can add elements to it - even if it is just one...
This should work...
$serverIps = #()
gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort | ForEach-Object{$serverIps += $_}
You can use Measure-Object to get the actual object count, without resorting to an object's Count property.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if (($serverIps | Measure).Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
Return as a referenced object, so it never converted while passing.
return #{ Value = #("single data") }
I had this problem passing an array to an Azure deployment template. If there was one object, PowerShell "converted" it to a string. In the example below, $a is returned from a function that gets VM objected according to the value of a tag. I pass the $a to the New-AzureRmResourceGroupDeployment cmdlet by wrapping it in #(). Like so:
$TemplateParameterObject=#{
VMObject=#($a)
}
New-AzureRmResourceGroupDeployment -ResourceGroupName $RG -Name "TestVmByRole" -Mode Incremental -DeploymentDebugLogLevel All -TemplateFile $templatePath -TemplateParameterObject $TemplateParameterObject -verbose
VMObject is one of the template's parameters.
Might not be the most technical / robust way to do it, but it's enough for Azure.
Update
Well the above did work. I've tried all the above and some, but the only way I have managed to pass $vmObject as an array, compatible with the deployment template, with one element is as follows (I expect MS have been playing again (this was a report and fixed bug in 2015)):
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
foreach($vmObject in $vmObjects)
{
#$vmTemplateObject = $vmObject
$asJson = (ConvertTo-Json -InputObject $vmObject -Depth 10 -Verbose) #-replace '\s',''
$DeserializedJson = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($asJson)
}
$vmObjects is the output of Get-AzureRmVM.
I pass $DeserializedJson to the deployment template' parameter (of type array).
For reference, the lovely error New-AzureRmResourceGroupDeployment throws is
"The template output '{output_name}' is not valid: The language expression property 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Expression.Expressions.JTokenExpression'
can't be evaluated.."
There is a way to deal with your situation. Leave most of you code as-is, just change the way to deal with the $serverIps object. This code can deal with $null, only one item, and many items.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
# The $serverIps could be $null. Even $null can loop once.
# So we need to skip the $null condition.
if ($_ -ne $null) {
# Get the index of the array.
# The #($serverIps) make sure it must be an array.
$idx = #($serverIps).IndexOf($item)
if ($idx -eq 0) { $primaryIp = $_ }
if ($idx -eq 1) { $secondaryIp = $_ }
}
}
In PowerShell Core, there is a .Count property exists on every objects. In Windows PowerShell, there are "almost" every object has an .Count property.

shortened output of property values in recursive function

I want to recursively enumerated all WMI namespaces.I have this function:
function Get-WmiNamespace {
Param(
[parameter()]
[string]$Namespace = 'root',
[parameter()]
[string]$Locale = 'MS_409',
[parameter()]
[switch]$Recurse
)
Begin {
$WMIParams = #{
Namespace = $Namespace
Class = '__NAMESPACE'
Locale = $Locale
ErrorAction = 'SilentlyContinue'
}
}
Process {
Get-WmiObject #WMIParams |
Sort-Object -Property Name -CaseSensitive -Culture "en-US" |
ForEach-Object {
$WMIParams.Namespace = "{0}\{1}" -f $_.__NAMESPACE, $_.Name
$object = [PSCustomObject] #{
Namespace = $WMIParams.Namespace
}
$object.PSTypeNames.Insert(0,'Wmi.Namespace.Name')
$object
if ($recurse) {
$PSBoundParameters.Namespace = $WMIParams.Namespace
Get-WMINamespace #PSBoundParameters
}
}
}
}
Inspired here:
[https://learn-powershell.net/2014/05/09/quick-hits-list-all-available-wmi-namespaces-using-powershell/]
I get this output:
Namespace
---------
ROOT\Appv
ROOT\CIMV2
ROOT\CIMV2\mdm
ROOT\CIMV2\mdm\dmmap
ROOT\CIMV2\mdm\MS_405
ROOT\CIMV2\ms_405
ROOT\CIMV2\ms_409
ROOT\CIMV2\power
ROOT\CIMV2\power\m...
ROOT\CIMV2\power\m...
ROOT\CIMV2\Security
ROOT\CIMV2\Securit...
ROOT\CIMV2\Securit...
ROOT\CIMV2\Termina...
ROOT\CIMV2\Termina...
ROOT\Cli
ROOT\Cli\MS_405
ROOT\Cli\MS_409
ROOT\DEFAULT
ROOT\DEFAULT\ms_405
ROOT\DEFAULT\ms_409
ROOT\directory
ROOT\directory\LDAP
ROOT\directory\LDA...
ROOT\directory\LDA...
ROOT\Hardware
ROOT\Hardware\ms_405
ROOT\Hardware\ms_409
ROOT\Intel_ME
ROOT\IntelNCS2
ROOT\IntelNCS2\ms_409
ROOT\Interop
ROOT\Interop\ms_405
ROOT\Interop\ms_409
ROOT\Microsoft
ROOT\Microsoft\Hom...
ROOT\Microsoft\pro...
ROOT\Microsoft\Sec...
ROOT\Microsoft\Uev
ROOT\Microsoft\Win...
...
ROOT\Microsoft\Win...
ROOT\msdtc
ROOT\PEH
ROOT\Policy
ROOT\Policy\ms_405
ROOT\Policy\ms_409
ROOT\RSOP
ROOT\RSOP\Computer
ROOT\RSOP\User
ROOT\SECURITY
ROOT\SecurityCenter
ROOT\SecurityCenter2
ROOT\ServiceModel
ROOT\StandardCimv2
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\StandardCimv2...
ROOT\subscription
ROOT\subscription\...
ROOT\subscription\...
ROOT\WMI
ROOT\WMI\ms_405
ROOT\WMI\ms_409
Namespaces names are truncated.
I guess the reason is the width of Name column is set in first iteration of function Get-WmiNamespace according longest value (ROOT\SecurityCenter2).
It can be fixed by piping output to Format-Table with -AutoSize parameter:
Namespace
---------
ROOT\Appv
ROOT\CIMV2
ROOT\CIMV2\mdm
ROOT\CIMV2\mdm\dmmap
ROOT\CIMV2\mdm\MS_405
ROOT\CIMV2\ms_405
ROOT\CIMV2\ms_409
ROOT\CIMV2\power
ROOT\CIMV2\power\ms_405
ROOT\CIMV2\power\ms_409
ROOT\CIMV2\Security
ROOT\CIMV2\Security\MicrosoftTpm
ROOT\CIMV2\Security\MicrosoftVolumeEncryption
ROOT\CIMV2\TerminalServices
ROOT\CIMV2\TerminalServices\ms_405
ROOT\Cli
ROOT\Cli\MS_405
ROOT\Cli\MS_409
...
What would be the best way to solve this behavior?
The entire name is stored in the namespace property. If you just want to see it in the command output, you could use -ExpandProperty from Select-Object
Get-WmiNamespace -Recurse | select -ExpandProperty namespace
Also, just adding Sort shows the full name
Get-WmiNamespace -Recurse | Sort
Without using Expand and Doug is points out, you can just dot it and avoid the format stuff or futzing with trying to change the function itself, etc. Well, at least for a single column.
(Get-WmiNamespace -Recurse).Namespace
# Results
<#
(Get-WmiNamespace -Recurse).Namespace
ROOT\Appv
...
ROOT\CIMV2\Security\MicrosoftTpm
ROOT\CIMV2\Security\MicrosoftVolumeEncryption
ROOT\CIMV2\TerminalServices
ROOT\CIMV2\TerminalServices\ms_409
...
ROOT\Microsoft\SqlServer\ComputerManagement15
ROOT\Microsoft\SqlServer\ComputerManagement15\MS_409
ROOT\Microsoft\SqlServer\ServerEvents
ROOT\Microsoft\SqlServer\ServerEvents\MSSQLSERVER
...
ROOT\Microsoft\Windows\DesiredStateConfigurationProxy
ROOT\Microsoft\Windows\DesiredStateConfigurationProxy\MS_409
...
#>
If you saying you want, like a Linux column file list of these then other steps are needed You can do this Linux like multi-column using the Format-Wide cmdlet.
For Example:
Get-ChildItem -Path 'C:\Program Files' -Recurse |
Format-Wide -Property Name
Get-ChildItem -Path 'C:\Program Files' -Recurse |
Format-Wide -Property Name -Column 5
So, for this list to get a table-like view, do the same thing.
Get-WmiNamespace -Recurse |
Format-Wide -Property namespace -Column 3
# Results
<#
ROOT\Appv ROOT\aspnet ROOT\CIMV2
ROOT\CIMV2\mdm ROOT\CIMV2\mdm\dmmap ROOT\CIMV2\mdm\MS_409
ROOT\CIMV2\ms_409 ROOT\CIMV2\NV ROOT\CIMV2\NV\Events
....
#>
Use whatever column count that fits your screen. No, you cannot use columns and autosize together as they are mutually exclusive.
So, as Doug points out you can make your own formatter, or as per your comment...
'My point was whether there is a way to modify function itself to get
table formated '
...you can doctor the function to use aforementioned for the results you are after.

-join operator on a variable for a parameter

function Get-Diskinfo {
param(
[string[]] $Computername = 'XEUTS001',
[string[]] $drive = 'c:'
)
$a = "-join $Computername[1..3]"
Get-WmiObject Win32_LogicalDisk `
-Filter "DeviceID = '$drive'" `
-ComputerName $Computername `
-Credential (Get-Credential -Credential ayan-$a) |
Select-Object `
#{n='Size'; e={$_.size / 1gb -as [int]}},
#{n='free';e={$_.freespace / 1gb -as [int]}},
#{n='% free';e={$_.freespace / $_.size *100 -as [int]}} |
Format-Table -AutoSize
}
I wrote this function to get some details about specific disks. However, I have to run them remotely and in a multi-domain environment. We have different usernames for computers in different OU's. I wanted the script to be able to take the username from the computername itself. The usernames are in this format ---- "name"+ "first 3 letters of the computername" which is the OU name. I am able to get the -Join method to work normally. However, it doesn't work if the variable is a parameter in a function. Here the username shows up as "ayan--join xeuts001[1..3]" when I want it to show up as "ayan-xeu"
What you have there is just a string that happens to contain a variable (which is expanded). Inside a string you are not in expression mode, so you cannot use operators. They just get embedded string content like you see there. What you want is probably:
$a = -join $Computername[1..3]
But that isn't correct, as it will yield oob for a computer name Foobar. If you want the first three letters, you'd need
$a = -join $Computername[0..2]
or even simpler (and easier to read, and faster):
$a = $Computername.Substring(0, 3)
P.S.: I also took the liberty of reformatting your original code, it was a horrible mess to read.

Getting a free drive letter

I saw the Get-NextFreeDrive function in this answer and I wondered if there was a more efficient way to do this. It appears that the function in the linked answer keeps going through all the letters even if it has already found a free drive letter.
At PowerShell Magazine, we ran a brain teaser contest to find out the shortest answer to your question. Check this:
http://www.powershellmagazine.com/2012/01/12/find-an-unused-drive-letter/
There are several answers but here is my fav one:
ls function:[d-z]: -n | ?{ !(test-path $_) } | random
My two cents:
get-wmiobject win32_logicaldisk | select -expand DeviceID -Last 1 |
% { [char]([int][char]$_[0] + 1) + $_[1] }
Range of valid [CHAR] is 68..90, adding a check if [char]$_[0] -gt 90 avoid unexpected results.
In case some unit is a mapped network drive it return always the major successive, ex.:
c: system drive
d: cd/dvd
r: network mapped drive
the command return s: and not e: as [string]
This give the first free drive letter ( a little ugly.. someone can do it better IMO):
$l = get-wmiobject win32_logicaldisk | select -expand DeviceID | % { $_[0] }
$s = [int][char]$l[0]
foreach ( $let in $l )
{
if ([int][char]$let -ne $s)
{
$ret = [char]$s +":"
break
}
$s+=1
}
$ret
I like this way, for the following reasons:
It doesn't require WMI, just regular powershell cmdlets
It is very clear and easy to read
It easily allows you to exclude specific driveletters
It easily allows you to order the driveletters in any order you would like
It finds the first non used driveletter and maps it, and then it is finished.
$share="\\Server\Share"
$drvlist=(Get-PSDrive -PSProvider filesystem).Name
Foreach ($drvletter in "DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()) {
If ($drvlist -notcontains $drvletter) {
$drv=New-PSDrive -PSProvider filesystem -Name $drvletter -Root $share
break
}
}
Here's what I came up with. I need the last available drive letter from A to Z.
$AllLetters = 65..90 | ForEach-Object {[char]$_ + ":"}
$UsedLetters = get-wmiobject win32_logicaldisk | select -expand deviceid
$FreeLetters = $AllLetters | Where-Object {$UsedLetters -notcontains $_}
$FreeLetters | select-object -last 1
This gets an array of letters A..Z
Then gets an array of the letters already in use from WMI
Next produces an array of letters not in use using the comparison operator -notcontains
Finally outputs a single letter.
$taken = Get-WmiObject Win32_LogicalDisk | Select -expand DeviceID
$letter = 65..90 | ForEach-Object{ [char]$_ + ":" }
(Compare-Object -ReferenceObject $letter -DifferenceObject $taken)[1].InputObject
Just for fun to shave an extra line of code (lol). If you wanted to be cloppy as heck you could skip instantiating variables and just pipe those directly into -Ref and -Diff directly, probably ought to be slapped for doing that though. :)
Selects [1] to avoid getting the A: drive just in case that might complicate matters.
I had to Write a function that works with Powershell V2.0.
The following Function will Return the next available letter, it also can get an exclude letter as parameter:
Function AvailableDriveLetter ()
{
param ([char]$ExcludedLetter)
$Letter = [int][char]'C'
$i = #()
#getting all the used Drive letters reported by the Operating System
$(Get-PSDrive -PSProvider filesystem) | %{$i += $_.name}
#Adding the excluded letter
$i+=$ExcludedLetter
while($i -contains $([char]$Letter)){$Letter++}
Return $([char]$Letter)
}
Let's say Your OS reports drive-letters C:,E:,F: and G: as being used.
Running: $First = AvailableDriveLetter ,Will result in $First containing 'D'
Running: $Sec = AvailableDriveLetter -ExcludedLetter $First ,Will result in $Sec containing 'H'
Another way...
$DriveList = Get-PSDrive -PSProvider filesystem | foreach({($_.Root).Replace(":\","")})
$AllDrives = [char[]]([int][char]'E'..[int][char]'Z')
$NextDriveLetter = ($AllDrives | Where-Object { $DriveList -notcontains $_ } | Select-Object -First 1) + ":"
I found to my own cost that the currently accepted answer (ls function:[d-z]: -n | ?{ !(test-path $_) } | random) can indeed return things like CD drives.
I've made this one to exclude any local drives from the array:
"$([char[]]([char]'D'..[char]'Z')|Where-Object {((Get-WmiObject -Class Win32_LogicalDisk).DeviceID).replace(':','') -notcontains $_ }|Select-Object -first 1):"
It will return the first available letter. If you'd prefer the last available letter just change Select-Object -first 1 to Select-Object -last 1
I found out that Test-Path evaluates my empty CD-Drive as False, here is another alternative that will compare every letter in the alphabeth until it finds one that doesn't exist in filesystem, then returns that drive as output.
$DriveLetter = [int][char]'C'
WHILE((Get-PSDrive -PSProvider filesystem).Name -contains [char]$DriveLetter){$DriveLetter++}
Write-Host "$([char]$Driveletter):"
just going to add one that works for remote drive letters
$computer will be the input and $driveletter would contain the next available drive on the remote computer
67..90 | foreach {if(((GWmi win32_logicaldisk -computer $computer -Property DeviceID).deviceID).Substring(0,1) -notcontains [char]$_){$driveLetter = [char]$_; break}}
might be able to shorten that but at that length its clear to see whats going on
This seems a bit like a "me too" answer, but I noticed that all the other answers use -contains or -notcontains and I just didn't like those solutions. So this may not be terribly efficient, but I like it better. The purpose of this code (for me) was to find the first drive that I can use to create a drive mapping.
$FreeDrive=Get-PSDrive -PSProvider FileSystem | Select-Object -ExpandProperty Name | Where-Object { ($_ -ne "A") -and ($_ -ne "B") -and ($_ -ne "C") } | ForEach-Object { [System.Convert]::ToByte([System.Convert]::ToChar($_)) }
$FreeDrive=#($FreeDrive)
if (($FreeDrive.Count -eq 1) -and ($FreeDrive[0] -ne "Z")) { $FreeDrive=[System.Convert]::ToChar($FreeDrive[0]+1) }
$j=0
while ((($FreeDrive[$j]+1) -eq $FreeDrive[$j+1]) -and ($j -lt ($FreeDrive.Count-1))) { $j++ }
$FreeDrive=[System.Convert]::ToChar($FreeDrive[$j]+1)
$FreeDrive

Convert GUID string to octetBytes using PowerShell

I have a powershell script which outputs all Exchange 2003 mailboxes by size.
$computers = "vexch01","vexch02"
foreach ($computer in $computers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, MailboxGUID | Export-Csv -notype -Path $computer.csv
}
Currently this outputs the MailboxGUID as a string type GUID (e.g. {21EC2020-3AEA-1069-A2DD-08002B30309D}). I want to look up users in AD by this, but AD stores them in octetBytes format.
I have found some powershell functions which will do the conversion but only when the curly braces are removed. The Guid.ToString method should supply this, but I can't get it to work in the above.
However, if I could figure out how to do that, the Guid.ToByteArray method might get me even closer.
Has anyone cracked this?
Update: the answers so far helped me write a function that converts the mailboxguid into the correct format for searching via LDAP. However, I now cannot get this working in the script. This is my updated script:
function ConvertGuidToLdapSearchString(
[parameter(mandatory=$true, position=0)]$Guid
)
{
$guid_object = [System.Guid]$Guid
($guid_object.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
# Gets data through WMI from specified Exchange mailbox servers
$servers = "vexch01","vexch02"
foreach ($server in $servers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} | Export-Csv -notype -Path $server.csv
}
I'm not sure why using the function in the select-object with #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} doesn't work.
Is there another way of using this function in select-object that will give the string?
In conjunction with Andy Schneider's answer, you may find this function useful:
function Convert-GuidToLdapSearchString(
[parameter(mandatory=$true, position=0)][guid]$Guid
)
{
($Guid.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
(I thought I had a more clever way to do this by adding a ScriptProperty to System.Guid, but I seem to have learned that you can't effectively add members to structs.)
I'm not sure I understand what you are trying to accomplish based on your comment, but I think you may have just left out a $_. Here is a somewhat contrived example that creates an object with a property that is a GUID, then uses select and Convert-GuidToLdapSearchString to convert the format. I hope it helps.
$o = New-Object PSObject -Property #{ GUID = $([Guid]::NewGuid()) }
$o
$o | select #{ Name='SearchString'; Expression={ Convert-GuidToLdapSearchString $_.GUID } }
This is not at all how I had imagined the function being used. I expected you would use it to create an LDAP search clause such as:
$searchString = Convert-GuidToLdapSearchString '{9e76c48b-e764-4f0c-8857-77659108a41e}'
$searcher = [adsisearcher]"(msExchMailboxGuid=$searchString)"
$searcher.FindAll()
Are you casting the string to a GUID ?
$guid = [System.Guid]"{21EC2020-3AEA-1069-A2DD-08002B30309D}"
$guid.ToString()
$guid.ToByteArray()