How to find a word/phrase in a line using PowerShell? - string

I have been at this for days now but can't seem to figure it out.
I am trying to compare values(PartNumber) from the database to the first line of the text file. If the PartNumber exists(in the first line of the file) display a message "Part Number Matches" if it doesn't send an email.
The issue is that I get messages saying parts do not match when they are clearly the same.
All wrongly mismatched part numbers have a letter in the beginning for ex. D78948, C56234, etc. At first I thought maybe it is because the part number is alphanumeric. But then the parts that are correctly matched are also alphanumeric.
The PartNumber in the terminal is the one from the database.You can clearly see from the picture that the part number from the database and the one in the text file are exactly the same but the check still fails.
Instead of If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches) I have tried
If($eiaContent -like "*$partno*") ,
If($eiaContent | Select-String -Pattern $partno)
But none of them seem to work.
Any help is appriciated!
Code:
$emailFrom = " CNC Data Discrepancy <DoNotReply#outlook.com>"
$emailTo = "ABC#nicholsonmfg.com"
$server = "Sidney.sidney.nii"
$database = "NML_Sidney"
#Query to get MAFN values in the last x hours(ET-Elapsed Time)
$MafnQueryEt = "SELECT [MAFN] As MAFNET FROM [NML_Sidney].[dbo].[MfgAidsDocLibraryTransactions]"
#Invoke Sql
$MafnSqlEt = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $MafnQueryEt
Write-Host $MafnSqlEt.MAFNET
$aidLibPathFolder = "C:\Users\Desktop\CNC_Transfer_Test_Folders\AidLib_Test"
$filterA = "MAZAK INTEGREX E-1550 WFO"
$filterB = "MILLING_MAZAK_INTEGREX_E1550"
#Filter child items to get the .EIA files
Get-ChildItem -Path $aidLibPathFolder -Include *.EIA -Recurse | ForEach-Object{
#Splitting the folder name because the first part of the name is the MAFN in the db(less the zeros). The second part after the dot(.) is the version number
$fullPath = $_.FullName
$baseName = $_.Name
$folderName = $_.Directory.Name
$splitFolderName = $folderName.Split('.')
$firstPart = $splitFolderName[0].TrimStart("0")
$version = "VER"+ " " + $splitFolderName[1]
$ver = $splitFolderName[1].TrimStart("0")
$versionNoZero = "VER"+ " " + $ver
#Filter the EIA files that have the specific line of text.
If(Get-Content -Path $fullPath | Select-String -Pattern $filterA,$filterB){
#Get first line of file.
$eiaContent= Get-Content -Path $fullPath -TotalCount 1
If($MafnSqlEt.MAFNET -eq $firstPart){
$partNoQuery = "SELECT [PartNo] FROM [NML_Sidney].[dbo].[vMADL_EngParts] Where MAFN=$firstPart"
$partNoSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $partNoQuery
$revQuery = "SELECT [MA_Rev] As Rev FROM [NML_Sidney].[dbo].[MADL_EngParts] Where MAFN=$firstPart"
$revSql = Invoke-Sqlcmd -ServerInstance $server -Database $database -Query $revQuery
[System.String]$partNo = $partNoSql.PartNo
$rev = "REV" + " "+ $revSql.Rev
$partNo.GetType()
}
If (Get-Content -Path $fullPath -TotalCount 1 | Select-String -Pattern $partNo -AllMatches){
Write-Host "PART matches" -BackgroundColor Green -ForegroundColor Black
Write-Host `PartNumberMatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Green -ForegroundColor Black
}
Else{
Write-Host "PART does not match" -BackgroundColor Red -ForegroundColor Black
Send-MailMessage -From $emailFrom -To 'hargul.sidhu#nicholsonmfg.com' -Subject "ALERT! DATA DISCREPANCY - '$folderName'" -Body "Incorrect Part Number in '$baseName' Path -'$fullPath'. **DO NOT REPLY. THIS INBOX IS NOT MONITORED**" -SmtpServer 'XXXX
Write-Host `PartNumberMismatch` $partNo `Folder` $folderName `FileName` $baseName -BackgroundColor Red -ForegroundColor Black
}

Related

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):

Powershell export CSV looks weird

I have an issue with my CSV export to Excel with powershell. When I import it looks like pretty bad and I can't find any information that helps me to solve it.
Here I attach an image of the import and the code. I see other CSV imports and it looks normal with its categories spaced by rows in Excel, but I don't know how to do it.
Image of my workbook
$Computers = Get-ADComputer -Filter {OperatingSystem -like "*Server*"} -Properties OperatingSystem | Select-Object -ExpandProperty Name
Foreach($computer in $computers){
if(!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "cannot reach $computer offline" -f red}
else {
$outtbl = #()
Try{
$sr=Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
$Xr=Get-WmiObject –class Win32_processor -ComputerName $computer -ErrorAction Stop
$ld=get-adcomputer $computer -properties Name,Lastlogondate,operatingsystem,ipv4Address,enabled,description,DistinguishedName -ErrorAction Stop
$r="{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $computer |Measure-Object Capacity -Sum).Sum / 1GB)
$x = gwmi win32_computersystem -ComputerName $computer |select #{Name = "Type";Expression = {if (($_.pcsystemtype -eq '2') )
{'Laptop'} Else {'Desktop Or Other something else'}}},Manufacturer,#{Name = "Model";Expression = {if (($_.model -eq "$null") ) {'Virtual'} Else {$_.model}}},username -ErrorAction Stop
$t= New-Object PSObject -Property #{
serialnumber = $sr.serialnumber
computername = $ld.name
Ipaddress=$ld.ipv4Address
Enabled=$ld.Enabled
Description=$ld.description
Ou=$ld.DistinguishedName.split(',')[1].split('=')[1]
Type = $x.type
Manufacturer=$x.Manufacturer
Model=$x.Model
Ram=$R
ProcessorName=($xr.name | Out-String).Trim()
NumberOfCores=($xr.NumberOfCores | Out-String).Trim()
NumberOfLogicalProcessors=($xr.NumberOfLogicalProcessors | Out-String).Trim()
Addresswidth=($xr.Addresswidth | Out-String).Trim()
Operatingsystem=$ld.operatingsystem
Lastlogondate=$ld.lastlogondate
LoggedinUser=$x.username
}
$outtbl += $t
}
catch [Exception]
{
"Error communicating with $computer, skipping to next"
}
$outtbl | select Computername,enabled,description,ipAddress,Ou,Type,Serialnumber,Manufacturer,Model,Ram,ProcessorName,NumberOfCores,NumberOfLogicalProcessors,Addresswidth,Operatingsystem,loggedinuser,Lastlogondate |export-csv -Append C:\temp\VerynewAdinventory.csv -nti
}
}
As commented, your locale computer uses a different delimiter character that Export-Csv by default uses (that is the comma).
You can check what character your computer (and thus your Excel) uses like this:
[cultureinfo]::CurrentCulture.TextInfo.ListSeparator
To use Export-Csv in a way that you can simply double-click the output csv file to open in Excel, you need to either append switch -UseCulture to it, OR tell it what the delimiter should be if not a comma by appending parameter -Delimiter followed by the character you got from the above code line.
That said, your code does not produce the full table, because the export to the csv file is in the wrong place. As Palle Due commented, you could have seen that if you would indent your code properly.
Also, I would advise to use more self-describing variable names, so not $r or $x, but $memory and $machine for instance.
Nowadays, you should use Get-CimInstance rather than Get-WmiObject
AND adding to an array with += should be avoided as it is both time and memory consuming. (on every addition to an array, which is of fixed size, the entire array has to be rebuilt in memory).
Your code revised:
# set the $ErrorActionPreference to Stop, so you don't have to add -ErrorAction Stop everywhere in the script
# remember the currens value, so you can restore that afterwards.
$oldErrorPref = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# get an array of computers, gathering all properties you need
$computers = Get-ADComputer -Filter "OperatingSystem -like '*Server*'" -Properties OperatingSystem, LastLogonDate, IPv4Address, Description
$result = foreach ($computer in $computers) {
$serverName = $computer.Name
if(!(Test-Connection -ComputerName $serverName -BufferSize 16 -Count 1 -ErrorAction SilentlyContinue -Quiet)) {
Write-Host "cannot reach $serverName offline" -ForegroundColor Red
continue # skip this computer and proceed with the next one
}
try {
# instead of Get-WmiObject, nowadays you should use Get-CimInstance
$bios = Get-WmiObject -Class Win32_bios -ComputerName $serverName
$processor = Get-WmiObject -Class Win32_Processor -ComputerName $serverName
$memory = Get-WmiObject -Class Win32_PhysicalMemory -ComputerName $serverName
$disks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $serverName
$machine = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $serverName |
Select-Object #{Name = "Type"; Expression = {
if ($_.pcsystemtype -eq '2') {'Laptop'} else {'Desktop Or Other something else'}}
},
Manufacturer,
#{Name = "Model"; Expression = {
if (!$_.model) {'Virtual'} else {$_.model}}
},
UserName
# output an object to be collected in variable $result
# put the properties in the order you would like in the output
[PsCustomObject] #{
ComputerName = $serverName
Enabled = $computer.Enabled
Description = $computer.description
IpAddress = $computer.IPv4Address
Ou = $computer.DistinguishedName.split(',')[1].split('=')[1]
Type = $machine.type
SerialNumber = $bios.serialnumber
Manufacturer = $machine.Manufacturer
Model = $machine.Model
Ram = '{0} GB' -f (($memory | Measure-Object Capacity -Sum).Sum / 1GB)
ProcessorName = $processor.Name
NumberOfCores = $processor.NumberOfCores
NumberOfLogicalProcessors = $processor.NumberOfLogicalProcessors
Addresswidth = $processor.Addresswidth
OperatingSystem = $computer.OperatingSystem
# {0:N2} returns the number formatted with two decimals
TotalFreeDiskSpace = '{0:N2} GB' -f (($disks | Measure-Object FreeSpace -Sum).Sum / 1GB)
LoggedInUser = $machine.UserName
Lastlogondate = $computer.LastLogonDate
}
}
catch {
Write-Warning "Error communicating with computer $serverName, skipping to next"
}
}
# restore the ErrorActionPreference to its former value
$ErrorActionPreference = $oldErrorPref
# output the completed array in a CSV file
# (using the delimiter characer your local machine has set as ListSeparator)
$result | Export-Csv -Path 'C:\temp\VerynewAdinventory.csv' -UseCulture -NoTypeInformation

Excel.Application: Microsoft Excel cannot access the file '[<filename>]' There are several possible reasons:

I have a PowerShell script that works, it helps me run multiple queries against multiple servers and save each output in different CSV and then merge them together into an Excel file.
$Servers = get-content -Path "Servers.txt"
$DatabaseName ="master"
#$credential = Get-Credential #Prompt for user credentials
$secpasswd = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("sa", $secpasswd)
$QueriesFolder = "Queries\"
$ResultFolder = "Results\"
ForEach($Server in $Servers)
{
$DateTime = (Get-Date).tostring("yyyy-MM-dd")
ForEach ($filename in get-childitem -path $QueriesFolder -filter "*.sql" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} )
{
$oresults = invoke-sqlcmd -ServerInstance $Server -Database $DatabaseName -Credential $credential -InputFile $filename.fullname
write-host "Executing $filename on $Server"
$BaseNameOnly = Get-Item $filename.fullname | Select-Object -ExpandProperty BaseName
$oresults | export-csv $ResultFolder$BaseNameOnly.csv -NoTypeInformation -Force
}
$All_CSVs = get-childitem -path $ResultFolder -filter "*.csv" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}}
$Count_CSVs = $All_CSVs.Count
Write-Host "Detected the following CSV files: ($Count_CSVs)"
Write-Host " "$All_CSVs.Name"`n"
$ExcelApp = New-Object -ComObject Excel.Application
$ExcelApp.SheetsInNewWorkbook = $All_CSVs.Count
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
if (Test-Path $output)
{
Remove-Item $output
Write-Host Removing: $output because it exists already
}
$xlsx = $ExcelApp.Workbooks.Add()
for($i=1;$i -le $Count_CSVs;$i++)
{
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $All_CSVs[$i-1].Name
$file = (Import-Csv $All_CSVs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$xlsx.SaveAs($output)
Write-Host Creating: $output
$ExcelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsx) | Out-Null;
Write-Host "Closing all worksheet"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelApp) | Out-Null;
Write-Host "Closing Excel"
[System.GC]::Collect();
[System.GC]::WaitForPendingFinalizers()
Remove-Item "$ResultFolder\*" -Include *.csv
Write-Host "Cleaning all *.csv"
Start-Sleep -Seconds 3
}
In order to make this script more portable I want all the paths mentioned into it to be stored into a variable and then concatenated.
But as soon as I change:
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
into:
$output = $ResultFolder + $Server + " $DateTime.xlsx"
things get nasty and I receive the error:
Microsoft Excel cannot access the file 'C:\Users\FrancescoM\Documents\Results\0DC80000'.
There are several possible reasons:
• The file name or path does not exist.
• The file is being used by another program.
• The workbook you are trying to save has the same name as a currently open workbook.
At C:\Users\FrancescoM\Desktop\CSV\QueryLauncher.ps1:50 char:2
+ $xlsx.SaveAs($output)
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
I don't understand, I think I'm concatenating things right.
I also followed this StackOverflow post and restarted my computer after adding "C:\Windows\SysWOW64\config\systemprofile\desktop" but the problem isn't fixed.
How can a variable path mess things up with Excel?
Because you are not defining the full path in the $ResultFolder variable, it will be expanded using the current working directory.
Just look at the path you want it to be:
"C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
and the resulting path using the partial $ResultFolder variable:
C:\Users\FrancescoM\Documents\Results\0DC80000
Since you want the output file in a folder on your desktop, set the $output to
$output = Join-Path $([Environment]::GetFolderPath("Desktop")) "CSV\Results\$Server $DateTime.xlsx"
EDIT
From your last comment I understand that you want the output to be in a subfolder called "Results" that resides inside the folder the script itself is in.
In that case do this:
# get the folder this script is running from
$ScriptFolder = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path $MyInvocation.MyCommand.Path }
# the following paths are relative to the path this script is in
$QueriesFolder = Join-Path -Path $ScriptFolder -ChildPath 'Queries'
$ResultFolder = Join-Path -Path $ScriptFolder -ChildPath 'Results'
# make sure the 'Results' folder exists; create if not
if (!(Test-Path -Path $ResultFolder -PathType Container)) {
New-Item -Path $ResultFolder -ItemType Directory | Out-Null
}
Then, when it becomes time to save the xlsx file, create the full path and filename using:
$output = Join-Path -Path $ResultFolder -ChildPath "$Server $DateTime.xlsx"
$xlsx.SaveAs($output)
P.S. I advice to use the Join-Path cmdlet to combine file paths or to make use of [System.IO.Path]::Combine() instead of joining paths together like you do with this line: $oresults | export-csv $ResultFolder$BaseNameOnly.csv. Using the latter can lead to unforeseen pathnames if ever you forget to postfix the first path part with a backslash.
P.S.2 Excel has its own default output path set in Tools->Options->General->Default File Location and has no idea of the relative path for the script. This is why you should save using a Full path and filename.

Making a test-connection script multi threaded

This script takes input from a test file, writes the results to screen, file, and generates an email under certain conditions. The issue is that it is extremely slow with a large lists of hosts. I have looking into multi threading but I am a PS newbie and have not been able to figure it out. Any help would be appreciated. Here is the script.
# start looping here
Do{
$available = #()
$notavailable = #()
$ComputerName = #()
#Path to text file of hostnames we want to monitor
$ComputerName = Get-Content hosts.txt
Write-Host (Get-Date)
# Read the File with the Hosts every cycle, this way to can add/remove hosts
# from the list without touching the script/scheduled task,
# also hash/comment (#) out any hosts that are going for maintenance or are down.
$ComputerName | Where-Object {!($_ -match "#")} |
#"test1","test2" | Where-Object {!($_ -match "#")} |
ForEach-Object {
if(Test-Connection -ComputerName $_ -Count 1 -ea silentlycontinue)
{
# if the Host is available then write it to the screen
$value = #()
$value = $_
Write-Host "Available host ---> "$_ -ForegroundColor Green
[String[]]$available += $_
if ($value -notmatch "[a-z]")
{
$hosttoip = #()
$iptohost = #()
$parsemac = #()
$getmac = #()
$getmac = #()
$findstr = #()
$iptoip = #()
$getmac = nbtstat -A $_ | findstr "Mac Address = **-**-**-**-**" | Select-String -Pattern "Mac Address" -SimpleMatch
$iptoip = $_
$iptohost = [System.Net.Dns]::gethostbyaddress($_).Hostname
$findstr = findstr "$_" output.csv
Write-Host $_ "Responds with:" -ForegroundColor White
Write-Host "Hostname" -ForegroundColor White
Write-Host "$iptohost" -ForegroundColor Cyan
Write-Host "IP Address:" -ForegroundColor White
Write-Host "$_" -ForegroundColor Cyan
Write-Host "Mac Address:" -ForegroundColor White
Write-Host "$getmac`n" -ForegroundColor Cyan
}
if ($value -match "[a-z]")
{
$iptohost = #()
$hosttoip = #()
$hosttohost =#()
$parsemac = #()
$getmac = #()
$getmac = #()
$findstr = #()
$hosttohost = $_
$getmac = nbtstat -a $_ | findstr "Mac Address = **-**-**-**-**" | Select-String -Pattern "Mac Address" -SimpleMatch
$hosttoip = [System.Net.Dns]::GetHostByName($_).AddressList[0].IpAddressToString
$findstr = findstr "$_" output.csv
Write-Host $_ "Responds with:" -ForegroundColor White
Write-Host "Hostname" -ForegroundColor White
Write-Host "$_" -ForegroundColor Cyan
Write-Host "IP Address:" -ForegroundColor White
Write-Host "$hosttoip" -ForegroundColor Cyan
Write-Host "Mac Address:" -ForegroundColor White
Write-Host "$getmac`n" -ForegroundColor Cyan
}
if ($findstr -eq $Null)
{
$WriteFile = #()
$WriteFile = "$_,$iptohost $hosttohost,$hosttoip $iptoip,$getmac"
$WriteFile | Out-file output.csv -Encoding ascii -Append
}
# if the Host was out and is now backonline, remove it from the OutageHosts list
if ($OutageHosts -ne $Null)
{
if ($OutageHosts.ContainsKey($_))
{
Write-host $_ "was on the outages list and came back online. Sending email notification" -ForegroundColor Yellow
$OutageHosts.Remove($_)
$Now = Get-date
if ($notifyonServerBackOnline)
{
$Body = "$_ is online right now $getmac - If available, the MAC address and hostname/IP will be recorded in ScriptLocation\output.csv"
Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification -Subject "Monitored host $_ is up" -SmtpServer $smtpserver
}
}
}
}
else
{
# If the host is unavailable, give a warning to screen
Write-Host "Unavailable host ------------> "$_ -ForegroundColor Red
if(!(Test-Connection -ComputerName $_ -Count 2 -ea silentlycontinue))
{
# If the host is still unavailable for 4 full pings, write error and send email
Write-Host "Unavailable host ------------> "$_ -ForegroundColor Red
[Array]$notavailable += $_
if ($OutageHosts -ne $Null)
{
if (!$OutageHosts.ContainsKey($_))
{
# First time down add to the list and send email
Write-Host "$_ Is not in the OutageHosts list, first time down"
$OutageHosts.Add($_,(get-date))
$Now = Get-date
if ($notifyonServerDown)
{
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "Host $_ is down" -SmtpServer $smtpserver
}
}
else
{
# If the host is in the list do nothing for 1 hour and then remove from the list.
Write-Host "$_ Is in the OutageHosts list"
if (((Get-Date) - $OutageHosts.Item($_)).TotalMinutes -gt $EmailTimeOut)
{
$OutageHosts.Remove($_)
}
}
}
else
{
# First time down create the list and send email
Write-Host "Adding $_ to OutageHosts."
$OutageHosts = #{$_=(get-date)}
$Now = Get-date
if ($notifyonServerDown)
{
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "Host $_ is down" -SmtpServer $smtpserver
}
}
}
}
}
Write-Host ""
Write-Host "Sleeping $SleepTimeOut seconds"
Start-Sleep -Seconds $SleepTimeOut
if ($OutageHosts.Count -gt $MaxOutageCount)
{
# If there are more than a certain number of host down in an hour abort the script.
$Exit = $True
$body = $OutageHosts | Out-String
if ($notifyonMaxOutageCount)
{
Send-MailMessage -Body "This message was generated by our ping monitoring script`n$body" -to $tonotification -from $fromnotification -Subject "More than $MaxOutageCount Hosts down, monitoring aborted" -SmtpServer $smtpServer
}
}
}
while ($Exit -ne $True)

Active Directory Filter memberof

I am trying to get all of the CN's out of active directory in order to populate groups based on that name into Sharepoint Services. I can list the "memberof" section but I can not seem to split it using split(",")
$Dom = 'LDAP://OU=External,OU=Users,OU=HomeOffice,DC=mydoman,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)"
Write-host $person.properties["memberof"]
}
"Total $i"
Now I get everything I need, but I need some way to filter only the CN's out...
As a general rule, write-host is not the best way to generate output. Ideally, you want to emit objects out of your function and let PowerShell do the formatting for you. This is the more "pipeline friendly" way of doing things. In this case, if you had a function Get-GroupMembers you could pipe it to something like
Get-Person | ft CN
The trick is creating a new object and adding properties to it, or just emitting the DirectoryServices object you are pulling already. To create a new custom object you can do the following:
$obj = new-object psobject
$obj | add-member -membertype noteproperty name $PropName -value $valueToStore
People can use your function and pipe it to format-table, format-list, select-object, group-object, sort-object and a variety of other things. Keith Hill's Effective PowerShell has a great chapter on Output that you might find helpful.
There is also an article by Don Jones on using objects instead of text that is quite good as well.
test1.ps1
#Connet using LDAP
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#Integer for the loop
$i=0
# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#Find the Groups
$adobj= $selector.findall() |`
where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj){
$prop=$person.properties
$i++
#Write-host "$($prop.department) - $($prop.sn), $($prop.givenname)" -foregroundcolor Magenta
$test = $person.properties["memberof"]
ForEach-Object {
$test`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
-replace ",","`r`n"
}
}
test2.ps1
# Lets start with a clean slate :)
Clear
# Lets reference the assembly / GAC that we need for this
#region
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$SPSite = New-Object Microsoft.SharePoint.SPSite("https://myintranetorextranetsite.myfqdn.com")
$OpenWeb = $SpSite.OpenWeb("/")
#endregion
# Add some eye candy :)
# region
# I really wanted some old school thing in here :)
write-host " _ ____ ____ " -foregroundcolor Magenta
write-host " / \ | _ \ / ___| _ _ _ __ ___ " -foregroundcolor Magenta
write-host " / _ \ | | | |____\___ \| | | | '_ \ / __|" -foregroundcolor Magenta
write-host " / ___ \| |_| |_____|__) | |_| | | | | (__ " -foregroundcolor Magenta
write-host "|_/ \_\____/ |____/ \__, |_| |_|\___|" -foregroundcolor Magenta
write-host " |___/ " -foregroundcolor Magenta
Write-Host " Version 2.0" -foregroundcolor Red
Write-Host " Build 2009 09-11 21:30" -foregroundcolor Red
Write-host " Created by Mitchell J. Skurnik" -foregroundcolor Red
#endregion
# Create the stopwatch
#region
[System.Diagnostics.Stopwatch] $sw;
$sw = New-Object System.Diagnostics.StopWatch
$sw.Stop()
$sw.Start()
#endregion
# Function to control Adding groups
function creategroup
{
param ([string] $siteurl = "https://myintranetorextranetsite.myfqdn.com")
$site = New-Object Microsoft.SharePoint.SPSite($siteurl)
$web = $site.RootWeb;
$group = $currentgroup;
$perm = "Read";
$owner = "jdoe";
if ($owner -eq "") { $owner = $web.CurrentUser.LoginName }
$exists = $web.SiteGroups | where { $_.Name -eq $group }
if ($exists -eq $null)
{
# Create group
$web.SiteGroups.Add($group, $web.EnsureUser($owner), $null, "");
# Give permissions to the group
$assign = New-Object Microsoft.SharePoint.SPRoleAssignment($web.SiteGroups[$group]);
$assign.RoleDefinitionBindings.Add($web.RoleDefinitions[$perm])
$web.RoleAssignments.Add($assign)
Write-Host -ForegroundColor green "Creating sharepoint group - " $currentgroup;
}
$site.Dispose();
}
# Function to add users to the specified group
function addUser
{
# Open a connection to the sharepoint site and then select the sub site you want
$themail = $prop.mail
$thedisplay = $prop.displayname
# If there are accounts that dont have some info lets populate it
if ($themail -eq "")
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq "")
{
$thedisplay = "Account, Test"
}
if ($themail -eq $null)
{
$themail = "testaccount#myfqdn.com"
}
if ($thedisplay -eq $null)
{
$thedisplay = "Account, Test"
}
$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#write-host "Added: " $thedisplay -foregroundcolor Red
}
# Function to remove people - be careful using this script :(
# Also not done
function removeUser
{
#$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
#$TheNewGroup.AddUser("NTAMR\" + $prop.samaccountname,$themail,$prop.displayname,"")
#$TheNewGroup.Remove($LoginToDel)
}
# Now onto the real stuff
Write-host "Searching for Groups" -foregroundcolor Green
# Clear out the existing text file so we have a clean slate
$file = New-Item -type file "C:\location\to\my\folder\allGroups.txt" -Force
# Execute the Group Dump Script
C:\location\to\my\folder\test.ps1 | Out-File -filepath "C:\location\to\my\folder\allGroups.txt" -append
# Clean up the list by removing duplicates and sorting everything
$TextFile = $TextFile = "C:\Powershell\allGroups.txt"
$NewTextFile = "C:\Powershell\allGroups - Sorted.txt"
GC $TextFile | Sort | GU > $NewTextFile
# Use LDAP to connect to Active Directory
#region
$Dom = 'LDAP://OU=External Accounts,OU=Users,OU=The Office,DC=mydomain,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
#endregion
# Create a selector and start searching from the Root of AD
#region
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
#endregion
# Integer to compare file length
$c=0
# Get the Group text file's length and write to scree and variable
$fileLength = [System.IO.File]::ReadAllText($NewTextFile).Split("`n").Count
Write-Host "Found " $fileLength "Groups in Active Directory" -foregroundcolor Magenta
# Integer for thumbing through 'memberOf' in active directory
$d = 0
# Integer for the amount of of users found
$f = 0
# Start a while loop where we read through the entire groups text file
while ($c -le $fileLength)
{
# Increment the line number for the next pass through
$c++
# Grab the first line of text from the groups file (Really the 0th line) and then tell the user
$currentGroup = (Get-Content $NewTextFile)[$c]
# Create the group
CreateGroup
#Write-Host "Created Group: " $currentGroup -foregroundcolor Red
#
Write-host $c "/" $fileLength "`t" $currentGroup -foregroundcolor Red
# Query Active directory and force some commands
$adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj)
{
# Variable for the different properties to reduce fatigue
$prop=$person.properties
# The Department
$department = $prop.department
# Sir Name
$sn = $prop.sn
# Given Name
$gn = $prop.givenname
$un = $prop.samaccountname
# Assign the really long memberof to a variable
$memberof = $person.properties["memberof"]
# Length of memberof
$memberofcount = $test.Count
# Loop for each group the member is in
while ($d -le $memberof.Count)
{
$blah = ForEach-Object{`
$memberof[$d]`
-replace "CN=OLDLEGACYGROUP",""`
-replace "CN=",""`
-replace ",OU=Sales",""`
-replace ",OU=Some Groups",""`
-replace ",OU=Groups","" `
-replace ",OU=The Office","" `
-replace ",DC=mydomain","" `
-replace ",DC=com","" `
}
# Incriment the d
$d++
# Is that user in the group?
if ($blah -eq $currentGroup)
{
# Hey look we found somebody in that group :)
Write-host "`t`t`t" $un -foregroundcolor Magenta
addUser
$f++
}
#elseif ($blah -ne $currentGroup)
#{
# removeUser
#}
else
{
# Oh noes...nobody is in that group...that is strange
}
}
# Are we at the end of what the user has
if ($d -ge $memberofs.Count)
{
# Looks like we are :)
$d=0
}
}
# Display amount of users found
#Write-Host "`t`t`t" $f " user(s) found"
$f = 0
}
# Stop Watch
$sw.Stop()
# Write the compact output to the screen
write-host "Updated in Time: ", $sw.Elapsed.ToString()
#This space is saved for future development

Resources