Access objects using variable name powershell - powershell

I am unable to access my object using .PropertyName.
I have tried using $val.Options.$propertyName but it yields no result.
$propertyName is a value input from a file
`$val.$propertyName` results in "Cannot index into null array"
$result = New-Object -TypeName 'System.Collections.ArrayList';
foreach ($user in $users) {
$val = Get-LocalUser $user | Select *
$val = $val.$propertyName
$result.Add($val)
}

In your context $val.$propertyName does't mean anything can you try :
$result = New-Object -TypeName 'System.Collections.ArrayList';
foreach ($user in $users) {
$val = Get-LocalUser $user
$result.Add($val)
}
$result will be an array of "Localuser".

You don't need an arraylist at all. Just let PowerShell do the collecting by capturing the output inside the loop
$result = foreach ($user in $users) {
(Get-LocalUser $user).$propertyName
}
This is assuming your variable `$propertyName` contains a valid attribute name
While the above code does what you've asked for, I don't think the result would be very helpful, because it just lists whatever is in the property stored in $propertyName, and you cannot see which user has what values in there.
A better approach would be to output objects with the properties you want returned.
Something like
# just an example..
$propertyName = 'Description'
$users = 'WDAGUtilityAccount', 'Administrator'
$result = foreach ($user in $users) {
output an object with the name of the user, and also the property you are after
Get-LocalUser $user | Select-Object Name, $propertyName
}
$result
Since parameter -Name (the first positional parameter) can take an array of usernames, this can be shortened to:
$result = Get-LocalUser $users | Select-Object Name, $propertyName

Related

No output to CSV using PowerShell psobject in Active Directory

I have this portion of code here that has worked in the past in multiple AD environments, however after testing within a new AD environment, I am getting no output to CSV or any errors being thrown. The size of the CSV file is always zero.
if (![string]::IsNullOrEmpty($searchbase))
{
$ADComputers = get-adcomputer -searchBase $searchbase -filter * -properties * -ResultPageSize $resultpagesize
}
else
{
$ADComputers=Get-ADComputer -Filter * -Properties * -ResultPageSize $resultpagesize
}
$data = #()
foreach ($computer in $ADComputers) {
$computer.member| foreach-object {$members += $_}
$computer.memberof | foreach-object {$memberof += $_}
$memstr = ($members -join ";")
$memstr2 = ($memberof -join ";")
$ADcomp = Get-ADComputer $computer -properties logonCount, ManagedBy | select-object logonCount, ManagedBy
$row = New-Object -TypeName psobject -Property #{
PrincipalID = $script:ctr;
logonCount=$ADcomp.logonCount;
ManagedBy=$ADcomp.ManagedBy;
}
$data += $row
$script:ctr++
}
$data | Export-Csv "ADComputers.csv" -NoTypeInformation
I'm not sure exactly where to go from here because I have tested multiple different options, so any help would be greatly appreciated!
The only reason you get no output is that $ADComputers has no elements. This may be related to a value in the variable $searchbase that does not exist or simply has no computer accounts in it.
But here are some general recommendations:
you do:
if (![string]::IsNullOrEmpty($searchbase))
you could also do:
If ($searchbase)
In principle if you have different scenarios to cover and so the parameter may change take a look at splatting.
Then you query all computers with all available properties but later in the loop you query the specific computer again which is not necessary. Also you should avoid adding elements to an array by using +=, this causes to rebuild the array each time which is slow.
Furthermore $computer.memberof is already an array holding the information but you pipe it to foreach and build a new array holding the identical information only to join it later to a string.
If this is not part of function I don't know why you raise the scope of the variable $ctr from local to script think this is not necessary.
Putting this all together you could do:
#Define HashTable for splatting
$parametersHt = #{
filer='*'
properties='*'
resultsetpagesize=$resultpagesize
}
#If searchbase is specified add parameter to splatting HashTable
If ($searchbase){
$parametersHt.add('Searchbase',$searchbase)
}
#Get computers
$ADComputers = get-adcomputer #parametersHt
#Set Counter
$ctr = 0
$data = #(
foreach ($computer in $ADComputers){
$ctr++
[PSCustomObject]#{
PrincipalId = $ctr #Really the counter here - not $computer.samaccountname?
logonCount=$computer.logonCount
manageddBy=$computer.ManagedBy
memberof=($computer.memberof -join ";") #added this, but in your code sample you don't return this value, if not needed remove. btw. a computer can be memberof a group but it can't have members
}
}
)
$data | Export-Csv -path ".\ADComputers.csv" -NoTypeInformation

find members of groups excluding disabled users

I have 10 security groups all that are called " 'companyname' RDS Users"
I am trying to create a script that does the following: List all the groups and then list all of the members excluding the disabled members, then have it email a csv. I have done the following but cant get the disabled user excluded.
The Script belows shows how far i got but the disabled users show in there which basically means the script is pointless.
$mailServer = ""
$mailFrom = ""
$mailTo = ""
$mailSubject = ""
$file = "somepath\RDSUsers.csv"
Import-Module ActiveDirectory
$US = Get-ADUser -Filter * -Property Enabled |where {$_.Enabled -eq "True"}| FT Name, Enabled -Autosize
$Groups = (Get-AdGroup -filter * | Where {$_.name -like "*RDS Users" -and $_.name -ne "RDS Users"}| select name -expandproperty name)
$Table = #()
$Record = [ordered]#{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
Foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $Group |select name,samaccountname
foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $Group
$Record."Name" = $Member.name
$Record."UserName" = $Member.samaccountname
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
}
if ($Table -eq "RDS Users") {}
$Table
there is usualy a line here that sends the email with excel attachment
The following should produce the output you want in the $Table variable. You can then pipe $Table to one of the format-* commands.
Import-Module ActiveDirectory
$US = Get-ADUser -Filter "Enabled -eq '$true'" -Property Enabled
$Groups = Get-ADGroup -Filter "Name -like '*RDS Users' -and Name -ne 'RDS Users'" |
Select-Object -ExpandProperty Name
$Table = Foreach ($Group in $Groups)
{
try
{
$Arrayofmembers = Get-ADGroupMember -Identity $Group -ErrorAction Stop | Select-Object Name, SamAccountName
$compare = Compare-Object -ReferenceObject $US -DifferenceObject $Arrayofmembers -ExcludeDifferent -IncludeEqual -PassThru -Property SamAccountName -ErrorAction Stop |
Select-Object Name, SamAccountName
$compare | ForEach-Object {
[pscustomobject]#{
"Group Name" = $Group
"Name" = $_.Name
"UserName" = $_.SamAccountName
}
}
}
catch
{
[pscustomobject]#{
"Group Name" = $Group
"Name" = $null
"UserName" = $null
}
Continue
}
}
$Table
Explanation:
The Get-ADGroupMember command will not provide the Enabled property of its returned objects. You will need to feed its output into another command like Get-ADUser for that data. Since you already stored all of the enabled users in $US, we can simply compare $US collection to the results of each Get-ADGroupMember output.
I removed most of the Where-Object commands in favor of using the -Filter parameter on the AD commands. Almost always, the -Filter parameter will be faster especially when you are comparing AD indexed attributes like Name and Enabled.
You do not need to store each output object in a variable unless you are going to further manipulate it. This is why $Record was removed. Instead, all returned objects are stored in the array $Table. I removed the += operator mainly because of its inefficiency when repeatedly building arrays. Also, you can simply set a variable to the output of a foreach loop, which will result in the array you require. Since we created a custom object on each loop iteration and provided the properties at the time of declaration, [ordered] is not required. However, if you create the hash table first and then create a corresponding object, you will potentially need to use [ordered]. As an aside when you are creating custom objects that are involved in a loop, it is usually best practice to create a new object each time. Otherwise, you could unintentionally update values on the wrong objects. Just because you add an object to an array, you can still update its properties after the fact.
The Compare-Object command ties everything together. The -ExcludeDifferent -IncludeEqual parameter combination will only output objects with matching property values. Since we are comparing $Arrayofmembers and $US, that is ideal. The -PassThru switch allows the objects to be returned with all of the properties that were passed into the command. Then you can use the Select-Object command to pick which properties matter to you.

Proxyaddresses added to the multivalued attribute not appearing on seperate lines

I am using the following script to create a spreadsheet of users and proxyaddress values (matching a string) as a record prior to removing them, in the case of an emergency I want to be able to put the values back.
The script works well if there is only a single entry per user, the issue is seen when a user has more than one entry. The problem is that when I attempt to revert back to the original values, when viewed through an attribute editor the proxyaddresses are appearing all on one line instead of a separate line for each proxy address. I am not sure if the fault is on the collection script, or the script I am using to set the values. To collect the values I am running the following:
$entry = "*test.com"
$users = get-content "Testusers.txt"
$date = get-date
$mydata = #()
$domain = [system.environment]::UserDomainName
$attribute = "Proxyaddresses"
$report = "{0}_{1}_{2:HHmm_dd-MM-yyyy}.txt" -f $attribute,$domain,$date
$px = $users | Get-ADUser -Properties proxyaddresses -server $domain
foreach ($user in $px){
if ($user.proxyaddresses -like $entry){
$name = $user.samaccountname
$proxyaddresses = $user.proxyaddresses -like $entry
$mydata += New-Object PSObject -Property #{
Samaccountname = $name
Proxyaddresses = $proxyaddresses
}
}
}
$mydata | select samaccountname,#{ l = "Proxyaddresses"; e = {$_.Proxyaddresses } }| export-csv "$PWD\$report" -NoTypeInformation -Append
To return the values back to the original state I am running the following:
$csv = import-csv "Proxyaddresses_Domain_1201_05-04-2017.csv"
$domain = [system.environment]::UserDomainName
$attribute = "Proxyaddresses"
foreach ($line in $csv){
$user = $line.samaccountname
$proxyaddresses = $line.proxyaddresses
Get-aduser $User -server $domain -Properties $attribute | Set-ADUser -add
#{$attribute = $proxyaddresses} -server $domain
}
I have tried various things such as in the collection script
$proxyaddresses = $line.proxyaddresses -join ","
I have also tried the same tactic in the script used to set the values but instead of creating a new line it removes a space between the entries on the same line when viewed through an AD attribute editor.
I have also tried to change
$proxyaddresses = $user.proxyaddresses -like $entry
to the following however this did not solve the problem
$proxyaddresses = ([Microsoft.ActiveDirectory.Management.ADPropertyValueCollection]$user.proxyaddresses -like $entry
HELP!!!
When you are exporting the data, change your select statement from this
select samaccountname,#{ l = "Proxyaddresses"; e = {$_.Proxyaddresses } }
to this
select samaccountname,#{Name="Proxyaddresses";Expression={$_.proxyaddresses -join "*"}}
Which will convert the ADPropertyValueCollection to a * separated string. If you are exporting to a CSV, you don't want to use a comma as the join separator as it will mess up the CSV. I've used a * in the example, as exchange uses colons and semi colons already.
Then when you re-import it, just make sure to convert the * seperated string back into a string array, and use -replace instead of -add
Set-ADUser -replace #{$attribute = $proxyaddresses.Split("*")} -server $domain

iterate over csv files and create contacts with attributes

I currently have the below script that will create a contact with certain attributes I require.
New-ADObject -name SGTContacttest5 -Type Contact -path "OU=SGTestOU,OU=Contacts,DC=DC,DC=Company,DC=local" -OtherAttributes #{
'department'="America";
'givenName'="SG";
'sn'="Test";
'displayname'="SG Test";
'title'="Job Title";
'telephoneNumber'="020";
'mobile'="075";
'mail'="SGTest888#example.com";
'physicalDeliveryOfficeName'="Office Name";
'company'="Company Name";
'proxyAddresses'="smtp:SGTest888#example.com";
'targetaddress'="smtp:SGTest888#example.com"
}
This works perfectly but now I am required to input about 100 of these contacts from a CSV file.
I get that at the top I would put:
$users = Import-Csv -Path C:\temp\users.csv
Then I would then use this somehow:
foreach ($user in $users) {
My CSV starts the same as my code the columns are shown as below:
First
Surname
Full Name
Job Title
Office Number
Mobile Number
E-mail
Location
Well its a fairly open ended question, but $user in this example is a simple place holder variable, so could be anything, $i or $temp, it represents one interation of the parent, in this case $users.
This will be a good start to see what your given back:
$users = import-csv -Path C:\temp\users.csv
foreach ($user in $users)
{
$user
}
use:
foreach ($user in $users)
{
$user | get-member
}
to see what you have to work with.
Then you can access properties of the $user like so:
foreach ($user in $users)
{
New-ADObject -name SGTContacttest5 -Type Contact -path "OU=SGTestOU,OU=Contacts,DC=DC,DC=Company,DC=local" -OtherAttributes #{
'department' = $user.department
'Surname' = $user.Surname
}
}
You can access the variables like $user.Job within the foreach loop. Notice that you should omit the quotes. Here an example with one property, you should be able to do the rest yourself:
$users = Import-Csv -Path C:\temp\users.csv
foreach ($user in $users)
{
New-ADObject -name SGTContacttest5 -Type Contact -path "OU=SGTestOU,OU=Contacts,DC=DC,DC=Company,DC=local" -OtherAttributes #{
'department'=$user.Job;
# ....
}
}

How can I cast as a user object in PowerShell?

I've got the following code which connects to every computer on the domain and checks the members of the local administrators group:
Foreach ($Computer in Get-ADComputer -Filter *){
$Path = $Computer.Path
$Name = ([ADSI]"$Path").Name
Write-Host $Name
$members = [ADSI]"WinNT://$Name/Administrators"
$members = #($members.psbase.Invoke("Members"))
ForEach($member in $members){
Write-Host $member.GetType().InvokeMember("Name", 'GetProperty', $null, $member, $null)
Write-Host $member.GetType().InvokeMember("AdsPath", 'GetProperty', $null, $member, $null)
}
}
I'm trying to store the value of $member in a $User object of some sort, so I can actually reference the attributes without all the crazy invoker stuff.
E.g., in pseudocode I want:
$user = (User) $member;
Write-Host $user.Name
Write-Host $user.AdsPath
I'm new to PowerShell, however... and I'm not sure if I really understand how to cast to an object type within it.
You want to create a custom object (or PSObject) with the specified members and values:
Foreach ($Computer in Get-ADComputer -Filter *){
$Path=$Computer.Path
$Name=([ADSI]"$Path").Name
write-host $Name
$members =[ADSI]"WinNT://$Name/Administrators"
$members = #($members.psbase.Invoke("Members"))
ForEach($member in $members){
$propsWanted = #('Name' , 'AdsPath') # An array of the properties you want
$properties = #{} # This is an empty hashtable, or associative array, to hold the values
foreach($prop in $propsWanted) {
$properties[$prop] = $member.GetType().InvokeMember($prop, 'GetProperty', $null, $member, $null)
}
$user = New-Object PSObject -Property $properties
$user # This is an object representing the user
}
}
Just to go over some of the changes:
I'm putting all of the property names you want into an array $propsWanted, and then iterating over that to invoke and get each one. This lets you easily work with more properties later, by adding the property names in a single place.
The $properties hashtable will store a key/value pair, where the key is the property name, and the value is the property value.
Once the hashtable is filled, you can pass it to the -Property parameter of New-Object when creating a PSObject.
You should use your new object and have a look at by testing a few things:
Pipe it to the Format- cmdlets to see various views:
$user | Format-Table
$user | Format-List
Check the values of its properties directly:
$user.Name
$user.AdsPath
If you make an array of these objects, you can filter them for example:
$user | Where-Object { $_.Name -like '*Admin' }
Alternatives
You might try using CIM or WMI, which will actually be a little friendlier:
CIM
$group = Get-CimInstance -ClassName Win32_Group -Filter "Name = 'Administrators'"
Get-CimAssociatedInstance -InputObject $group -ResultClassName Win32_UserAccount |
select -ExpandProperty Caption
Get-CimAssociatedInstance -InputObject $group -ResultClassName Win32_Group |
select -ExpandProperty Caption
WMI
$query = "ASSOCIATORS OF {Win32_Group.Domain='$($env:COMPUTERNAME)',Name='Administrators'} WHERE ResultClass = Win32_UserAccount"
Get-WmiObject -Query $query | Select -ExpandProperty Caption
$query = "ASSOCIATORS OF {Win32_Group.Domain='$($env:COMPUTERNAME)',Name='Administrators'} WHERE ResultClass = Win32_Group"
Get-WmiObject -Query $query | Select -ExpandProperty Caption
.NET 3.5 Required for this method:
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $env:COMPUTERNAME
$idtype = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, 'Administrators')
$group.Members |
select #{N='Domain'; E={$_.Context.Name}}, samaccountName
Attribution
All of the above alternatives were taken directly from this "Hey, Scripting Guy!" article, The Admin's First Steps: Local Group Membership. It goes into detail about all of these, including the [ADSI] method. Worth a read.
How to Actually Cast
I just realized I didn't actually answer this question, even though it's not exactly what you needed. Classes/types are specified with square brackets. In fact, when you did this:
[ADSI]"WinNT://$Name/Administrators"
You casted a [String] (the string literal in ") to an [ADSI] object, which worked because [ADSI] knows what to do with it.
Other examples:
[int]"5"
[System.Net.IPAddress]'8.8.8.8'
Since we don't know the type of the "user" object you're seeking (or it's not even really loaded), you can't use this method.