Using property names from a CSV in foreach - powershell

I am trying to import a CSV containing information on a large amount of users and computers. I am using this CSV to build my Active Directory. I am trying to build the computers and am having an issue with the foreach loops and I believe I may be misunderstanding them. Here is the relevant section of my code:
$adusercsv = Import-CSV .\tst.csv
foreach ($_.computername in $adusercsv)
{
if ($_.city -eq "Vancouver")
{
New-ADComputer -Name $_.computername -Path OU=Computers,OU=Vancouver,DC=VANDC
}
if ($_.city -eq "Calgary")
{
New-ADComputer -Name $_.computername -Path OU=Computers,OU=Calgary,DC=InternalCAL
}
}
This code throws the syntax error:
At line:21 char:12
+ foreach ($_.computername in $adusercsv)
+ ~
Missing 'in' after variable in foreach loop.
At line:21 char:39
+ foreach ($_.computername in $adusercsv)
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingInInForeach
How do I read each line from the array?

First off, the $_ is the default variable representing the current object in the pipeline. You are trying to assign the current computer to the computername property of an undefined variable... try this instead
$OUs = #{
Vancouver = 'OU=Computers,OU=Vancouver,DC=VANDC'
Calgary = 'OU=Computers,OU=Calgary,DC=InternalCal'
}
$aduserscsv = Import-CSV .\tst.csv
foreach ($row in $aduserscsv) {
New-ADComputer -Name $row.Computername -path $OUs.($row.city)
}
This has no error checking in, which would be advisable, but it demonstrates how you use the foreach (){} loop and how you don't need to check the property of the city if your values are in your source data

$_ is used for the current value in the pipeline. In this case, you should not use it.
Try this out:
adusercsv = Import-CSV .\tst.csv
foreach ($computer in $adusercsv)
{
if ($computer.city -eq "Vancouver")
{
New-ADComputer -Name $computer.computername -Path OU=Computers,OU=Vancouver,DC=VANDC
}
if ($computer.city -eq "Calgary")
{
New-ADComputer -Name $computer.computername -Path OU=Computers,OU=Calgary,DC=InternalCAL
}
}

Have a look on this page for an example.
You can try like this :
$adusercsv = Import-CSV .\tst.csv
foreach ($pc in $adusercsv)
{
if ($pc.city -eq "Vancouver")
{
New-ADComputer -Name $pc.computername -Path OU=Computers,OU=Vancouver,DC=VANDC
}
if ($pc.city -eq "Calgary")
{
New-ADComputer -Name $pc.computername -Path OU=Computers,OU=Calgary,DC=InternalCAL
}
}

Related

Script to get vmusage takes way too long and returns empty file

I have a script that used to work perfectly in our old domain. It pulls the last vm usage info on our vmware users. Since we switched to a new domain, I updated the ou line and now the script TAKES FOR EVER (like over an hour) and then winds up erring out at the Export-CSV command. I have the SqlServer module installed and am running with my privileged account that has access to our server and all accounts and persistent disks.
Export-CSV : Cannot process argument because the value of argument "name" is not valid. Change the value of the "name" argument and run the operation again.
At \\.\Get-LastVmUsage2.ps1:88 char:13
+ $CSVArray | Export-CSV $Filename -Append -force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Export-Csv], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.ExportCsvCommand
So tried removing $Filename variable and put in the full path
$CSVArray | Export-CSV -Path c:\temp\lastlogintime.csv -Append -force
Still takes an astronomically long time. Could someone help me figure out why its taking so long and why it leaves the file empty? I'm super new to programming and learning this all on my own. Full script below with identifying info removed
Param (
[string]$Filename,
[int]$InactiveDays
)
try {
Import-Module VMWare.PowerCli
Write-Host "Imported VMware PowerCLI"
}
catch {
Write-Host "Unable to import VMware module, please install it by running 'Install-Module Vmware.PowerCLI -Scope CurrentUser -AllowClobber'" -ForegroundColor Red
Break
}
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
Connect-VIServer -Server icon-vc.iconid
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
if(!$InactiveDays){
$InactiveDays = 45
}
if(!$Filename){
$SaveDialog = New-Object -Typename System.Windows.Forms.SaveFileDialog
$SaveDialog.Filter = "CSV File (*.csv) | *.csv"
$Result = $SaveDialog.ShowDialog()
if($Result.value__ -ne 1){
Exit
}
$Filename = $SaveDialog.Filename
}
$Computers = Get-ADComputer -filter 'Enabled -eq $True' -SearchBase "OU=Virtual,OU=Computers-Internal,DC=Domain,DC=Com"
class CsvRow {
[object] ${ComputerName}
[object] ${LastUser}
[object] ${LastLogon}
[object] ${Inactive}
}
$CSVArray = #()
$Today = Get-Date
foreach($Computer in $Computers){
$Entry = New-Object CsvRow
Try{
$Data = Get-ChildItem "\\$($computer.Name)\d$\Users" -ErrorAction SilentlyContinue | Sort-Object -Property LastWriteTime -Descending -ErrorAction SilentlyContinue | Select-Object -Property Name, LastWriteTime -First 1 -ErrorAction SilentlyContinue
if($Data.LastWriteTime.AddDays($InactiveDays) -lt $Today){
$Entry.Inactive = "True"
} else {
$Entry.Inactive = "False"
}
} catch {
$Data = #{"Name"="";"LastWriteTime"=""}
}
$Entry.ComputerName = $Computer.Name
$Entry.LastUser = $Data.Name
$Entry.LastLogon = $Data.LastWriteTime
$CSVArray += $Entry
}
$CSVArray | Export-CSV -Path c:\temp\lastlogintime.csv -Append -force
A big timeloss is the way you expand your array.
This creates a new empty Array:
$CSVArray = #()
Inside your loop you keep adding to that array:
$CSVArray += $Entry
Because an array has a fixed size by design, powershell copies the entire array to a new array with an index greater by 1 compared to the old array.
And since you are doing this inside a loop, it does this for each loop, and it keeps getting slower each time.
if you have to add to an array, use a linked list:
# declaration
$CSVArray = New-Object System.Collections.ArrayList
# Adding Entry to List
[void]$CSVArray.Add($Entry)
The .Add() method returns the current Count of the List.
To suppress this, is use [void] infront of the command.
This can save valueable time compared to the powershell CmdLet Out-Null,
especially in a big loop.
Here is a piece of code that shows the difference in executiontime:
$ArrayList = New-Object System.Collections.ArrayList
$Array = #()
Measure-Command -Expression { 0..10000 | % { [void]$ArrayList.Add($_) } } |
Select-Object -ExpandProperty TotalMilliseconds |
ForEach-Object { "ArrayList with [void] finished in:`t`t`t$($_) ms" }
Measure-Command -Expression { 0..10000 | % { $ArrayList.Add($_) | Out-Null } } |
Select-Object -ExpandProperty TotalMilliseconds |
ForEach-Object { "ArrayList piped to Out-Null finished in:`t$($_) ms" }
Measure-Command -Expression { 0..10000 | % { $Array += $_ } } |
Select-Object -ExpandProperty TotalMilliseconds |
ForEach-Object { "Array expanding with += finished in:`t`t$($_) ms" }
ArrayList with [void] finished in:               48.0285 ms
ArrayList piped to Out-Null finished in:   274.6679 ms
Array expanding with += finished in:      1766.2551 ms
EDIT
Do not Import all of VmWare.PowerCli.
The import is rather slow, instead execute each PowerCLI command that you use in the script in a new clean shell.
Afterwards run
Get-Module Vmware*
to see witch modules were auto imported and are realy needed.
Then import only those Modules at the start of your script:
Import-Module VmWare.Vim.Core,VmWare.Vim.Sdk,VMWare.WHATEVER...

If not run after get-service in powershell

I'm trying to access the service status of the remote server. i wrote this
$ServerList = get-content -Path "c:\users\cont015\Desktop\ServerList.txt"
ForEach ($ServerName in $ServerList)
{
$Status= Get-Service -ComputerName $ServerName | ?{$_.DisplayName -like "SQL Server (*"} | select Status | format-wide
if($st -eq "Running")
{
$SeverName
$Status
}
else
{
}
}
it is showing
$Status= Get-Service -ComputerName $ServerName | ?{$_.DisplayName -li ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Service], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.GetServiceCommand
in error. i don't know what i am missing. but when i run without if condition if shows proper output.
$ServerList = Get-Content -Path "c:\users\cont015\Desktop\ServerList.txt"
ForEach ($ServerName in $ServerList)
{
$Status= #(
Get-Service -ComputerName $ServerName -DisplayName "SQL Server (*" |
Select-Object -ExpandProperty Status)
if ("Running" -in $Status)
{
[PSCustomObject]#{
Server = $ServerName
Status = $Status
}
}
else
{
}
}
Explanation:
Get-Service docs: -DisplayName
Specifies, as a string array, the display names of services to be retrieved. Wildcards are permitted (used instead of Where-Object as such filtering is always faster).
Array subexpression operator #( ). -
Returns the result of one or more statements as an array. The result is always an array of 0 or more objects (i.e. force Powershell to always return an array when a call returns only one object or even $null)
Used [PSCustomObject]#{} in output instead of a sequence of strings (learn advantages at Everything you wanted to know about PSCustomObject).

Issue while executing get-acl for remote servers

I am having the below code to get the data from remote servers. thanks to #Santiago Squarzon
$serverlist = Get-Content -Path "C:\ServerList.txt"
# Collect results here
$result = Invoke-Command -ComputerName $serverlist -ScriptBlock {
$paths_list = $env:Path -Split [System.IO.Path]::PathSeparator
foreach($sys_Path in $paths_list)
{
$Permissions = (Get-Acl -Path $sys_Path).Access
foreach($acl in $Permissions)
{
if(-not $acl.IdentityReference)
{
continue
}
[pscustomobject]#{
ComputerName = $env:ComputerName
SystemFolderPath = $sys_Path
IdenityReference = $acl.IdentityReference.Value
FileSystemRights = $acl.FileSystemRights
}
}
}
} -HideComputerName
$result | Export-Csv -Path "C:\status_report.csv" -NoTypeInformation
But I am getting below error while executing it
Cannot validate argument on parameter 'Path'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
+ CategoryInfo : InvalidData: (:) [Get-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetAclCommand
+ PSComputerName
Please let me know on this.
Might adding the following check before $Permissions = (Get-Acl -Path $sys_Path).Access would resolve the issue:
if (($sys_Path -eq $null) -or ($sys_Path -eq '') ) {
continue
}

Get local profile list with powershell

I'm on a Windows server 2008 R2 and I need an extract of the local profile list, so I use Powershell to look into the registry and get what I want :
$path = 'Registry::HKey_Local_Machine\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\*'
$items = Get-ItemProperty -path $path
Foreach ($item in $items) {
$objUser = New-Object
System.Security.Principal.SecurityIdentifier($item.PSChildName)
$objName = $objUser.Translate([System.Security.Principal.NTAccount])
$item.PSChildName = $objName.value
}
echo $items | Select-Object -Property PSChildName | Export-Csv
C:\scripts\PSScripts\UserProfile.csv -Encoding UTF8
It worked with another machine using Windows Server 2012 R2 but here I got a lot of errors, but always the same one :
Exception calling "Translate" with "1" argument(s): "Some or all
identity references could not be translated." At
C:\scripts\PSScripts\users_profile.ps1:5 char:34
+ $objName = $objUser.Translate <<<< ([System.Security.Principal.NTAccount])
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
The .csv file is created but with issues, like a profile shown more than one time, like this :
DOMAIN\User1
DOMAIN\User2
DOMAIN\User3
DOMAIN\User3
DOMAIN\User4
DOMAIN\User5
DOMAIN\User5
DOMAIN\User5
DOMAIN\User6
Is there a difference between WS2008 and WS2012 which can cause this problem? Or is it something else?
I'd suggest using WMI to be consistent across platforms plus some error handling:
$path = 'C:\scripts\PSScripts\UserProfile.csv'
Get-CimInstance -ClassName Win32_UserProfile -Filter Special=FALSE -PipelineVariable user |
ForEach-Object -Begin {$ErrorActionPreference = 'Stop'} {
try
{
$id = [System.Security.Principal.SecurityIdentifier]::new($user.SID)
$id.Translate([System.Security.Principal.NTAccount]).Value
}
catch
{
Write-Warning -Message "Failed to translate $($user.SID)! $PSItem"
}
} |
Select-Object -Property #{Label='PSChildName'; Expression={$PSItem}} |
Export-Csv -Path $path -Encoding ascii -NoTypeInformation
PSv2 solution:
Get-WmiObject -Class Win32_UserProfile -Filter Special=FALSE |
ForEach-Object -Begin {$ErrorActionPreference = 'Stop'} {
try
{
$sid = $_.SID
$id = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $sid
$id.Translate([System.Security.Principal.NTAccount]).Value
}
catch
{
Write-Host "Failed to translate $sid! $_" -ForegroundColor Red
}
} |
Select-Object -Property #{Label='PSChildName'; Expression={$_}} |
Export-Csv -Path $path -Encoding ascii -NoTypeInformation

Powershell Script reading file into array

I have a script I'm working on. I want it to read in a column named ComputerName and one named UserName.
My CSV file looks like this:
ComputerName | Username
computer01 | user1
computer02 | user2
The Pipes are representing cells in excel.
Here's my script:
$computerName = #()
$userName = #()
Import-Csv C:\test\script\Computername_username_test.csv -Delimiter "|" |`
ForEach-Object
{
$computerName += $_.ComputerName
$userName += $_.UserName
}
$destination = New-Item -ItemType Directory -Path C:\test\$userName\dictionary_Files\ -force
$fileList = Get-WmiObject -Class CIM_DataFile -Filter "Drive='C:' And Extension='dic'" -Computername $computerName
foreach ($file in $fileList)
{
$drive, $path = $file.Name.Split('\',2)
$drive = $drive -replace ':','$'
$remoteFile = "\\$computerName\$drive\$path"
Write-Verbose "Copy $remoteFile to $destination"
Copy-Item $remoteFile -Destination $destination -Confirm
}
My goal is to search the C drive of the remote computer for all files with the .dic extension and copy them to a location inside a folder that is named the same as their username from the excel sheet.
When I run this I'm getting the following:
PS C:\Test\Script> C:\Test\Script\DicFiles03_importCSV.ps1
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]:
$computerName += $_.ComputerName
$userName += $_.UserName
Get-WmiObject : Cannot validate argument on parameter 'ComputerName'. The argument is null, empty, or an element of the argument
collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
At C:\Test\Script\DicFiles03_importCSV.ps1:13 char:102
+ ... -Filter "Drive='C:' And Extension='dic'" -Computername $computerName
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Thank you for your help.
I'm think its because you have your { after the foreach-object on the next line powershell is a scripting language so its particular about line endings.