Powershell - Get Last 3 char from AD attribute in PsObject - string

I got a script which export to csv some AD attributes
I want to have the last 3 characters of the 'initials' attribute in a PsObject but i have an error and i spent Hours on this...
Could you help me ?
The Error:
*Method invocation failed because [Microsoft.ActiveDirectory.Management.ADUser] doesn't contain a method named 'substring'.
At C:\scripts\ExtractDWH\Untitled2.ps1:15 char:26
+ "Test" = $_.substring <<<< ($_.initials.length - 3, 3)
+ CategoryInfo : InvalidOperation: (substring:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound*
The Code :
$output = 'c:\scripts\ExtractDWH\consultants_test.csv'
$users = Get-ADUser -Filter * -SearchBase "ou=Rennes,ou=Consultants,ou=Utilisateurs,ou=FedFinance,dc=dfedinterim,dc=fr" - Properties * | ? { ($_.initials -notlike 'IC*') -and ($_.initials -notlike 'IM*') -and ($_.initials -ne $null) }
fileIn | % {
$users | % {
New-Object psobject -Property #{
"ID" = $_.initials
"Last 3 strings ID" = $_.substring($_.initials.length - 3, 3)
"Centre Imputation" = $_.extensionAttribute10
"Date Entrée" = $_.extensionAttribute9
# The line below does not work
"Test" = $_.substring($_.initials.length - 3, 3)
}
}
} | Select-Object ID, 'Centre Imputation', 'Date Entrée', 'Test'
| Export-CSV $output -Delimiter ';' -Encoding "UTF8" -NoTypeInformation `
Thank you !

Change this:
"Last 3 strings ID" = $_.substring($_.initials.length - 3, 3)
To This:
"Last 3 strings ID" = $_.initials.substring($_.initials.length -3, 3)

Avshalom showed you the error. You can also use the following to access the last 3 characters:
"Test" = $_.initials[-3 .. -1] -join ''

Related

updating hash table while reading excel file failing in powershell

Any idea why below code is failing.
# Open an Excel workbook first:
$ExcelObj = New-Object -comobject Excel.Application
$ExcelWorkBook = $ExcelObj.Workbooks.Open("c:\anil\test.xlsx",2,$true)
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Item("Sheet1")
# Get the number of filled in rows in the XLSX worksheet
$rowcount=$ExcelWorkSheet.UsedRange.Rows.Count
$report = #{}
$outFile = "vm_info.csv"
"VM Name,Vcenter,Disk usage" | Out-File -FilePath $outFile
######
$data = $ExcelWorksheet.Range("A1:Z100000").Value2
for( $row = 2 ; $row -lt $data.GetUpperBound(0); $row++) {
$vmname = $data[$row, 1]
if ( $vmname -notin $report.keys){
$report[$vmname] = #{}
$report[$vmname]['vcenter'] = $data[$row, 5]
$report[$vmname]['datasize'] = $data[$row, 12]
}
}
$ExcelWorkBook.close($true)
$report.GetEnumerator()|foreach {
"$($_.name),$($_.value.vcenter),$($_.value.datasize)" | Out-File -FilePath $outFile -Append
}
Message getting
Index operation failed; the array index evaluated to null.
At C:\anil\scripts\get_vm_disk_usage_from_excel.ps1:20 char:26
$report[$vmname] = #{}
~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : NullArrayIndex

How to add colors to Excel output file in Powershell

I have written a script to export specific registry keys and the sub keys inside it with the server ping response, but my scripts works as expected and I can able to export that to Excel as well.
But I need inputs or some help on how to add the colors to the Excel output column based on the value.
As Ex: in my script I will get ping response as true or false, for True I need to add green colour and for False I need to add Red color in my output, please help me to achieve this with my script.
CODE
## Get full list of servers
$Servers = GC -Path ".\Servers.txt"
## Loop through each server
$Result = foreach ($vm in $Servers) {
## Check the Ping reponse for each server
Write-Host "Pinging Server" $vm
$Ping = Test-Connection -Server $vm -Quiet -Verbose
if ($Ping){Write-host "Server" $vm "is Online" -BackgroundColor Green}
else{Write-host "Unable to ping Server" $vm -BackgroundColor Red}
## Check the Network Share path Accessibility
Write-Host "Checking Share Path on" $vm
$SharePath = Test-Path "\\$vm\E$" -Verbose
if ($SharePath){Write-host "Server" $vm "Share Path is Accessible" -BackgroundColor Green}
else{Write-host "Server" $vm "Share path access failed" -BackgroundColor Red}
Invoke-Command -ComputerName $vm {
## Get ChildItems under HKLM TCPIP Parameter Interface
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces' | ForEach-Object {
Get-ItemProperty -Path $_.PSPath | Where-Object { $_.PsObject.Properties.Name -like 'Dhcp*' }
} | Select-Object -Property #{Name = 'ComputerName'; Expression = {$env:COMPUTERNAME+"."+$env:USERDNSDOMAIN}},
#{Name = 'Ping_Response'; Expression = {if($using:Ping) {'Pinging'} else {'Unable to ping'}}},
#{Name = 'Share_Path_Access'; Expression = {if($using:SharePath) {'Accessible'} else {'Not Accessible'}}},
DhcpIPAddress, #{Name = 'DhcpNameServer'; Expression = {$_.DhcpNameServer -split ' ' -join '; '}},
DhcpServer, #{Name = 'DhcpDefaultGateway'; Expression = {$_.DhcpDefaultGateway -join '; '}}
}}
$Result | Select-Object * -Exclude PS*, RunspaceId | Export-Excel -Path "$PSScriptRoot\TCPIP_Interface_Details.xlsx" -AutoSize -BoldTopRow -FreezeTopRow -TitleBold -WorksheetName TCPIP_Interface_Details
You can use the New-ConditionalText cmdlet to highlight cells containing the specified -Text with the color of our choice. The cmdlet can also take RGB colors. I encourage you to read the documentation on it, there are also many examples:
Get-Help New-ConditionalText
Since I don't have access to your $result object I can only give you an example of how you can do it using a simple example:
$result = 0..10 | ForEach-Object {
[pscustomobject]#{
ComputerName = 'Host' + $_
Ping_Response = ('Not Responding', 'Pinging')[($_ % 2)]
}
}
function RGB ($red, $green, $blue ){
return [System.Double]($red + $green * 256 + $blue * 256 * 256)
}
$fontGreen = RGB 0 97 0
$backGreen = RGB 198 239 206
$condProps = #{
Text = 'Pinging'
ConditionalTextColor = $fontGreen
BackgroundColor = $backGreen
}
$conditionalTrue = New-ConditionalText #condProps
$conditionalFalse = New-ConditionalText -Text 'Not Responding'
$props = #{
AutoSize = $true
InputObject = $result
Path = 'test.xlsx' # => Use your absolute Path here!
TableName = 'myTable'
TableStyle = 'Medium11'
WorksheetName = 'myWorkSheetName'
ConditionalText = $conditionalTrue, $conditionalFalse
}
Export-Excel #props
The end result should look something like this (unfortunately Google Sheets doesn't do it justice):

Error append line to excel file with powershell

I am getting error-messages, if i am running this code. I want to read users active directory data such as username, name, mail and append these informations to an xlsx file for license-tracking and ordering.
$user = Read-Host "Username"
$data = Get-ADUser -Identity $user -Properties * | select SamAccountName,AdminDisplayName,EmailAddress
$user = $data.SamAccountName
$name = $data.AdminDisplayName
$mail = $data.EmailAddress
$clo = Read-Host "Alter Computername (Nur bei Übertrag)"
$cln = Read-Host "Neuer Computername"
if ($clo -ne '') {
$out = "Übertrag: $($user), $($name), $($mail), Alter PC: $($clo), neuer PC: $($cln)"
}else {
$kst = Read-Host "Kostenstelle"
$out = "$($user), $($name), $($mail), PC: $($cln), KST: $kst"}
$excel_file_path = '"##PATHTOFILE#CENSORED#\test.xlsx"'
## Instantiate the COM object
$Excel = New-Object -ComObject Excel.Application
$ExcelWorkBook = $Excel.Workbooks.Open($excel_file_path)
$ExcelWorkSheet = $Excel.WorkSheets.item("Tabelle1")
$ExcelWorkSheet.activate()
## Find the first row where the first 7 columns are empty
$row = ($ExcelWorkSheet.UsedRange.Rows | ? { ($_.Value2 | ? {$_ -eq $null}).Count -eq 7 } | select -first 1).Row
$ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text'
$ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text'
$ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text'
$ExcelWorkSheet.Cells.Item($row,4) = "$out"
$ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text'
$ExcelWorkBook.Save()
$ExcelWorkBook.Close()
$Excel.Quit(
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel))
Stop-Process -Name EXCEL -Force
The following instruction does not return any value for $row for me:
$row = ($ExcelWorkSheet.UsedRange.Rows | ? { ($_.Value2 | ? {$_ -eq $null}).Count -eq 7 } | select -first 1).Row
I tried to browse the worksheet until a row where the first 7 columns are empty is found, using a loop. It seems to be working.
$lastrow = $ExcelWorkSheet.UsedRange.Rows.Count
for ($row = 1; $row -le $lastrow; $row ++) {
$test = $true
foreach ($col in 1..7) {$test = $test -and ($ExcelWorkSheet.Cells.Item($row,$col).Value2 -eq $null)}
if ($test) {break}
}

PowerShell - Enumerating through a collection and change the collection

How it is posible to fix this script?
Yes, I´m changing the collection in the foreach loop and this is the reason for this error.
An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
At C:\Users\user\Documents\PowerShell\ChangeAllListsV2.ps1:47 char:20
+ foreach <<<< ($list in $webLists)
+ CategoryInfo : InvalidOperation: (Microsoft.Share...on+SPEnumerator:SPEnumerator) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
#Script change in all lists the required field property "testfield" to false
#Part 0 - Configuration
$urlWebApp = "http://dev.sharepoint.com"
$countFound = 0
$countList = 0
$countFoundAndChange = 0
#Part 1 - PreScript
$snapin = Get-PSSnapin | Where-Object {$_.Name -eq "Microsoft.SharePoint.Powershell"}
if ($snapin -eq $null)
{
Write-Host “Loading SharePoint Powershell”
Add-PSSnapin Microsoft.SharePoint.Powershell
}
#Part 2 - Script
$webApp = Get-SPWebApplication $urlWebApp
#$webApp | fl
$webAppSites = $webApp.sites
foreach($site in $webAppSites)
{
Write-Host "***********************************************************************"
Write-Host "Found site: " $site -foreground blue
$siteAllWebs = $site.AllWebs
foreach($web in $siteAllWebs)
{
Write-Host "Found web: " $web -foreground blue
#$web | fl
$webLists = $web.Lists
foreach($list in $webLists)
{
$countList ++
Write-Host "Found list: " $list -foreground blue
#Change list property
$field = $Null
$field = $list.Fields["testfield"]
if($field){
Write-Host "Field found: " $list -foreground green
#Write-Host "in web: " $web -foreground green
$countFound ++
try{
if($field.Required)
{
#######################################################
$field.Required = $False
$field.Update()
#######################################################
$field = $Null
Write-Host "Done!: Change list: " $list -foreground green
$countFoundAndChange ++
}else{
Write-Host "Already!: Change list: " $list -foreground green
}
}
catch{
$field = $Null
Write-Host "Error!: Change list: " $list -foreground red
Write-Host "in web: " $web -foreground red
$_
}
}
}
}
}
Write-Host "Found lists: " $countList
Write-Host "Found lists with column [testfield]: " $countFound
Write-Host "Change lists with column [testfield]: " $countFoundAndChange
The SPListCollection tends to modify the collection when updating its properties (fields, event receivers, etc.). You can use a for-loop instead:
for ($i = 0; $i -lt $webLists.Count; $i++)
{
$list = $web.Lists[$i];
# ...
}
I know this is a pretty old thread. This is for anybody ending up to this page looking for an answer.
The idea is, like other answers suggest, to copy the collection (using the clone() method) to another and iterate "another" and modify the original variable inside the loop without having to use for in place of foreach:
A collection of type ArrayList:
[System.Collections.ArrayList]$collection1 = "Foo","bar","baz"
$($collection1.Clone()) | foreach {
$collection1.Remove("bar")
}
Output:
PS H:\> $collection1
Foo
baz
A collection of type Hashtable:
[System.Collections.Hashtable]$collection2 = #{
"Forum" = "Stackoverflow"
"Topic" = "PowerShell"
}
$($collection2.Clone())| foreach {
$collection2.Remove("Forum")
}
Output:
PS H:> $collection2
Name Value
---- -----
Topic PowerShell
And, a basic array:
[System.Array]$collection3 = 1, 2, 3, 4
$($collection3.Clone()) | foreach {
$collection3[$collection3.IndexOf($_)] = 10
}
Output:
PS H:\> $collection3
10
10
10
10
As long as your collection is not of fixed size.
You can try copying the collection you're currently iterating on to another collection (an array or a list) and then iterate on that new collection.
Something like this:
$collection = #(1, 2, 3, 4)
$copy = #($collection)
$collection[0] = 10
$collection -join " "
$copy -join " "
The code above gives the following output:
10 2 3 4
1 2 3 4
Note that the $copy variable refers to a different collection.
Check: http://soreddymanjunath.blogspot.in/2014/07/collection-was-modified-enumeration.html
Here is anonther example for same issue
if($web.IsMultilingual -eq $true )
{
foreach($cul in $web.SupportedUICultures)
{
if($cul.LCID -ne $webCul.LCID -and $cul.LCID -ne "1033")
{
$web.RemoveSupportedUICulture($cul)
}
}
$web.Update()
}
for the first time it will go through the loop foreach will remove supported culture for frist time, when it comes to loop for the second iteration then it will throw you the exception “Collection was modified; enumeration operation may not execute”,
Solution to Above problem is to Store to values to modified in a Arraylist and try to modify which will fix the problem, Here i am storing Arraylist called enumcul and inserting values into it and modifying it...
$enumcul=New-Object Collections.ArrayList
$i=0
if($web.IsMultilingual -eq $true )
{
foreach($cul in $web.SupportedUICultures)
{
if($cul.LCID -ne $webCul.LCID -and $cul.LCID -ne "1033")
{
$enumcul.Insert($i, $cul)
$i=$i+1
}
}
foreach( $k in $enumcul)
{
$web.RemoveSupportedUICulture($k)
$web.Update()
}

Powershell filter a List by Name and Date

I need a bit of help... I'm new to powershell and i want to Filter a List (csv). I would love to remove all lines with certain names in it. and cut the list down to the last month. In the script you can see how far i got till now.
param(
[Parameter(ValueFromPipeline=$true,HelpMessage="Enter CSV path(s)")]
[String[]]$Path = $null
)
if($Path -eq $null) {
Add-Type -AssemblyName System.Windows.Forms
$Dialog = New-Object System.Windows.Forms.OpenFileDialog
$Dialog.InitialDirectory = "$InitialDirectory"
$Dialog.Title = "Select CSV File(s)"
$Dialog.Filter = "CSV File(s)|*.csv"
$Dialog.Multiselect=$true
$Result = $Dialog.ShowDialog()
if($Result -eq 'OK') {
Try {
$Path = $Dialog.FileNames
}
Catch {
$Path = $null
Break
}
}
else {
Write-Host -ForegroundColor Yellow "Notice: No file(s) selected."
Break
}
}
$info=Import-Csv "$path" -Delimiter ';'
$info | Get-Member
$info | Format-Table
as you can see i tryed to link the path to a filebrowser.
For the purposes of discussion, I will assume that the full pathname of the CSV is in the variable $InputPath, and that you want to write the result to a CSV file whose full pathname is in the variable $OutputPath. I will also assume that the CSV file contains a column named 'Name', and that the value from the Name column that you want to exclude is in the variable $ExcludedName. Given that, you can simply do
Import-CSV -Path $InputPath | Where-Object {$_.Name -ne $ExcludedName} | Export-CSV -Path $OutputPath -NoTypeInformation
You can do this by my code,but dont forget that first row must contains names of column and delimiter must be ';' and $nameslist is array of names that you need delete:
$info=Import-Csv "D:\testdir\file2.csv" -Delimiter ';'
$nameslist=#('James','John','andrew')
foreach($i in $info){
if($nameslist -contains $i.Name){
$i.Name=""
}
$i|Export-Csv -Path "D:\testdir\file1.csv" -Delimiter ';' -NoTypeInformation -Force -Encoding UTF8 -Append
}
Try this:
$data = Import-Csv "Path" | Select-Object * -ExcludeProperty Names
$data | export-csv "Path" -Notype
This will cut the column names.
Try it first without using a function:
Import-Csv <Filename> | Where-Object {$_.<FieldName> -notlike "*<Value>*"}
Also, you might consider something like this:
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true, HelpMessage = "Enter CSV path(s)")]
[String[]]$Path = $(
Add-Type -AssemblyName System.Windows.Forms
$DialogProperties = #{
Title = 'Select CSV File(s)'
Filter = 'CSV File(s)|*.csv'
Multiselect = $True
}
$Dialog = New-Object System.Windows.Forms.OpenFileDialog -Property $DialogProperties
$Dialog.ShowDialog()
If ($Result -eq 'OK') {
$Path = $Dialog.FileNames
} Else {
Write-Error 'Notice: No file(s) selected.'
}
)
)
Process {
ForEach ($PathItem in $Path) {
Import-Csv $PathItem | Where-Object { $_.Name -notlike "*NotThisOne*" }
}
}

Resources