powershell transpose and keep only one set of headers - excel

I have a data set that looks like below
headers data
ip 192.168.1.1
netmask 255.255.255.0
description class C
ip 172.20.1.1
netmask 255.255.0.0
description class B
ip 10.0.0.1
netmask 255.255.255.0
description class A
How can I turn that into my expected output of:
ip netmask description
192.168.1.1 255.255.255.0 class C
172.20.1.1 255.255.0.0 class B
10.0.0.1 255.0.0.0 class A
Currently I am importing this data from CSV like below:
$data = import-csv .\data.txt -headers headers,data
If there is an easy way to do this in excel that would be fantastic as well.
Any assistance would be great. Thank you.

The previous answer didn't use the data specified in the question, this one does. It's also fully commented to explain what it does. Below the code is a rundown of how it works:
$newrow = #{} # newrow contains the values on an output row at any given time
$newtable = [System.Collections.ArrayList]#() # newtable contains the final output
# Loop through each row in the input data, assign oldrow to the current row
foreach($oldrow in $data) {
# If the output row already has an ip, netmask or description, then start a new row (hashtable) and add the current row (hashtable) to output
if ($newrow[$oldrow.headers] -ne $null) {
Write-Host "$($oldrow.headers) Already found. Adding new row to output" -ForegroundColor Green
# Add the current row to the target object
[void]$newtable.Add($(New-Object PSObject -Property $newrow))
# Create a new empty row (hashtable)
$newrow = #{}
}
# For each iteration, keep adding data
Write-Host "Adding new entry for $($oldrow.headers)"
$newrow[$oldrow.headers] = $oldrow.data
}
# The final row is not added to the table within the loop, so it must be added here
Write-Host "Loop finished, adding final row to output" -ForegroundColor Green
[void]$newtable.Add($(New-Object PSObject -Property $newrow))
$newtable | select ip,netmask,description
Assumption
Upon encountering a record with the same header value, a new row is required. Until that point, records should be added to the same row, with the header value providing the new column name.
Process
Set up a hashtable called $newrow.
Set up an ArrayList called $newtable. ArrayLists are more flexible than standard Arrays as they have an Add() method.
Iterate through the data using a foreach loop
Start with an if statement that deals with finding a record with a column that we have already assigned. At this point fire a message using Write-Host, add the old row and set up the new row. On the first iteration of the foreach loop, this always evaluates to false as the hashtable is empty.
In the block outside the if condition, keep adding values to the hashtable.
At the end, add the final row and then use select-object to print the output in the right order.

#Import data
$data = Import-Csv .\data.txt -headers headers,data
$data | FT -AutoSize
#Remove the first line
get-content $data | select -Skip 1 | set-content "$data-temp"
move "$data-temp" $data -Force
#Convert the table
$Duration = Measure-Command {
$b = #()
foreach ($Property in $data.Property | Select -Unique) {
$Props = [ordered]#{ Property = $Property }
foreach ($Server in $data.Server | Select -Unique){
$Value = ($data.where({ $_.Server -eq $Server -and
$_.Property -eq $Property })).Value
$Props += #{ $Server = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
}
Write-Host "Finished transposing " -ForegroundColor Green -NoNewline
Write-Host "$(($data | Get-Member -MemberType Properties).count)/$($data.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " columns/rows into " -ForegroundColor Green -NoNewline
Write-Host "$(($b | Get-Member -MemberType Properties).count)/$($b.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " columns/rows in " -ForegroundColor Green -NoNewline
Write-Host $Duration.Milliseconds -ForegroundColor Yellow -NoNewline
Write-Host " Milliseconds" -ForegroundColor Green
$b | FT -AutoSize
$b | Out-GridView
$b | Export-Csv .\newData.csv -NoTypeInformation
newData.csv should look as you expected, if not, let me know again.

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

List down column headers and get the maximum length of string per column

I'm looking for a translation of my Excel formula in a form of a script in Powershell, vbscript or Excel VBA. I'm trying to get the list of column headers and the max length of string under it.
Normally, what I do is manually open the .txt file in Excel, from there I can get the header names.. next, I create an array formula =MAX(LEN(A1:A100,000)) for example. This will get the max length of string in the column. I'll do the same formula to other columns.
Right now I can't do this since files have increased to 1GB in size and i can't open them anymore, my desktop crashes. It is also maybe because theyre more than 1 million rows which Excel cant handle. My friend suggested Powershell but I have limited knowledge there.. don't know if it can be done in vbscript or Excel VBA.
Thanks in advance for your help.
Below code works for .csv files but does not with .txt delimited files -
$fileName = "C:\Desktop\EFile.csv"
<#
Sample format of c:\temp\data.csv
"id","name","grade","address"
"1","John","Grade-9","test1"
"2","Ben","Grade-9","test12222"
"3","Cathy","Grade-9","test134343"
#>
$colCount = (Import-Csv $fileName | Get-Member | Where-Object {$_.MemberType -eq 'NoteProperty'} | Measure-Object).Count
$csv = Import-Csv $fileName
$csvHeaders = ($csv | Get-Member -MemberType NoteProperty).name
$dict = #{}
foreach($header in $csvHeaders) {
$dict.Add($header,0)
}
foreach($row in $csv)
{
foreach($header in $csvHeaders)
{
if($dict[$header] -le ($row.$header).Length)
{
$dict[$header] =($row.$header).Length
}
}
}
$dict.Keys | % { "key = $_ , Column Length = " + $dict.Item($_) }
This is how I get my data.
$data = #"
"id","name","grade","address"
"1","John","Grade-9","test1"
"2","Ben","Grade-9","test12222"
"3","Cathy","Grade-9","test134343"
"#
$csv = ConvertFrom-Csv -Delimiter ',' $data
But you should get your data like this
$fileName = "C:\Desktop\EFile.csv"
$csv = Import-Csv -Path $fileName
And then
# Extract the header names
$headers = $csv | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
# Capture output in $result variable
$result = foreach($header in $headers) {
# Select all items in $header column, find the longest, and select the item for output
$maximum = $csv | Select-Object -ExpandProperty $header | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum
# Generate new object holding the information.
# This will end up in $results
[pscustomobject]#{
Header = $header
Max = $maximum.Length
String = $maximum
}
}
# Simple output
$result | Format-Table
This is what I get:
Header Max String
------ --- ------
address 10 test134343
grade 7 Grade-9
id 1 3
name 4 John
Alternatively, if you have memory issues dealing with large files, you may have to get a bit more dirty with the .NET framework. This snippet processes one csv line at a time, instead of reading the entire file into memory.
$fileName = "$env:TEMP\test.csv"
$delimiter = ','
# Open a StreamReader
$reader = [System.IO.File]::OpenText($fileName)
# Read the headers and turn it into an array, and trim away any quotes
$headers = $reader.ReadLine() -split $delimiter | % { $_.Trim('"''') }
# Prepare a hashtable for the results
$result = #{}
# So long as there's more data, keep running
while(-not $reader.EndOfStream) {
# Read a single line and process it as csv
$csv = $reader.ReadLine() | ConvertFrom-Csv -Header $headers -Delimiter $delimiter
# Determine if the item in the result hashtable is smaller than the current, using the header as a key
foreach($header in $headers) {
$item = $csv | Select-Object -ExpandProperty $header
if($result[$header].Maximum -lt $item.Length) {
$result[$header] = [pscustomobject]#{
Header = $header
Maximum = $item.Length
String = $item
}
}
}
}
# Clean up our spent resource
$reader.Close()
# Simple output
$result.Values | Format-Table

Powershell, Excel Finding string in cell, Color row, delete others

I am looking for a way to read my excel sheet, then;
Find empty cells and give them a name, then mark them yellow.
Find rows containing a certain set of word and mark it red. If the script
If cannot find the words specified in row it should delete the entire row.
Here's my script so far:
Any help will be much appreciated
Updated with help of #TheMadTechician
#If there is no Out-Clipboard, set it
If(!(Get-Command Out-Clipboard -ErrorAction SilentlyContinue)){Set-Alias Out-Clipboard "$env:SYSTEMROOT\System32\clip.exe"}
#Get current date
$Date = get-date -format yyyy-MM-dd
$Company = "company"
$Company2 = "company2"
#Define all files/Paths.
$Path = "C:\$Company2\BlockedIP"
md "$Path\HTML\$Date" -Force |Out-Null
$path2 = "$Path\HTML\$Date"
$PathWeb = "/HTML/$Date"
#Path = C:/$Company2/BlockedIP
#Path2 = C:/$Company2/BlockedIP/HTML/2014-07-09
#Define File's used or created in this script.
$File = "$Path\IP-$Date.txt"
$FileHtml = "$Path2\IP-$Date.htm"
$FileXML = "$Path\IP-$Date.xlsx"
$FileHTMLWeb = "$PathWeb\IP-$date.htm"
#File = C:/$Company2/BlockedIP/IP-2014-07-09.txt
#FileXML = C:/$Company2/BlockedIP/HTML/2014-07-09/IP-2014-07-09.htm
#FileHtml = C:/$Company2/BlockedIP/HTML/2014-07-09/IP-2014-07-09.xlsx
#FileHTMLWeb = PublicIP/HTML/2014-07-09/IP-2014-07-09.htm
#Define error actions.
#$erroractionpreference = "SilentlyContinue"
#Get content from given IP list.
$colComputers = #(get-content $File | Sort -unique)
$count = $colComputers.Count
write-output "$Count IP's detected."
#Get DNS Results
$Progress=1
$DNSResults = $colComputers | %{
Write-Progress -Activity "Creating a usable 'Blocked IP' list ($Progress/$count)" -PercentComplete ($Progress/$Count*100) -Status "Please stand by"
try {
($dnsresult = [System.Net.DNS]::GetHostEntry($_))|out-null
}
catch {
$dnsresult = "Fail"
}
[PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$dnsresult.HostName
IPAddress=$dnsresult.AddressList[0].ToString()
}
$Progress++
}
$DNSResults | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
#Open Excel.
$a = New-Object -comobject Excel.Application
#Since we want this script to look like it's being used without excel I set it's visibility to false.
$a.visible = $True
#Disable excel confirmations.
$a.DisplayAlerts = $False
<#
# set interactive to false so nothing from excel is shown.
$Excel.DisplayAlerts = $false
$Excel.ScreenUpdating = $false
$Excel.Visible = $false
$Excel.UserControl = $false
$Excel.Interactive = $false
#>
#Create sheets in Excel.
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Activate() | Out-Null
#Create a Title for the first worksheet and adjust the font
$c.Cells.Item(1,1)= "Blocked IP's $Date"
$c.Cells.Item(1,1).Font.ColorIndex = 55
$c.Cells.Item(1,1).Font.Color = 8210719
$range = $c.Range("a1","e1")
$range.Style = 'Title'
$range.Select()
$range.MergeCells = $true
$range.VerticalAlignment = -4108
$CounterRow = $Count+5
#Define subjects.
$c.Name = "Blocked IP's ($Date)"
$c.Cells.Item(2,1) = "Given IP"
$c.Cells.Item(2,2) = "Resolved DNS"
$c.Cells.Item(2,3) = "Returned IP"
$c.Cells.Item(2,5) = "$Company"
$c.Cells.Item($Count+5,1) = "Created by"
$link = "http://www.$Company"
$link2 = "https://www.linkedin.com/profile/view?id=#########"
$r = $c.Range("E2")
[void]$c.Hyperlinks.Add($r, $link)
$r = $c.Range("A$Counterrow")
[void]$c.Hyperlinks.Add($r, $link)
#Define cell formatting from subjects.
$c.Range("A2:E2").Interior.ColorIndex = 6
$c.Range("A2:E2").font.size = 13
$c.Range("A2:E2").Font.ColorIndex = 1
$c.Range("A2:E2").Font.Bold = $True
#Define html code for Excel save to .htm.
$xlExcelHTML = 44
#Define the usedrange, excluding header and footer rows
$e = $c.Range("A3:E$($DNSResults.Count+2)")
#Populate data into spreadsheet
$DNSResults | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
#$c.Cells.Item(3,1).Select()
$c.Paste($e,$false)
$e = $c.Range("A3:C$($DNSResults.Count+2)")
$c.Paste($e,$false)
$Keywords = "Google","thenetworkfactory","HappyTreeFriends"
$Filter = "($(($Keywords|%{[RegEx]::Escape($_)}) -join "|"))"
$DNSResults | Where{$_ -match $filter} | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
ForEach($Cell in $e){
If([String]::IsNullOrWhitespace($Cell.value2)){$Cell.interior.colorindex=6}
}
#Define the usedrange for autofitting.
$d = $c.UsedRange
#Set background color for the IP list.
$E.interior.colorindex = 15
#Define borders here.
$xlOpenXMLWorkbook = 51
$xlAutomatic=-4105
$xlBottom = -4107
$xlCenter = -4108
$xlRight = -4152
$xlContext = -5002
$xlContinuous=1
$xlDiagonalDown=5
$xlDiagonalUp=6
$xlEdgeBottom=9
$xlEdgeLeft=7
$xlEdgeRight=10
$xlEdgeTop=8
$xlInsideHorizontal=12
$xlInsideVertical=11
$xlNone=-4142
$xlThin=2
$selection = $c.range("A3:C$($DNSResults.Count+2)")
$selection.select() |out-null
$selection.HorizontalAlignment = $xlRight
$selection.VerticalAlignment = $xlBottom
$selection.WrapText = $false
$selection.Orientation = 0
$selection.AddIndent = $false
$selection.IndentLevel = 0
$selection.ShrinkToFit = $false
$selection.ReadingOrder = $xlContext
$selection.MergeCells = $false
$selection.Borders.Item($xlInsideHorizontal).Weight = $xlThin
#Make everything fit in it's cell.
$d.EntireColumn.AutoFit() | Out-Null
#Save the file as .xlsx on every placed IP to ensure the file is not lost due to any reason.
$b.SaveAs("$FileXML")
#Clear screen on every checked IP to remove the 'True' statement.
#cls
#
#Save final result as a .htm file
$b.SaveAs("$FileHTML",$xlExcelHTML)
#Close and quit Excel.
$b.Close()
get-process *Excel* | Stop-Process -force
#Move .txt file to the correct HTML folder.
move-item $file $path2 -Force
#Move .xlsx file to the correct HTML folder.
move-item $filexml $path2 -Force
#Declare XLSX file for mail
$MailXML = "$path2\IP-$Date.xlsx"
#Clear screen, again. (Let's keep things tidy.)
#cls
#Variables for public IP
# I am defining website url in a variable
$url = "http://checkip.dyndns.com"
# Creating a new .Net Object names a System.Net.Webclient
$webclient = New-Object System.Net.WebClient
# In this new webdownlader object we are telling $webclient to download the
# url $url
$IpPublic = $webclient.DownloadString($url)
# Just a simple text manuplation to get the ipadress form downloaded URL
# If you want to know what it contain try to see the variable $IpPublic
$IpPublic2 = $IpPublic.ToString()
$ipPublic3 = $IpPublic2.Split(" ")
$ipPublic4 = $ipPublic3[5]
$ipPublic5 = $ipPublic4.replace("</body>","")
$FinalIPAddress = $ipPublic5.replace("</html>","")
$ipLocal = (Get-WmiObject -class win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').ipaddress[0]
#Variables e-mail.
$From = "Blocked IP <r.van.tour#$Company>"
$To = "IT Dept <r.van.tour#$Company>"
$CC = "Someone <$Company2#$Company"
$Subject = "Blocked IPs for $date ($Count Total)"
#The href should point to the htm file in your iis/apache folder.
$WebLink = $FinalIPAddress+$FileHtmlWeb
$here = "<a href='http://$Weblink'><b>Here</b></a>"
#Define the body of your e-mail, in this case it displays a message and shows the server it is send from with it's local IP.
#A link to the .htm file, how many IP's were blocked and the date of the message.
$Body = "<!DOCTYPE html><html><head> <title>Blocked IP's $Date</title></head><header><h1>Blocked IP</h1><p><time pubdate datetime='$date'></time></p></header><br>"
$body += "<body>Dear <font color=black>$to</font>,<br><br>"
$body += "This is an automated message generated by server: <font color=red><b>$env:COMPUTERNAME, $IPLocal.</b></font><br><br>"
$body += "Click <font color=red><b>$here</b></font> to see the Blocked IP report for $date containing $count IP's.<br>"
$body += "Or see the attachment to open it in Excel.<br></body></html>"
#Clear screen, again. (Let's keep things tidy.)
#cls
#Send output as e-mail.
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "###gmail.com"
$Password = "##"
$message = New-Object System.Net.Mail.MailMessage
$message.IsBodyHTML = $true
$message.ReplyTo = $From
$message.Sender = $From
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.from = $From
$message.attachments.add($MailXML)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
#Create a function to relase Com object at end of script.
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
#Release COM Object
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$a) | Out-Null
#Clear screen for the final time. (Let's keep things tidy.)
#cls
#Exit powershell
exit
#TheMadTechnician
There seems to be a problem with this part of the script where non-resolved IP adresses fail to be shown in $DNSResults
$Keywords = "Google","Cloudflare","Cloud","Ping","Easy- Voyage","McAfee","Pingdom","Panopta","Scoot","Uniglobe"
$Filter = "($(($Keywords|%{[RegEx]::Escape($_)}) -join "|"))"
#Define error actions.
#$erroractionpreference = "SilentlyContinue"
#Get content from given IP list.
$colComputers = #(get-content $File | Sort -unique)
$SourceCount = $colComputers.Count
write-output "$SourceCount IP's detected."
#Get DNS Results
$Progress=1
$DNSResults = $colComputers | %{
Write-Progress -Activity "Creating a usable 'Blocked IP' list ($Progress/$sourcecount)" -PercentComplete ($Progress/$sourceCount*100) -Status "Please stand by"
try {
($dnsresult = [System.Net.DNS]::GetHostEntry($_))|out-null
}
catch {
$dnsresult = "Fail"
}
[PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$dnsresult.HostName
IPAddress=$dnsresult.AddressList[0].ToString()
}
$Progress++
}
$count = ($DNSResults|?{$_ -match $filter}).count
Ok, I'm guessing you have borrowed and pieced this together from scripts found all over because the way it is put together seems kind of unplanned. Like things were added and revised, and there wasn't a master plan that put it all together. So, where to start? With the progress bar, since I already addressed that.
The Progress Bar
Move $i=1 above ForEach($strComputer in $colComputers) and add $i++ after $intRow = $intRow + 1 (which could be shortened to $intRow++). But we already knew that. This kind of becomes a moot point, since I've reworked a good bit of your script, but in practice you'll see it with the ForEach loop coming up next.
The ForEach Loop
Next, your ForEach loop. Boy, that's quite a doozy there. You are making it do things over, and over, and over that really just need to be done once after everything is complete. So, what shall we move to after the loop? Let's start with, well, almost everything. Cell formatting? Later. Filling cells? Later. Sorting? Later. Adjusting column width? Later. Saving the file? Later!
So, what does that leave us in the loop? Not much really, all it leaves is the progress bar and checking DNS entries. Why do it this way? Because we can create an array in PowerShell with the data you want, sort the data, select only the fields that you want to use from the array, and then paste all data in at once instead of one record at a time, and do all the formatting afterwards. In fact, what would probably be better for formatting, instead of checking cells for blanks and coloring them red is to just apply Conditional Formatting to them so that if they're blank they show up red, and let Excel do the work for you.
This is going to make the script run a lot faster since you aren't doing the same work several times, and are working with raw data in PowerShell instead of making Excel do it. I ended up reducing your ForEach loop down to just a few lines:
#Get DNS Results
$Progress=1
$DNSResults = $colComputers | %{
Write-Progress -Activity "Creating a usable 'Blocked IP' list ($Progress/$count)" -PercentComplete ($Progress/$Count*100) -Status "Please stand by"
try {
($dnsresult = [System.Net.DNS]::GetHostEntry($_))|out-null
}
catch {
$dnsresult = "Fail"
}
[PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$dnsresult.HostName
IPAddress=$dnsresult.AddressList[0].ToString()
}
$Progress++
}
That will loop through the entries and create an array of custom objects that have 3 properties that are the 3 cells you wanted in your spreadsheet.
Excel Setup
Ok, you obviously have a grasp of things as far as formatting and injecting simple text, so I'm going to glaze over your title setup for now and get to getting the processed data into the spreadsheet.
For arrays of data (like your Computer/HostName/IP array of data that you were putting in) it is easier to paste it into Excel as a tab delimited CSV object. Out-Clipboard isn't a standard PowerShell cmdlet, even if I think it should be. On the other hand Clip.exe comes standard with windows, so we can just set an alias for it (if you have the PowerShell Community Extensions this is already done for you). I put this at the top of the script to get it out of the way. Normally I would put it right after any functions that I had setup in a script if I needed to setup an alias like this. It checks if you have Out-Clipboard, and if you don't it sets up the alias for Clip.exe to Out-Clipboard.
If(!(Get-Command Out-Clipboard -ErrorAction SilentlyContinue)){Set-Alias Out-Clipboard "$env:SYSTEMROOT\System32\clip.exe"}
Now we can pipe things to the clipboard, and that's real handy for what we want to do next. We are going to take our array and convert it to a tab delimited CSV (with no type info), skip the first entry (the header row), sort what's left by HostName, and pipe it to the clipboard.
$DNSResults | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
Now we just have to paste that into Excel. To do that we need a range object to specify as the target, so I'll set that up, and then we use the WorkSheet object's Paste(Range,link) method. Don't worry about the link part, we are going to use the $false Boolean for that because we are not going to link the pasted data to a datasource for dynamic updating. This looks something like:
$e = $c.Range("A3:E$($DNSResults.Count+2)")
$c.Paste($e,$false)
Then we go on to setting the color for your results, and the rest of your formatting.
So... why'd we change it?
So, my favorite uncle always told me growing up "if it ain't broke, don't fix it". Your script did what you wanted, so why did I revamp it if it wasn't broken? The changes aren't huge, it's really about speeding things up and inserting everything at once instead of one at a time, but the biggest thing is that now we can filter in PowerShell before you insert into Excel, and that brings us to your original questions:
Set blank cells to yellow.
Match rows with key words, delete all other rows.
Seek and destroy!
We'll get to the blanks in a second, but deleting the rows that don't have your certain key words is easy now. Don't delete them, just don't insert them to start with! It's easy enough to setup a list of words to filter for, and then only include records with those words when we go to export to the clipboard.
$Keywords = "Google","Facebook","HappyTreeFriends"
$Filter = "($(($Keywords|%{[RegEx]::Escape($_)}) -join "|"))"
The second line creates a string that you can do a RegEx match against. It takes your keywords, escapes any special characters, joins them up with a pipe separating them, and encloses them in parenthesis. After those two lines $Filter = (Google|Facebook|HappyTreeFriends). Then on the line that you want to send data to the clipboard just add a Where clause:
$DNSResults | Where{$_ -match $filter} | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
That way only records that have one of your blacklisted words will be inserted into Excel, and you don't have to worry about going back and deleting rows. You want rows that do have the key words to be highlighted red? Just color them that way (right now you have them set to color index 15, grey, just change that to 3 and they'll be red) to start with since they should be the only things in there.
What's missing?
Blank cells can be hard to spot, especially once you get some formatting going, and have less defined cell borders. Finding empty cells in this case is really easy though. We already have $e, which is a range object that includes all of the data that we just inserted into Excel, even the blank cells. A range object is basically a collection of cells, each having it's own properties like it's location (row,column), formatting, value, etc. What you can do is simple enough:
#If there is no Out-Clipboard, set it
If(!(Get-Command Out-Clipboard -ErrorAction SilentlyContinue)){Set-Alias Out-Clipboard "$env:SYSTEMROOT\System32\clip.exe"}
#Get current date
$Date = get-date -format yyyy-MM-dd
$Company = "company"
$Company2 = "company2"
#Define all files/Paths.
$Path = "C:\$Company2\BlockedIP"
md "$Path\HTML\$Date" -Force |Out-Null
$path2 = "$Path\HTML\$Date"
$PathWeb = "/HTML/$Date"
#Path = C:/$Company2/BlockedIP
#Path2 = C:/$Company2/BlockedIP/HTML/2014-07-09
#Define File's used or created in this script.
$File = "$Path\IP-$Date.txt"
$FileHtml = "$Path2\IP-$Date.htm"
$FileXML = "$Path\IP-$Date.xlsx"
$FileHTMLWeb = "$PathWeb\IP-$date.htm"
#File = C:/$Company2/BlockedIP/IP-2014-07-09.txt
#FileXML = C:/$Company2/BlockedIP/HTML/2014-07-09/IP-2014-07-09.htm
#FileHtml = C:/$Company2/BlockedIP/HTML/2014-07-09/IP-2014-07-09.xlsx
#FileHTMLWeb = PublicIP/HTML/2014-07-09/IP-2014-07-09.htm
$Keywords = "Google","thenetworkfactory"
$Filter = "($(($Keywords|%{[RegEx]::Escape($_)}) -join "|"))"
#Define error actions.
#$erroractionpreference = "SilentlyContinue"
#Get content from given IP list.
$colComputers = #(get-content $File | Sort -unique)
$SourceCount = $colComputers.Count
write-output "$Count IP's detected."
#Get DNS Results
$Progress=1
$DNSResults = $colComputers | %{
Write-Progress -Activity "Creating a usable 'Blocked IP' list ($Progress/$sourcecount)" -PercentComplete ($Progress/$sourceCount*100) -Status "Please stand by"
try {
($dnsresult = [System.Net.DNS]::GetHostEntry($_))|out-null
}
catch {
$dnsresult = "Fail"
}
[PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$dnsresult.HostName
IPAddress=$dnsresult.AddressList[0].ToString()
}
$Progress++
}
$count = ($DNSResults|?{$_ -match $filter}).count
#Open Excel.
$a = New-Object -comobject Excel.Application
#Since we want this script to look like it's being used without excel I set it's visibility to false.
$a.visible = $True
#Disable excel confirmations.
$a.DisplayAlerts = $False
<#
# set interactive to false so nothing from excel is shown.
$Excel.DisplayAlerts = $false
$Excel.ScreenUpdating = $false
$Excel.Visible = $false
$Excel.UserControl = $false
$Excel.Interactive = $false
#>
#Create sheets in Excel.
$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)
$c.Activate() | Out-Null
#Create a Title for the first worksheet and adjust the font
$c.Cells.Item(1,1)= "Blocked IP's $Date"
$c.Cells.Item(1,1).Font.ColorIndex = 55
$c.Cells.Item(1,1).Font.Color = 8210719
$range = $c.Range("a1","e1")
$range.Style = 'Title'
$range.Select()
$range.MergeCells = $true
$range.VerticalAlignment = -4108
$CounterRow = $Count+5
#Define subjects.
$c.Name = "Blocked IP's ($Date)"
$c.Cells.Item(2,1) = "Given IP"
$c.Cells.Item(2,2) = "Resolved DNS"
$c.Cells.Item(2,3) = "Returned IP"
$c.Cells.Item(2,5) = "$Company"
$c.Cells.Item($Count+5,1) = "Created by"
$link = "http://www.$Company"
$link2 = "https://www.linkedin.com/profile/view?id=#########"
$r = $c.Range("E2")
[void]$c.Hyperlinks.Add($r, $link)
$r = $c.Range("A$Counterrow")
[void]$c.Hyperlinks.Add($r, $link)
#Define cell formatting from subjects.
$c.Range("A2:E2").Interior.ColorIndex = 6
$c.Range("A2:E2").font.size = 13
$c.Range("A2:E2").Font.ColorIndex = 1
$c.Range("A2:E2").Font.Bold = $True
#Define html code for Excel save to .htm.
$xlExcelHTML = 44
#Define the usedrange, excluding header and footer rows
$e = $c.Range("A3:E$(2+$Count)")
#Populate data into spreadsheet
$DNSResults | Where{$_ -match $filter} | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
#$c.Cells.Item(3,1).Select()
$c.Paste($e,$false)
ForEach($Cell in $e){
If([String]::IsNullOrWhitespace($Cell.value2)){$Cell.interior.colorindex=6}
}
#Define the usedrange for autofitting.
$d = $c.UsedRange
#Set background color for the IP list.
$E.interior.colorindex = 15
#Define borders here.
$xlOpenXMLWorkbook = 51
$xlAutomatic=-4105
$xlBottom = -4107
$xlCenter = -4108
$xlRight = -4152
$xlContext = -5002
$xlContinuous=1
$xlDiagonalDown=5
$xlDiagonalUp=6
$xlEdgeBottom=9
$xlEdgeLeft=7
$xlEdgeRight=10
$xlEdgeTop=8
$xlInsideHorizontal=12
$xlInsideVertical=11
$xlNone=-4142
$xlThin=2
$selection = $c.range("A3:C$($DNSResults.Count+2)")
$selection.select() |out-null
$selection.HorizontalAlignment = $xlRight
$selection.VerticalAlignment = $xlBottom
$selection.WrapText = $false
$selection.Orientation = 0
$selection.AddIndent = $false
$selection.IndentLevel = 0
$selection.ShrinkToFit = $false
$selection.ReadingOrder = $xlContext
$selection.MergeCells = $false
$selection.Borders.Item($xlInsideHorizontal).Weight = $xlThin
#Make everything fit in it's cell.
$d.EntireColumn.AutoFit() | Out-Null
#Save the file as .xlsx on every placed IP to ensure the file is not lost due to any reason.
$b.SaveAs("$FileXML")
#Clear screen on every checked IP to remove the 'True' statement.
#cls
#
#Save final result as a .htm file
$b.SaveAs("$FileHTML",$xlExcelHTML)
#Close and quit Excel.
$b.Close()
get-process *Excel* | Stop-Process -force
#Move .txt file to the correct HTML folder.
move-item $file $path2 -Force
#Move .xlsx file to the correct HTML folder.
move-item $filexml $path2 -Force
#Declare XLSX file for mail
$MailXML = "$path2\IP-$Date.xlsx"
#Clear screen, again. (Let's keep things tidy.)
#cls
#Variables for public IP
# I am defining website url in a variable
$url = "http://checkip.dyndns.com"
# Creating a new .Net Object names a System.Net.Webclient
$webclient = New-Object System.Net.WebClient
# In this new webdownlader object we are telling $webclient to download the
# url $url
$IpPublic = $webclient.DownloadString($url)
# Just a simple text manuplation to get the ipadress form downloaded URL
# If you want to know what it contain try to see the variable $IpPublic
$IpPublic2 = $IpPublic.ToString()
$ipPublic3 = $IpPublic2.Split(" ")
$ipPublic4 = $ipPublic3[5]
$ipPublic5 = $ipPublic4.replace("</body>","")
$FinalIPAddress = $ipPublic5.replace("</html>","")
$ipLocal = (Get-WmiObject -class win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').ipaddress[0]
#Variables e-mail.
$From = "Blocked IP <r.van.tour#$Company>"
$To = "IT Dept <r.van.tour#$Company>"
$CC = "Someone <$Company2#$Company"
$Subject = "Blocked IPs for $date ($Count Total)"
#The href should point to the htm file in your iis/apache folder.
$WebLink = $FinalIPAddress+$FileHtmlWeb
$here = "<a href='http://$Weblink'><b>Here</b></a>"
#Define the body of your e-mail, in this case it displays a message and shows the server it is send from with it's local IP.
#A link to the .htm file, how many IP's were blocked and the date of the message.
$Body = "<!DOCTYPE html><html><head> <title>Blocked IP's $Date</title></head><header><h1>Blocked IP</h1><p><time pubdate datetime='$date'></time></p></header><br>"
$body += "<body>Dear <font color=black>$to</font>,<br><br>"
$body += "This is an automated message generated by server: <font color=red><b>$env:COMPUTERNAME, $IPLocal.</b></font><br><br>"
$body += "Click <font color=red><b>$here</b></font> to see the Blocked IP report for $date containing $count IP's.<br>"
$body += "Or see the attachment to open it in Excel.<br></body></html>"
#Clear screen, again. (Let's keep things tidy.)
#cls
#Send output as e-mail.
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "###gmail.com"
$Password = "##"
$message = New-Object System.Net.Mail.MailMessage
$message.IsBodyHTML = $true
$message.ReplyTo = $From
$message.Sender = $From
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.from = $From
$message.attachments.add($MailXML)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
#Create a function to relase Com object at end of script.
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
#Release COM Object
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$a) | Out-Null
#Clear screen for the final time. (Let's keep things tidy.)
#cls
#Exit powershell
exit
Edit: I found the issue with your script. You didn't update the line where you are copying to the clipboard, you just added in lines later in the script, after the script already pasted to Excel, so the code you added effectively did nothing. I've made a couple of updates to your code (to handle record counts better, and filter correctly), and updated the above script.
Edit2: Man this post is getting long. Ok, so to include all entries I've modified a few things. First the ForEach loop that looks up the DNS results, I modified the object creation lines to check if they exist before trying to populate so it stops throwing errors on things that don't have one or both HostName and IP Addresses for DNS Lookups.
[PSCustomObject][Ordered]#{
Source=$_.ToUpper()
HostName=$(if(!([string]::IsNullOrEmpty($dnsresult.HostName))){$dnsresult.HostName})
IPAddress=$(if(!([string]::IsNullOrEmpty($dnsresult.AddressList))){$dnsresult.AddressList[0].ToString()})
}
Then I split the results into three catagories: With Hostname matching a keyword, with Hostname not matching a keyword, and no Hostname (also change $count wince we are including everything).
$DNSWithKeyword = $DNSResults | ?{$_.HostName -match $Filter}
$DNSNoKeyword = $DNSResults | ?{!($_.HostName -match $Filter) -and !([string]::IsNullOrEmpty($_.HostName))}
$DNSLookupFailed = $DNSResults | ?{([string]::IsNullOrEmpty($_.HostName))}
#$count = ($DNSResults|?{$_ -match $filter}).count
$count = $SourceCount
Then down a ways where used range is defined I added one for each category, and instead of just one Copy/Paste I do three so there are those matching keywords first, sorted by hostname, then no keyword sorted by hostname, and then those that have no hostname. Then I colored each by section, and went back after and colored blank cells yellow again.
#Define the usedrange, excluding header and footer rows
$KeyRange = $c.Range("A3:E$(2+$DNSWithKeyword.Count)")
$NoKeyRange = $c.Range("A$(2+$DNSWithKeyword.Count+1):E$(2+$DNSWithKeyword.Count+$DNSNoKeyword.Count)")
$NoDNSRange = $c.Range("A$(2+$DNSWithKeyword.Count+$DNSNoKeyword.Count+1):E$(2+$DNSWithKeyword.Count+$DNSNoKeyword.Count+$DNSLookupFailed.Count)")
$e = $c.Range("A3:E$(2+$Count)")
#Populate data into spreadsheet
$DNSWithKeyword | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
$c.Paste($KeyRange,$false)
$DNSNoKeyword | Sort HostName | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
$c.Paste($NoKeyRange,$false)
$DNSLookupFailed | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Out-Clipboard
$c.Paste($NoDNSRange,$false)
#Define the usedrange for autofitting.
$d = $c.UsedRange
#Set background color for the IP list.
$KeyRange.interior.colorindex = 3
$NoKeyRange.interior.colorindex = 15
$NoDNSRange.interior.colorindex = 14
ForEach($Cell in $e){
If([String]::IsNullOrWhitespace($Cell.value2)){$Cell.interior.colorindex=6}
}

PowerShell script to monitor IIS logs for 500 errors every 10 minutes

I'm trying to set up a script to monitor IIS 7.5 logs fro 500 errors. Now I can get it to do that OK but I would like it to check every 30 minutes. Quite naturally I don't want it to warn me about the previous 500 errors it has already reported.
As you can see from the script below I have added a $time variable to take this into account, however I can't seem to find a way to use this variable. Any help would be appreciated.
#Set Time Variable -30
$time = (Get-Date -Format hh:mm:ss (Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select time,csuristem,scstatus
OK With KevinD's help and PowerGUI with a fair bit of trial and error, I got it working as I expected. Here's the finished product.
#Set Time Variable -30
$time = (Get-Date -Format "HH:mm:ss"(Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select #{n="Time"; e={Get-Date -Format "HH:mm:ss"("$($_.time)")}},csuristem,scstatus | ? { $_.time -ge $time }
Thanks again Kev you're a good man. Hope this code helps someone else out there.
Here's
Try changing your last line to:
$IISLog | select #{n="DateTime"; e={Get-Date ("$($_.date) $($_.time)")}},csuristem,scstatus | ? { $_.DateTime -ge $time }
In the select, we're concatenating the date and time fields, and converting them to a date object, then selecting rows where this field is greater than your $time variable.
You'll also need to change your $time variable:
$time = (Get-Date).AddMinutes(-30)
You want a DateTime object here, not a string.

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