The Exchange PowerShell commands for Mailbox Folder Statistics and Permissions are disjointed and require you to massage data to take statistics and make them usable as variables for removing folder permissions.
I'm trying to use the replace commands in PowerShell to manipulate the values without breaking the array itself.
I've tried various ways of using the -replace command to handle this as it has been unsuccessful.
I'm trying to use code similar to this:
Get-MailboxFolderStatistics -Identity jon#towles.com | Select Identity | ForEach-Object { $_."Identity" -replace '.com','.com:'}
When I use the replace function, it breaks the array so we no longer see headings and cannot use it with stuff like foreach-object {Remove-MailboxFolderPermissions -identity $_.identity -user testuser}
I expect that the replace function will still keep the data layout.
If you want to maintain your psobject structure, you need to avoid dereferencing properties or expanding properties. In your case, you can use a calculated property in your Select-Object command.
Get-MailboxFolderStatistics -Identity user#domain.com |
Select-Object #{Name='Identity';Expression={$_.Identity.Replace('.com','.com:')}}
Your current pipeline object $_ is a psobject with accessible properties. When you use the dereference operator ., you are retrieving a value for one of the properties. $_.Identity produces a different object. Since you do not incorporate that value back into a custom object, the only properties you have available are ones available to its object type, which does not include Identity.
With that said, you don't technically need to maintain your object schema to perform subsequent tasks. Even if you output a string with your first command, you can store that string in a variable and use it in another command. If your plan is to use a Foreach-Object to update all of your objects, you can update the pipelined object for future use within the loop.
Get-MailboxFolderStatistics -Identity user#domain.com | ForEach-Object {
$_.Identity = $_.Identity.Replace('.com','.com:')
Remove-MailboxFolderPermissions -Identity $_.Identity -User testuser
}
Related
I am trying to utilize PowerShell to audit all of our security group members in AD. I have been trying to get Get-ADGroupMember to work but anytime I try it, it returns the message 'Cannot find an object with identity 'groupName' under: 'DC=xxxx,DC=xxxx,DC=xxxx,DC=xxxx'.
Ive tried the following with no luck:
$groupNames = 'groupName1' , 'groupName2' , 'groupName3'
foreach ($group in $groupNames) {
Get-AdGroupMember -Identity $group
}
Has anyone successfully compiled a list of group members in security groups from AD and exported them into a .CSV?
There are few things to consider when querying AD groups using the Get-AdGroup* commands.
The -Identity parameter only accepts values that match an object's Guid, ObjectSid, DistinguishedName, or SamAccountName. If your input is something other than one of those attribute values, you will either need to run another command to retrieve the proper data or change your list.
-Identity only accepts a single value, which means if you want to supply a list of values, you need to loop through them.
Get-AdGroupMember does not output as many attribute/value pairs as Get-AdUser. You cannot force it to output more attributes than it does. It does not have a -Properties parameter like Get-AdUser. Sometimes it requires using both commands to get all of the required data.
You can send Get-Ad* output to CSV using Export-Csv. If you do not use any property filtering like with Select-Object, the returned property names will be the columns of the CSV. The associated values of the properties will appear in rows with each row representing one returned object. You can choose to either send the entire results of the command once to the CSV or each time the command runs using Export-Csv -Append.
Use Select-Object to only output properties you care about. Select-Object Property outputs a custom object that includes only the property Property and the value(s) of Property for each object returned. If you only want to return the value rather than a custom object, you can use Select-Object -Expand Property.
Get-Content can be used to read a file. If the file contains only a list of values, perhaps SamAccountName values, you can use Get-Content file.txt to retrieve that list. The list will be an array that can be looped through.
Since Get-AdUser can be verbose, it is wise to use the -Properties parameter to explicitly list any extra properties beyond the default set you want to return. -Properties * will return all properties, but that is not best practice.
Given the above considerations, I would do the following:
$groupNames = 'groupName1' , 'groupName2' , 'groupName3'
# Alternatively, if you have a file (file.txt) with your group names listed as one group per line
$groupNames = Get-Content file.txt
# The Foreach-Object section is only needed if $groupNames does not contain a valid -Identity value
# The Filter below uses Name attribute as an example because it assumes $groupNames contains Name attribute values. If it contains another attribute, update the filter accordingly.
$SamAccountNames = $groupNames | Foreach-Object {
Get-AdGroup -Filter "Name -eq '$_'" | Select-Object -Expand SamAccountName
}
# Storing the loop output into a variable is efficient provided you have enough memory for the operation.
# Alternatively, you can just pipe the `Get-AdGroupMember` into `Export-Csv -Append` but that could be a lot of writes!
$output = foreach ($group in $SamAccountNames) {
Get-AdGroupMember -Identity $group # You can use Select-Object here for specific properties
}
$output | Export-Csv -Path output.csv -NoTypeInformation
Hopefully this answer isn't above me. I've created a custom object with properties and methods. I create several of them on the fly, depending on what the user selects at the beginning.
So for this example, the script might create $PRD1, $PRD2, $TST1 and $TST4.
$PRD1, $PRD2, $TST1 and $TST4 will have some properties like DebugMode, DisableAppsStartTime, DisableAppsStopTime. They'll have some methods like DisableApps(), EnableApps().
How can I find out which variables the script ended up creating? I can use Get-Variable to know the ones it created (plus I DO still have the initial list of names to create). My issue is that I'm having trouble figuring out to call the ones I've created, in a manner that allows me to use the methods and properties, without a ridiculous mash up of nested foreach/if/switch commands.
I certainly hope that made sense.
Thanks in advance,
SS
I DO still have the initial list of names to create
Assuming that $list contains this list, the following creates an (ordered) hash table of those variables that were actually created from that list:
$variableMap = [ordered] #{}
(Get-Variable -ErrorAction Ignore -Scope Local $list).
ForEach({ $variableMap[$_.Name] = $_.Value })
Note: -Scope Local limits the lookup to the current scope[1]; omit it to target all variables visible in the current scope, which includes those from ancestral (parent) scopes.
You can then loop over $variableMap.Keys to process them all, or access one by name selectively, e.g., $variableMap.PRD1 (or $variableMap['PRD1']).
You then use regular dot notation to access properties and methods of these entries; e.g., $variableMap.PRD1.DisableApps().
[1] This includes variables created with the AllScope option, e.g., $HOME, because they are copied to every scope, as the name suggests. You can find all such variables with
Get-Variable | Where-Object Options -match 'AllScope'
I just did this with the where-object cmdlet and the -like operator with an foreach loop.
foreach($var in (get-variable | Where-object {$_.name -like '*PRD*' -or $_.name -like '*TST*'})){
$var
}
Can anyone explain to me the differences I'm seeing in either using a | to pipe one command to another or using $ to 'pipe' it a different way (sorry not sure if the use $ is actually considering piping).
So… this works:
Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" } |
Get-MailboxPermission
Which is great, however because I want to place another where command after the Get-MailboxPermission which doesn't work above I then tried to use this:
$Mailbox = Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
Get-MailboxStatistics -Identity $Mailbox |
where { $_.IsInherited.tostring() -eq "False" }
It causes me to get this error:
Cannot process argument transformation on parameter 'Identity'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "Microsoft.Exchange.Configuration.Tasks.GeneralMailboxOrMailUserIdParameter".
Surely using | or $ is the same in the sense that it pipes through the results to the next command or am I completely wrong?
I don't have an exchange shell here to test but I guess I can still explain the basics and point you in the right direction.
The pipe | is used to redirect output from one command to another command. $ in Powershell is the character which defines that the character sequence right behind it is either a variable (e.g. $Mailbox as an example for a normal variable or $_ as an example for a variable that holds data that has been piped through from a previous command) or an expression. An example for an expression one is $(4+5).
Or in a more frequently used example:
PS C:\Users\Administrator> $file = (get-childitem)[0]
PS C:\Users\Administrator> write-output "The fullname of $file is $($file.fullname)"
The fullname of .ssh is C:\Users\Administrator\.ssh
In that example it is actually necessary to use an expression, because variable detection inside a string doesn't recognize dots as separator between variable and a variable member (fullname is a member of $file).
If it's not clear to you why there is a point and what members are, you should probably look into object oriented programming a bit because Powershell is object oriented through and through.
In your 2nd example you just save everything that's returned by your Get-Mailbox command in the $Mailbox variable. The $Mailbox variable is available as long as you don't delete it or leave its scope (in this case, the powershell session). You can actually use the variable as input for multiple commands without losing its data.
When using the pipe, the data returned by your first command is only accessible for the command behind the pipe and after that it's gone.
That's probably the difference you're interested in.
As for your actual problem: Powershell tells you that it's not expecting to be handed a variable of type System.Collections.ArrayList, which is what Get-Mailbox returns. The technet help is unclear as to what Get-Mailbox specificly returns, but I strongly guess it's an ArrayList of Mailbox-Objects. You can check it like this:
$Mailbox.GetType()
$Mailbox[0].GetType() # gets the type of the first object in $Mailbox
To fix your code, you need to loop over what's been returned by Get-Mailbox. Try this:
$Mailboxes = Get-Mailbox -ResultSize Unlimited | where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
$Mailboxes | ForEach-Object { Get-MailboxStatistics -Identity $_ }
The ForEach-Object cmdlet loops over an array or a list and works on each item individually.
Your first example works so far because Powershell has been made smarter about piped data a few versions ago (See paragraph about 'Member Enumeration'). It's actually ForEach-ing over the passed in data.
Follow up links:
The $_ variable in powershell
Powershell is an object oriented language
Sorry to have to say this, but you're completely wrong. Pipelines and variables are two entirely different things.
The pipe (|) connects the output of one cmdlet to the input of another cmdlet. List output is processed one item at a time, i.e. the second cmdlet receives each list item separately.
If you collect a list of items in a variable ($mailbox) and call a cmdlet with that variable you're passing the list object instead of individual list items. That only works if the cmdlet parameter accepts array input.
The pipe operator | i used to flow the output of one command into the input of another command.
The dollar symbolc, $ is used to denote that the name following it is a variable, and has nothing to do with piping data between cmdlets. The where cmdlet create a $_ variable for use within its expression.
I have the power shell below that selectes certain fields
dir -Path E:\scripts\br\test | Get-FileMetaData | Select-Object name, Comments, Path, Rating
what i want to do is utilize Name,Comments,Path,Rating in further Pipes $_.name etc dosnt work
If I understand your question correctly, you want to do something with the output of Select-Object, but you want to do it in a pipeline.
To do this, you need to pass the output down the pipeline into a Cmdlet that accepts pipeline input (such as ForEach-Object). If the next operation in the pipeline does not accept pipeline input, you will have to set the output to a variable and access the information through the variable,
Using ForEach-Object
In this method, you will be processing each object individually. This will be similar to the first option in Method 1 (that is, dealing with individual items in the collection of items returned by Select-Object).
dir | Get-FileMetaData | Select-Object Name,Comments,Path,Rating | ForEach-Object {
# Do stuff with $_
# Note that $_ is a single item in the collection returned by Select-Object
}
The variable method is included in case your next Cmdlet does not accept pipeline input.
Using Variable
In this method, you will treat $tempVariable as an array and you can operate on each item. If need be, you can actually access each column individually, getting everything at once.
$tempVariable = dir | Get-FileMetaData | Select-Object Name,Comments,Path,Rating
# Do stuff with each Name by using $tempVariable[i].Name, etc.
# Or do stuff with all Names by using $tempVariable.Name, etc.
I want to reference properties selected from a cmdlet and then use them later on in my script as $mailboxarray.totalitemsize and $mailboxarray.totalitemsize
This is breaking later on in my script as I assume it doesn't like the type of object it is. How can I make anything I pull into the references a string? At the moment I am getting "No mapping exists from object type System.Management.Automation.PsObject to a known managed provider type". My code at the moment: the first command works fine but I can't use expandproperty when there are multiple entries like line two?
$recipienttype = Get-mailbox -identity $recordset.PrimarySmtpAddress | Select -expandproperty RecipientTypeDetails
$mailboxarray = Get-Mailbox -identity $recordset.PrimarySmtpAddress | Get-MailboxStatistics | Select-object totalitemsize, lastlogontime