Create a second variable from the first variablbe - powershell

I'm trying to run a command again some mailboxes, after obtaining the mailbox names from a Retention Compliance Policy.
I can export the GUID to a CSV and then call the CSV, but I'm trying to limit my use of CSV's
The Below works with a CSV file
$Accounts = Import-Csv 'C:\Temp\MailboxInfoGUID.csv'
ForEach($Account in $Accounts)
{
$Guid = $null
$Guid = #()
$Guid = $Account.ArchiveGuid
Start-ManagedFolderAssistant -Identity $Guid -FullCrawl}
$Users = Get-RetentionCompliancePolicy $LabelPolicyName -
DistributionDetail | Select - ExpandProperty ExchangeLocation | Select -
ExpandProperty Name
I'd like to run the command without using a CSV file, but to get the mailbox identity I need to look in the Compliance Policy
How can I use the mailbox names obtained from the policy to then run a crawl on the mailbox

If I understood you correct, you would like the following:
Get the mailboxes from Get-RetentionCompliancePolicy.
Iterate through each of those mailboxes grabbing the GUID property.
Using Get-Mailbox.
Pass the newly obtained GUID to Start-ManagedFolderAssistant.
Given that's what you're after, the following should work:
(Get-RetentionCompliancePolicy $LabelPolicyName -DistributionDetail).ExchangeLocation.Name |
ForEach-Object -Process {
Start-ManagedFolderAssistant -Identity (Get-Mailbox -Identity $_).ArchiveGuid -FullCrawl
}
Due keep in mind that I don't have the ExchangePowerShell module installed so I am going off just logic; you may have to adjust the properties.

Related

Powershell improperly reading data in a CSV

I currently am writing a Powershell script that remotely removes users from a local admin group on a list of servers. The CSV headers are Computer and Name. For each entry of user (name), matches the server (computer).
Ex.
Computer,Name
Server1,User1
Server1,User2
Server2,User1
Script:
$List = Import-CSV C:\temp\LocalAdmin.CSV
$user = $List.Name
$objGroup = $List.Computer
write-host "Removing user" $user "from server" $objGroup "local admin group:" -ForegroundColor Green
Invoke-Command -ComputerName $objGroup -ScriptBlock {Remove-LocalGroupMember -Group "Administrators" -Member $using:user }
write-host "Completed."
When the script runs, it runs through perfectly fine the first time through, but then it runs through the script line by line for how many ever lines there are causing it to attempt to remove the users multiple times. Can someone help me fix this logic? It is almost like the CSV is being read as an array vs a list. I appreciate the help!
I would say:
$List = Import-CSV C:\temp\LocalAdmin.CSV
ForEach ($Item in $list)
{
$user = $Item.Name
$objGroup = $Item.Computer
write-host "Removing user $user from server $objGroup local admin group:" -ForegroundColor Green
Invoke-Command -ComputerName $objGroup -ScriptBlock {Remove-LocalGroupMember -Group "Administrators" -Member $user }
}
write-host "Completed."
Not tested, but this should be your starting point.
As for what you tried:
Given that $List contains an array of objects, with each element containing an object representing a row of the CSV file, $List.Name and $List.Computer similarly return arrays of property (column) values, courtesy of PowerShell's member-access enumeration
Therefore, $using:user refers to the array of all usernames, across all servers.
While the -Member parameter of Remove-LocalGroupMember does accept arrays, there are two problems with your approach:
At least hypothetically you'll run the risk of deleting users you shouldn't from certain servers, or you'll run into users that don't exist on a given server (though you could ignore that with -ErrorAction Ignore).
Since a given server name can appear multiple times in the CSV, the targeted user(s) will have already been deleted, starting with the second call to that server - this is the problem you saw.
TheStingPilot's helpful answer
provides an effective solution: loop over the objects representing the CSV rows one by one, and call Invoke-Command for each target server, with only the username at hand.
The downside of this approach - which may or may not matter, depending on how many computers you target - is that forgo the benefits of parallel execution that you get when you pass multiple computer names to Invoke-Command's -Computer parameter (by default, up to 32 computers at a time are targeted in parallel; you can modify that number with -ThrottleLimit).
To avoid multiple calls to a given server while preserving the benefits of parallel execution:
Build a hashtable from the CSV input that maps server names to user names.
Pass that hashtable to a single Invoke-Command call, as you tried, and let each remote computer look up the relevant usernames in it and act on them.
# Read the CSV file and create a hashtable (map) that
# maps server (computer) names to usernames to remove.
$serverUserMap = [ordered] #{}
Import-CSV C:\temp\LocalAdmin.CSV |
ForEach-Object {
[array] $serverUserMap[$_.Computer] += $_.Name
}
# * $serverUserMap.Keys now contains all unique server names,
# which can be passed to -ComputerName
# * Inside the script block, accessing the hashtable with the local
# computer name as the key returns only the relevant user(s).
Invoke-Command -ComputerName $serverUserMap.Keys -ScriptBlock {
Remove-LocalGroupMember -Group "Administrators" -Member ($using:serverUserMap)[$env:COMPUTERNAME]
}

Build variables from properties in Powershell script

I am trying to build a script to gather the DFSR backlog details of a list of servers.
So far the script will query a text file for a list of servers.
From that list it will use various Powershell commands to determine what replication groups it is a member of and what connections it has. Once I have that data stored in variables I can then use it in dfsrdiag backlog to check the status.
The problem I have is how can I capture and set select properties to variables that I can use to create the dfsrdiag command.
Anyone out there know the best way to select the particular properties and store then to variables in Powershell?
Cheers
Woodsy
Here is a simple example using get-service. You can create an object called $report that contains only the properties you want. You can export $Reportto a CSV.
All you need to do is apply this to your script.
$Services = Get-Service | Where-Object {$_.name -like "A*"}
[Array]$Report = foreach ($service in $Services)
{
[PScustomobject]#{
Name = $service.DisplayName
Shortname = $service.name
Status = $service.Status
StartType = $service.StartType
}
}
$Report | select Name, Shortname, Status, StartType

Powershell GPO Login Script checking AD resource group membership

The system I have to work with uses AD resource group membership to manage most of the permissions for users and computers. I have been asked to improve the current logon script as it currently contains some VB ADSISEARCHER calls. I started trying to do this purely in powershell but have hit a number of hurdles.
Target machines do not have the Active Directory Module installed
The users logging into the system have a restricted user accounts
The resource groups are nested so the script needs to handle this
I have tried a couple of approaches firstly the pure Powershell Cmdlet method of Get-ADGroup or Get-ADPricipalGroupMembership but these Cmdlet's require the Active Directory Module. Then I tried the .net approach with System.DirectoryServices.DirectoryEntry although this is a step away from a pure PowerShell solution at least it isn't as Legacy as the VB route. However when I try to build the object it also appears to be missing the name space.
First Attempt:
function Get-UserResourceMembership
{
[CmdletBinding()]
Param
(
# Username or Groupname to Discover Group Membership
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$User
)
Begin
{
$Groups = #(Get-ADPrincipalGroupMembership $User)
}
Process
{
foreach($ADGroup in $Groups)
{
if($ADGroup.ObjectClass -eq "Group")
{
Get-UserResourceMembership $ADGroup
}
$GrpMembership = #($ADGroup)
}
}
End
{
return ,$GrpMembership
}
}
Second Attempt:
# $rootGroup is passed in from earlier in the script
$groupname = $rootGroup.'Group Name'
$filter = ("(&(objectCategory=Group)(name=$($groupname)))")
$searcher.Filter = $filter
$searcher.SearchScope = "Subtree"
$searchResults = $searcher.FindAll().GetDirectoryEntry().memberOf |
% { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } |
Sort-Object Children | select #{name="Group Name";expression={$_.Name}}
foreach($resource in $searchResults)
{
if($resource.'Group Name' -match "<Groupname>")
{
$printResource += $resource
}
}
Does anyone in the community have any suggestions how to pull group membership [nested] from Active Directory from a standard users login script??? Any idea's much appreciated....
PS I can't change the way the system is designed (above my pay grade).
As for ...
• Target machines do not have the Active Directory Module installed
• The users logging into the system have a restricted user accounts
• The resource groups are nested so the script needs to handle this
Does not matter, they do not need to be installed on a client to use
them. You can use PSRemoting to proxy those using 'Implicit
Remoting'. The cmdlets are only available in the remote session.
Does not matter, as every user has read access, by default to ADDS
in Windows.
You can get to those using the cmdlets you are using and
there are even pre-built scripts in the Microsoft PowershellGallery.com
for this as well.
As for …
I have tried a couple of approaches firstly the pure Powershell Cmdlet
method of Get-ADGroup or Get-ADPricipalGroupMembership but these
Cmdlet's require the Active Directory Module.
As noted above, this can be addressed as described below:
PowerShell Implicit Remoting: Never Install a Module Again
Remote Session
# create a session then import a module via the session, for example:
$adsess = New-PSSession -ComputerName savdaldc01
Import-Module -Name ActiveDirectory -PSSession $adsess
Get-Module
Get-ADUser -Filter *
Remove-Module ActiveDirectory
# It's also possible to prefix modules loaded from remote servers to differentiate from local modules, e.g.
Import-Module -Name ActiveDirectory -PSSession $adsess -Prefix OnDC
Get-OnDCADUser -Filter * #I don't have regular Get-ADUser anymore
Remove-Module ActiveDirectory
Remove-PSSession $adsess
As for ...
Does anyone in the community have any suggestions how to pull group
membership [nested]
Get nested group membership - function
This function will recursively enumerate members of a given group
along with nesting level and parent group information. If there is a
circular membership, it will be displayed in Comment column.It accepts
input from pipeline and works well with get-adgroup. Download:
Get-ADNestedGroupMembers.ps1
As well as just doing this...
We can get group members by using the Active Directory powershell
cmlet Get-ADGroupMember. The Get-ADGroupMember cmdlet provides the
option to get all the nested group members by passing the parameter
-Recursive. This powershell script also handles circular membership (infinite loop) problem.
Function Get-ADNestedGroupMembers
{
[cmdletbinding()]
param
(
[String] $Group
)
Import-Module ActiveDirectory
($Members = Get-ADGroupMember -Identity $Group -Recursive)
}
Get-ADNestedGroupMembers "Domain Admins" | Select Name,DistinguishedName
or this way.
function Get-NestedGroupMember
{
param
(
[Parameter(Mandatory, ValueFromPipeline)]
[string]$Identity
)
process
{
Import-Module ActiveDirectory
$user = Get-ADUser -Identity $Identity
$userdn = $user.DistinguishedName
$strFilter = "(member:1.2.840.113556.1.4.1941:=$userdn)"
Get-ADGroup -LDAPFilter $strFilter -ResultPageSize 1000
}
}
All of the methods below list all groups including nested groups.
The example below would execute a gpresult command in the user's context. The gpresult outputs to an XML file within the user's local profile, which they should have full access to already. Then the XML file is read and traversed through each node until you reach the node containing the groups. The group list contains local and domain groups and is outputted directly to the console. This can easily be stored in a variable or output to a file. If you only want domain groups, that could easily be filtered from here with a Regex. It requires that the client machines are running at least Windows Vista SP1 or later.
gpresult /USER "$env:userdomain\$env:username" /X "$env:userprofile\rsop.xml"
$xml = [xml](Get-Content "$env:userprofile\rsop.xml")
$xml.Rsop.UserResults.SecurityGroup.Name."#text" # Displays the groups
Remove-Item "$env:userprofile\rsop.xml" # Removes the XML file
You could also use a potentially use Regex matching to find the group list:
$out = gpresult /R /USER $env:username
$GroupsUnfiltered = (($out | out-string) -split "-{10,}")[-1]
$Groups = ($GroupsUnfiltered.trim() -replace "(?m)^\s+","") -split "(?m)\r?\n"
$Groups
The following can also work if your group list always begins with a predictable group like Domain Users in this example:
$out = gpresult /R /USER $env:username
$GroupList = $out.where({$_ -match "domain users"},'SkipUntil').trim()
$GroupList
This code assumes that the users and machines are joined to the same domain or are at least joined to trusted domains. The second code snippet assumes every user is in the Domain Users group and the machines are natively PowerShell v4 or higher.

Update Job Descriptions in AD using Email Addresses Using PowerShell

I am currently attempting to write a powershell script that will update the Job Description of Various Users in Active Directory via a CSV File
The identifier I have been given to use is the User's Email Address instead of Username (Which I think would of been easier!)
Can anyone assist as I am struggling to write anything effective that works! :(
Ok, suppose your .csv looks like this:
"email","jobtitle"
"user1#mydomain.com","New job description for user1"
"user2#mydomain.com","New job description for user2"
"user3#mydomain.com","New job description for user3"
you could then do something like
Import-Module ActiveDirectory
Import-CSV -Path <PATH-TO-YOUR-CSV-FILE> | Foreach-Object {
# properties from the csv
$mail = $_.email
$title = $_.jobtitle
Get-ADUser -Filter {(mail -eq "$mail")} | Set-ADUser -Title $title
}

Printer Migration - Powershell script

I have found some great examples on foreach loops in Powershell here but I just can't wrap my head around foreach loops for what I am doing.
I found great scripts that deal with migrating printer when migrating from one Windows print server to another however my challenge is that I am migrating from an Novell iPrint server to a Windows server.
The struggle is that the printer name or share name (or any printer property) for iPrint printer is not the hostname so I have to come up with some translation table with iPrint name and Printer hostname.
Initially, I wanted to just have column 2 of my translation table have it execute my powershell command to install a network printer which would make things easier.
I am in the process of trying to create a logon script to query printers that are installed on computer and have it do a 'foreach' loop against a CSV with iPrint names and hostnames.
csv 1
installediprintprintername1
installediprintprintername2
installediprintprintername3
printtranslationtable.csv
column 1 column 2
iprintprintername1 hostnameprinter1
iprintprintername2 hostnameprinter2
iprintprintername3 hostnameprinter3
iprintprintername4 hostnameprinter4
This is what I got so far but not able to get it to work. Any help would be appreciated!
$printers = #(Get-wmiobject win32_printer)
$path = "\\networkdrive\printtranslationtable.csv"
$printertranslation = Import-Csv -path $path
foreach ($iprintprinter in $printtranslationtable) {
foreach ($name in $csv1) {
if ($name -eq $printtranslationtable.column1) {
Write-Host $newPrinter = $printtranslationtable.column2
}
}
}
Update
So I was able to tweak the script #TheMadTechnician suggested and able to get this PS script to work in my environment. What I am trying to do is to check if new printers are installed and if they are then just exit script. This is what I have but can't get it to exit or break. I was also trying to write the new printers into text file but not necessary, I would like for it to stop executing script.
if (($printers.name -like "\winprint*") -eq $true) {
$printers.name -like "\winprint\" | out-file -FilePath "C:\windowsprinters.txt" -Append
{break} {exit}
}
When you read the file with Import-Csv, PowerShell creates an array of custom objects with property names from the header line. On the other hand Get-Content produces simple array of string values. I came up with this one liner, which goes thru the translation table and checks if the printer list contains one. This is not optimal if you have billions of printers, but keeps things clear:
printers.txt:
iprinter2
iprinter3
printertable.csv:
"Column1";"Column2"
"iprinter1";"hostname1"
"iprinter2";"hostname2"
"iprinter3";"hostname3"
"iprinter4";"hostname4"
PowerShell:
$printers = Get-Content .\printers.txt
$prtable = Import-Csv -Delimiter ";" .\printertable.csv
$prtable | ?{ $printers -contains $_.Column1 } | %{Write-Host "Install $($_.Column2)"}
Ok, so you query what printers are installed, and you have a translation table loaded from a CSV, now you just need to look at that translation table and cross reference which entries have a listing in the local computer's printer listings.
$printers = #(Get-wmiobject win32_printer)
$path = "\\networkdrive\printtranslationtable.csv"
$printertranslation = Import-Csv -path $path
$printertranslation | Where{$_.Column1 -in $printers.ShareName} | ForEach{ Add-Printer $_.Column2 }
I don't know what property of the win32_printer object aligns best for you, but I would suggest ShareName or DeviceId. Those should be something like:
ShareName: XeroxColor02
DeviceId: \\printserver\XeroxColor02