Change column title between columns in table of hash values - powershell

I have a table variable that can be treated like a csv file...
"SamAccountName","lastLogonTimestamp","AccountExpires","givenname","sn","distinguishedName","employeeNumber","employeeID","Description","extensionattribute8","userAccountControl"
"value1","value1","value1","value1","value1","value1","value1","value1","value1","value1","value1"
"value2","value2","value2","value2","value2","value2","value2","value2","value2","value2","value2"
"value3","value3","value3","value3","value3","value3","value3","value3","value3","value3","value3"
What I want to do, is change the two title names givenname to FirstName, and sn to LastName.
Note: I also want to change the values for lastLogonTimestamp and AccountExpires, but I already have the working code that does this. This code is as follows...
$listOfBadDateValues = '9223372036854775807', '9223372036854770000', '0'
$maxDateValue = '12/31/1600 5:00 PM'
$tableFixed = $table | % {
if ($_.lastLogonTimestamp) {
$_.lastLogonTimestamp = ([datetime]::FromFileTime($_.lastLogonTimestamp)).ToString('g')
}; if (($_.AccountExpires) -and ($listOfBadDateValues -contains $_.AccountExpires)) {
$_.AccountExpires = $null
} else {
if (([datetime]::FromFileTime($_.AccountExpires)).ToString('g') -eq $maxDateValue) {
$_.AccountExpires = $null
} Else {
$_.AccountExpires = ([datetime]::FromFileTime($_.AccountExpires)).ToString('g')
}
};$_}
How can I write the code so the two title names are changed to FirstName and LastName?

You can simply take the object and pipe it to a select statement to "alias" the property names.
$yourObject | Select-Object -Property #{N='MyNewName1';E={$_.ExistingName1}}, #{N='MyNewName2';E={$_.ExistingName2}}

Related

Add a calculated field to an imported csv based on datetime

Could someone please assist with this one.
My current code imports a csv file which has three columns, so far it will update the column names to be more readable. I need to add a fourth column which is a calculated field based on a datetime field.
So need to check the datetime field then display a number of days before it is 90 days old.
e.g. "Today's date" - "03/03/2020 8:00:00 AM" = 31 days
90 days - 31 days = 59 days (the 59 days is to go into the calculated field column
Bit of a newb with powershell and have all other functions working, but this is what I'm left with and need to add it into the below call, when the csv is imported, header columns updated then exported to a new file.
$input = "C:\Data\test\unchanged101.csv"
$output = "C:\Data\test\unchanged101conv.csv"
$checkDate = (Get-Date).AddDays(-90)
$data = Import-Csv $input |
Where-Object {
($_."pwdlastset" -as [DateTime]) -lt $CheckDate
}
$headerConversion = #(
#{ Name = 'User account'; Expression = { $_.'cn' } }
#{ Name = 'Last modified date'; Expression = { $_.'pwdlastset' } }
#{ Name = 'Email address'; Expression = { $_.'mail' } }
)
(Import-Csv -Path $input) |
Select-Object -Property $headerConversion | Select-Object *,"Days Left" |
Export-Csv -Path $output -NoTypeInformation
The new column is the "Days Left" where I need to display the number of days left until it is 90 days old. How to I get the result from the code here, into that column for each row?
$checkDate = (Get-Date).AddDays(-90)
$data = Import-Csv $input |
Where-Object {
($_."pwdlastset" -as [DateTime]) -lt $CheckDate
}
Been working on this one for the past few days and just cant figure the last part out.
First of all, you should not use variable name $input as this is an Automatic variable
If you subtract one datetime object from another, the result is a TimeSpan object which has a property called Days you could use
$inputFile = "C:\Data\test\unchanged101.csv"
$outputFile = "C:\Data\test\unchanged101conv.csv"
$checkDate = (Get-Date).AddDays(-90).Date # .Date sets this to midnight
$result = Import-Csv $inputFile |
Where-Object { [DateTime]$_.pwdlastset -lt $CheckDate } |
Select-Object #{ Name = 'User account'; Expression = { $_.cn } },
#{ Name = 'Last modified date'; Expression = { $_.pwdlastset } },
#{ Name = 'Email address'; Expression = { $_.mail } },
#{ Name = 'Days Left'; Expression = { ($checkDate - [DateTime]$_.pwdlastset).Days } }
# output on screen
$result | Format-Table -AutoSize
# output to csv
$result | Export-Csv -Path $outputFile -NoTypeInformation
I would first add the new column and then go trough all the lines (with a foreach loop) check the remaining days and write them to the new "Days Left" column.
Note: I omitted your header conversion here. You need to run it first and then the "Days Left" code...
$data = Import-Csv $input #import everything
foreach($line in $data){
$daysLeft = 0
$daysSinceLastSet = ((Get-Date) - [DateTime]$line.pwdlastset).Days
if ($daysSinceLastSet -lt 90){
$daysLeft = 90-$daysSinceLastSet
}
$line."Days Left" = $daysLeft
}
$data | Export-Csv -Path $output -NoTypeInformation

How to use 2 conditions in one foreach loop

I have a script snippet. This gives me an array with 2 propertys: Account and AccessRights. Now I want to build a foreach loop, but I also need to store the second value in a variable for further use.
So if I do:
foreach ($id in $ACLFile.Account) {
# do stuff
}
I only have the Account property saved in $id. But how can I also get its AccessRights value?
$ACLFile = GetNTFSAccess | select Account, AccessRights
$ACLGroup = $ACLFile | Group-Object Account
$Singles = $ACLGroup.Where({$_.Count -eq 1}).Group
$Duplicates = $ACLGroup.Where({$_.Count -gt 1})
$ItemizedDuplicates = $Duplicates | foreach {
[PSCustomObject][ordered]#{
"Account"=$_.Group.Account[0];
"AccessRights" = $_.Group.AccessRights -join ", "
}
}
#($ItemizedDuplicates, $Singles)
Iterate over the objects instead of just one property.
foreach ($acl in $ACLFile) {
$id = $acl.Account
$access = $acl.AccessRights
# ...
}

Is there a wild card that can be used for any column?

If I already have a variable $test with a set of users.
Each user entry has 10 columns that represent email addresses.
How to return only values with a specific entries from the $test variable.
Example of a user entry:
Alias : User01
EmailAddresses_1 : X500:/o=bla bla bla b
EmailAddresses_2 : x500:/o=bla1 bla1 bla1 bla1
EmailAddresses_3 : smtp:USR1#testdomain1.com
EmailAddresses_4 : smtp:user01#testdomain1.com
EmailAddresses_5 : smtp:user1#testdomain2.com
EmailAddresses_6 : SMTP:user001#testdomain1.com
EmailAddresses_7 : SIP:usr01#testdomain1.com
EmailAddresses_8 : smtp:u1#testdomain2.com
EmailAddresses_9 :
EmailAddresses_10 :
So as you can see, some columns are populated with different values and other are empty.
How can I return only the columns with a specific value assuming I only have the variable to work with.
For example all the user entries with only the values that start with "SIP:*"
A little guiding light is appreciated.
If you are looking for the Alias of all the users that have a property name that starts with EmailAddresses and contains a specific value, this might help you out:
$Test = [PSCustomObject]#{
Alias = 'User01'
EmailAddresses_1 = 'X500:/o=bla bla bla b'
EmailAddresses_2 = 'x500:/o=bla1 bla1 bla1 bla1'
EmailAddresses_3 = 'smtp:USR1#testdomain1.com'
EmailAddresses_4 = 'smtp:user01#testdomain1.com'
EmailAddresses_5 = 'smtp:user1#testdomain2.com'
EmailAddresses_6 = 'SMTP:user001#testdomain1.com'
EmailAddresses_7 = 'SIP:usr01#testdomain1.com'
EmailAddresses_8 = 'smtp:u1#testdomain2.com'
EmailAddresses_9 = $null
EmailAddresses_10 = $null
}
$SearchString = 'SIP:'
$Found = Foreach ($T in $Test) {
$Properties = $Test | Get-Member | Where {($_.MemberType -EQ 'NoteProperty') -and ($_.Name -like 'EmailAddresses*')}
Foreach ($P in $Properties) {
if ($T.($P.Name) -like "$SearchString*") {
$T.Alias
}
}
}
$Found | Select -Unique
After clarification in the comments, this might be more what you're looking for:
$SearchString = 'SIP:'
$Test | Select Alias,
#{Name='EmailAddres1';Expression={if ($_.EmailAddresses_1 -like "$SearchString*"){$_.EmailAddresses_1}}},
#{Name='EmailAddres2';Expression={if ($_.EmailAddresses_2 -like "$SearchString*"){$_.EmailAddresses_2}}},
#{Name='EmailAddres3';Expression={if ($_.EmailAddresses_3 -like "$SearchString*"){$_.EmailAddresses_3}}},
#{Name='EmailAddres4';Expression={if ($_.EmailAddresses_4 -like "$SearchString*"){$_.EmailAddresses_4}}},
#{Name='EmailAddres5';Expression={if ($_.EmailAddresses_5 -like "$SearchString*"){$_.EmailAddresses_5}}},
#{Name='EmailAddres6';Expression={if ($_.EmailAddresses_6 -like "$SearchString*"){$_.EmailAddresses_6}}},
#{Name='EmailAddres7';Expression={if ($_.EmailAddresses_7 -like "$SearchString*"){$_.EmailAddresses_7}}},
#{Name='EmailAddres8';Expression={if ($_.EmailAddresses_8 -like "$SearchString*"){$_.EmailAddresses_8}}},
#{Name='EmailAddres9';Expression={if ($_.EmailAddresses_9 -like "$SearchString*"){$_.EmailAddresses_9}}},
#{Name='EmailAddres10';Expression={if ($_.EmailAddresses_10 -like "$SearchString*"){$_.EmailAddresses_10}}}

PowerShell hash table key issues

Maybe I am doing it wrong, but when I try and reference a hash table member using the key, I get no results, however when I filter a .GetEnumerator() output with the same key, I get a result.
This doesn't work:
$year = "2015"
$msol_year_members_table = #{}
foreach ($member in $(Get-MsolGroupMember -GroupObjectId $(Get-MsolGroup | ?{ $_.DisplayName -eq $("Class of " + $year) }).ObjectId)) {
$msol_year_members_table[$member.ObjectId] = $member
}
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
if ($msol_year_members_table.ContainsKey($($mb.ExternalDirectoryObjectId))) {
$msol_year_members_table[$($mb.ExternalDirectoryObjectId)]
}
}
Doing this works though:
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
if ($result = $msol_year_members_table.GetEnumerator() | ?{ $_.Name -eq $($mb.ExternalDirectoryObjectId) }) {
$result
}
}
Any pointers would be appreciated - assuming it is some stupid mistake.
Are you sure you don't have a type mismatch between the keys and the test values?
When you use the .containskey() method, the argument value must be the same type as the key, but when you use the .getenumerator() method, the -eq test is going to try to coerce the .Name value and the test value to the same type for the operation:
$ht = #{
1 = 'one'
2 = 'two'
3 = 'three'
}
$ht['1']
$ht.GetEnumerator() |? { $_.name -eq '1'}
Name Value
---- -----
1 one
Here is my working code - just in case it helps anyone, I used it to assign address book policies for dir sync'd groups to Office 365 mailboxes:
# Years to be processed
$years = #("2015","2016","2017","2018","2019","2020","2021","2022","2023","2024","2025","2026","2027")
# Loop through each year and retrieve the members for the groups
$msol_year_members_table = #{}
foreach ($year in $years) {
foreach ($member in $(Get-MsolGroupMember -GroupObjectId $(Get-MsolGroup | ?{ $_.DisplayName -eq $("Class of " + $year) }).ObjectId)) {
$key = $member.ObjectId.ToString()
$msol_year_members_table[$key] = $member
}
}
# Loop through the mailboxes and set the mailbox poilicies for matching members
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
$key = $mb.ExternalDirectoryObjectId.ToString()
if ($msol_year_members_table.ContainsKey($key)) {
$alias = $msol_year_members_table[$key].Alias; $alias_split = $alias.Split(".")
$year = $alias_split[$alias_split.Length-1)]
Set-Mailbox -Identity $alias -AddressBookPolicy $("Class of " + $year + " ABP")
}
}

compare two csv using powershell and return matching and non-matching values

I have two csv files, i want to check the users in username.csv matches with userdata.csv copy
to output.csv. If it does not match return the name alone in the output.csv
For Ex: User Data contains 3 columns
UserName,column1,column2
Hari,abc,123
Raj,bca,789
Max,ghi,123
Arul,987,thr
Prasad,bxa,324
username.csv contains usernames
Hari
Rajesh
Output.csv should contain
Hari,abc,123
Rajesh,NA,NA
How to achieve this. Thanks
Sorry for that.
$Path = "C:\PowerShell"
$UserList = Import-Csv -Path "$($path)\UserName.csv"
$UserData = Import-Csv -Path "$($path)\UserData.csv"
foreach ($User in $UserList)
{
ForEach ($Data in $UserData)
{
If($User.Username -eq $Data.UserName)
{
# Process the data
$Data
}
}
}
This returns only matching values. I also need to add the non-matching values in output
file. Thanks.
something like this will work:
$Path = "C:\PowerShell"
$UserList = Import-Csv -Path "$($path)\UserName.csv"
$UserData = Import-Csv -Path "$($path)\UserData.csv"
$UserOutput = #()
ForEach ($name in $UserList)
{
$userMatch = $UserData | where {$_.UserName -eq $name.usernames}
If($userMatch)
{
# Process the data
$UserOutput += New-Object PsObject -Property #{UserName =$name.usernames;column1 =$userMatch.column1;column2 =$userMatch.column2}
}
else
{
$UserOutput += New-Object PsObject -Property #{UserName =$name.usernames;column1 ="NA";column2 ="NA"}
}
}
$UserOutput | ft
It loops through each name in the user list. Line 9 does a search of the userdata CSV for a matching user name if it finds it it adds the user data for that user to the output if no match is found it adds the user name to the output with NA in both columns.
had to change your userList csv:
usernames
Hari
Rajesh
expected output:
UserName column1 column2
-------- ------- -------
Hari abc 123
Rajesh NA NA
I had a similar situation, where I needed a "changed record collection" holding the entire record when the current record was either new or had any changes when compared to the previous record. This was my code:
# get current and previous CSV
$current = Import-Csv -Path $current_file
$previous = Import-Csv -Path $previous_file
# collection with new or changed records
$deltaCollection = New-Object Collections.Generic.List[System.Object]
:forEachCurrent foreach ($row in $current) {
$previousRecord = $previous.Where( { $_.Id -eq $row.Id } )
$hasPreviousRecord = ($null -ne $previousRecord -and $previousRecord.Count -eq 1)
if ($hasPreviousRecord -eq $false) {
$deltaCollection.Add($current)
continue forEachCurrent
}
# check if value of any property is changed when compared to the previous
:forEachCurrentProperty foreach ($property in $current.PSObject.Properties) {
$columnName = $property.Name
$currentValue = if ($null -eq $property.Value) { "" } else { $property.Value }
$previousValue = if ($hasPreviousRecord) { $previousRecord[0]."$columnName" } else { "" }
if ($currentValue -ne $previousValue -or $hasPreviousRecord -eq $false) {
$deltaCollection.Add($currentCenter)
continue forEachCurrentProperty
}
}
}