for hours now I'm searching the web but didn't find a suitable solution yet.
I want to add groups as member to other groups and have a CSV as source.
The CSV looks like (first line is the header)...
agroup,bgroup1,bgroup2,bgroup3,...
a-group1,b-group1,b-group2,b-group3
a-group2,b-group5
a-group3,b-group2,b-group3,b-group4,b-group5,b-group6
a-group4,b-group15,b-group23
(...and so forth... Where a-group should represent company roles and b-group corresponding permissions to ressources but that is not the point of this question)
So, following this example a-group1 has b-group1, b-group2 and b-group3 as members.
Now my problem is that I don't know how to iterate through columns 2 to end and ignore empty columns/values.
Other questions here on stackoverflow are about CSV with neatly user,group but I can't do this because the CSV covers hundrets of rows and columns.
Someone please help.
Edit
Sorry, forgot my code so far.
Import-Module ActiveDirectory
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
$newpath = $path + "\input.csv"
$csv = #()
$csv = Import-Csv -Path $newpath -Delimiter ";"
$csv | Foreach-Object {
foreach ($property in $_.PSObject.Properties) {
Write-Host $_.agroup $property.Value
}
}
I did a write-host for testing.
Final Edit
Working code:
$csv | Foreach-Object {
foreach ($property in $_.PSObject.Properties) {
if ($property.Value -ne $null -and $property.Value -ne "") {
Add-ADGroupMember -Identity $_.agroup -Member $property.Value -WhatIf }
}
}
With this code I filtered out the empty values. You might want to remove whatif to have it working.
$csv | Foreach-Object {
foreach ($property in $_.PSObject.Properties) {
if ($property.Value -ne $null -and $property.Value -ne "") {
Add-ADGroupMember -Identity $_.agroup -Member $property.Value -WhatIf }
}
}
Related
I have a CSV file with Name and Status columns. We need to read the Name from the CSV file and perform a set of action on that, once we finish the action on one Name we need to update the status of that Name as 'Completed' on the same .csv file.
Name Status
poc_dev_admin
poc_dev_qa
poc_dev_qa1
poc_dev_qa2
poc_dev_qa3
poc_dev_qa7
poc_dev_qa8
poc_dev_ro
poc_dev_support
poc_dev_test1
poc_dev_test14
poc_dev_test15
poc_dev_test16
After execution of poc_dev_admin the CSV file should be like the following:
Name Status
poc_dev_admin Completed
poc_dev_qa
poc_dev_qa1
poc_dev_qa2
poc_dev_qa3
poc_dev_qa7
poc_dev_qa8
poc_dev_ro
poc_dev_support
poc_dev_test1
poc_dev_test14
poc_dev_test15
poc_dev_test16
I have tried with this logic and it updates Status column for all groups rather than updating it after processing the respective group. May I what change should we make to work this as expected?
$P = Import-Csv -Path c:\test\abintm.csv
foreach ($groupname in $P) {
### Process $groupname####
$newprocessstatus = 'Completed'
$P | Select-Object *, #{n='Status';e={$newprocessstatus}} |
Export-Csv -NoTypeInformation -Path C:\test\abintm.csv
}
Something like this should work for what you described:
$file = 'c:\test\abintm.csv'
$csv = Import-Csv $file
foreach ($row in $csv) {
$groupname = $row.Name
# ...
# process $groupname
# ...
$row.Status = 'Completed'
}
$csv | Export-Csv $file -NoType
If you need to update the CSV before processing the next record you can put the CSV export at the end of the loop (right after setting $row.Status to "Completed").
Modified your code for the use case you've specified,
Changed current item variable in foreach loop to $row instead of $groupname
However, defined the $groupname variable inside the foreach block
$List = Import-Csv -Path "c:\test\abintm.csv"
foreach ($row in $List) {
$groupName = $row.Name
$status = $row.Status
if ($groupName -eq "poc_dev_admin") {
$status = "Completed"
($List | ? { $_.Name -eq $groupName }).Status = $status
}
}
if (($List | ? { $_.Status -eq "Completed" }) -ne $null) {
# $List has been modifed and hence write back the updated $List to the csv file
$List | Export-Csv -Notypeinformation -Path "C:\test\abintm.csv"
}
# else nothing is written
this script works for finding a particular user who has any explicit permissions on a folder within a folder structure, which is good! However, is there an easier way to list the folder path and $_.Access.IdentityReference.Value without having to loop my way in? Or, is this actually okay?
$foldStruct = get-childitem "C:\temp" -recurse -Attributes D | get-acl
ForEach ($fold in $foldStruct) {
ForEach ($perm in $fold.Access.IdentityReference) {
ForEach ($user in $perm.Value) {
If ($user -like "Dom\A*" -or $user -like "Dom\B*") {
Write-Host $user
Write-Host $fold.Path
}
}
}
}
It's debatable if this IS easier.
A more PowerShell way is to have objects as output.
instead of two -like I'd use the RegEx based -match with an alternation
the nested ForEach could be replaced with a Where-Object and a Select-Object
the Path would include Microsoft.PowerShell.Core\FileSystem:: I'd remove that with a -split '::'.
## Q:\Test\2019\02\07\SO_54569198.ps1
$Base = "C:\temp"
# to use a -match instead of -like anchor at begin and escape the \ => \\
$Users = "^Dom\\A|^Dom\\B"
$folderACLs = Get-ChildItem $Base -Recurse -Directory | Get-Acl |
Where-Object {$_.Access.IdentityReference.Value -match $Users } |
Select-Object #{n='User';e={($_.Access.IdentityReference.Value|?{$_ -match $Users})}},
#{n='Path';e={($_.Path -split '::')[1] }}
The output could/would include multiple users in the column, so to seperate them:
ForEach($folderACL in $folderACLs){
ForEach($User in ($folderACL.User){
[PSCustomObject]#{
User = $User
Path = ($_.folderACL.Path -split '::')[1]
}
}
}
I was thinking somewhere around the same thing as LotPings, using a regex -match to filter the users instead of doing -like twice.
I came up with this:
$users = '^Dom\\[AB].*' # regex to find usernames beginning with 'A' or 'B'
$subfolders = Get-ChildItem -Path "C:\Temp" -Recurse -Directory
foreach ($folder in $subfolders) {
$folder | Get-Acl | ForEach-Object { $_.Access } |
Where-Object {$_.IdentityReference.Value -match $users} | ForEach-Object {
[PSCustomObject]#{
'Folder' = $folder.FullName
'User' = $_.IdentityReference
# add extra info about access type for this user if you like
# 'AccessControlType' = $_.AccessControlType
# 'IsInherited' = $_.IsInherited
# 'InheritanceFlags' = $_.InheritanceFlags
# 'PropagationFlags' = $_.PropagationFlags
}
}
}
$csv = Import-Csv "C:\csv.csv"
Foreach ($line in $csv) {
Get-CMBoundary -BoundaryName $line.Name |
Where-Object { $_.Value -eq $line.Subnet } |
Remove-CMBoundary -Verbose
}
The above code works perfectly to clear subnets from an sccm site. What i want to do though is, only remove 1 Site or Line in the CSV at a time - using a variable already entered.
So something like:
$csv = Import-Csv "C:\csv.csv"
$site = "London"
Foreach ($line in $csv) {
Get-CMBoundary -BoundaryName $line.$site |
Where-Object { $_.Value -eq $line.Subnet } |
Remove-CMBoundary -Verbose
}
I have two csv files, each that contain a PATH column. For example:
CSV1.csv
PATH,Data,NF
\\server1\folderA,1,1
\\server1\folderB,1,1
\\server2\folderA,1,1
\\server2\folderB,1,1
CSV2.csv
PATH,User,Access,Size
\\server1\folderA\file1,don,1
\\server1\folderA\file2,don,1
\\server1\folderA\file3,sue,1
\\server2\folderB\file1,don,1
What I'm attempting to do is create a script that will result in separate csv exports based on the paths in CSV1 such that the new files contain file values from CSV2 that match. For example, from the above, I'd end up with 2 results:
result1.csv
\\server1\folderA\file1,don,1
\\server1\folderA\file2,don,1
\\server1\folderA\file3,sue,1
result2.csv
\\server2\folderB\file1,don,1
Previously I've used a script lime this when the two values are exact:
$reportfile = import-csv $apireportoutputfile -delimiter ';' -encoding unicode
$masterlist = import-csv $pathlistfile
foreach ($record in $masterlist)
{
$path=$record.Path
$filename = $path -replace '\\','_'
$filename = '.\Working\sharefiles\' + $filename + '.csv'
$reportfile | where-object {$_.path -eq $path} | select FilePath,UserName,LastAccessDate,LogicalSize | export-csv -path $filename
write-host " Creating files list for $path" -foregroundcolor red -backgroundcolor white
}
however since the two path values are not the same, it returns nothing. I found a -like operator but am not sure how to use it in this code to get the results I want. where-object is a filter while -like ends up returning a true/false. Am I on the right track? Any ideas for a solution?
Something like this, maybe?
$ht = #{}
Import-Csv csv1.csv |
foreach { $ht[$_.path] = New-Object collections.arraylist }
Import-Csv csv2.csv |
foreach {
$path = $_.path | Split-Path -Parent
$ht[$path].Add($_) > $null
}
$i=1
$ht.Values |
foreach { if ($_.count)
{
$_ | Export-Csv "result$i.csv" -NoTypeInformation
$i++
}
}
My suggestion:
$1=ipcsv .\csv1.CSV
$2=ipcsv .\csv2.CSV
$equal = diff ($2|select #{n='PATH';e={Split-Path $_.PATH}}) $1 -Property PATH -IncludeEqual -ExcludeDifferent -PassThru
0..(-1 + $equal.Count) | %{%{$i = $_}{
$2 | ?{ (Split-Path $_.PATH) -eq $equal[$i].PATH } | epcsv ".\Result$i.CSV"
}}
I'm new to Powershell, I'm creating a code to delete a file/s if more than "x" days.
I'm almost done. Need your help in representing my date (table) and should not produce a log file if no files will be delete.
Here's my code:
$max_days = "-30"
$curr_date = Get-Date
$del_date = $curr_date.AddDays($max_days)
$Path = "C:\Desktop\Code"
$DateTime = Get-Date -Format "D=yyyy-MM-dd_T=HH-mm-ss"
$itemsearch = Get-ChildItem C:\Test -Recurse | Where-Object { $_.LastWriteTime -lt $del_date}
Foreach ($item in $itemsearch)
{
Write "File:", $item.Name "Modified:", $item.LastWriteTime "Path:", $item.FullName "Date Deleted:" $del_date | Out-File "C:\Desktop\Code\Deleted\SFTP_DeleteFiles_WORKSPACE_$DateTime.txt" -append
$item | Remove-Item
}
Can anyone please help me? It's already working by the way.
Just need to present the data in table form and don't create a log file if there's nothing to delete.
Update:
Already solved the condition statement by doing:
if($itemsearch)
{
Foreach ($item in $itemsearch)
{
Write "File:", $item.Name "Modified:", $item.LastWriteTime "Path:", $item.FullName "Date Deleted:" $del_date | Out-File "C:\Desktop\Code\Deleted\SFTP_DeleteFiles_WORKSPACE_$DateTime.txt" -append
$item | Remove-Item
}
}
else
{
Write "No files will be deleted."
}
Thanks!
What I want to display it in Excel/Text file is like this one:
http://i59.tinypic.com/30wv33d.jpg
Anyone?
It returns me with this one:
IsReadOnly;"IsFixedSize";"IsSynchronized";"Keys";"Values";"SyncRoot";"Count"
False;"False";"False";"System.Collections.Hashtable+KeyCollection";"System.Collections.Hashtable+ValueCollection";"System.Object";"4"
False;"False";"False";"System.Collections.Hashtable+KeyCollection";"System.Collections.Hashtable+ValueCollection";"System.Object";"4"
False;"False";"False";"System.Collections.Hashtable+KeyCollection";"System.Collections.Hashtable+ValueCollection";"System.Object";"4"
False;"False";"False";"System.Collections.Hashtable+KeyCollection";"System.Collections.Hashtable+ValueCollection";"System.Object";"4"
In Excel. Do you have any idea? I have to search it though.
To introduce tabular logging I would use a CSV file as output by replacing your foreach block by this code:
$results = #()
foreach ($item in $itemsearch)
{
$success = $true
try
{
$item | Remove-Item
}
catch
{
$success = $false
}
if( $success -eq $true )
{
Write-Host $item.FullName 'successfully deleted.'
$results += [PSCustomObject]#{'File'=$item.Name;'Modified'=$item.LastWriteTime;'Path'=$item.FullName;'Date Deleted'=$del_date;'State'='SUCCESS'}
}
else
{
Write-Host 'Error deleting' $item.FullName
$results += [PSCustomObject]#{'File'=$item.Name;'Modified'=$item.LastWriteTime;'Path'=$item.FullName;'Date Deleted'=$del_date;'State'='ERROR'}
}
}
$results | Export-Csv -Path "C:\Desktop\Code\Deleted\SFTP_DeleteFiles_WORKSPACE_$DateTime.csv" -Encoding UTF8 -Delimiter ';' -NoTypeInformation
First an empty array is created ($results).
The try/catch block is here to detect if the deletion succeeded or not, then the appropriate line is added to $results.
At the end the $results array is exported to CSV with ';' separator so you can open it right away with Excel.