PowerShell keeps throwing an exception - exception

$RootPath = "W:\"
$OutFile = "I:\Permissions.csv"
$Header = "Folder Path,IdentityReference,AccessControlType,IsInherited,InheritanceFlags,PropagationFlags"
#Del $OutFile
Add-Content -Value $Header -Path $OutFile
$Folders = dir $RootPath -recurse | where {$_.psiscontainer -eq $true}
foreach ($Folder in $Folders)
{
$ACLs = get-acl $Folder.fullname | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs)
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($ACL.IdentityReference.Value)
#$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$objUser = $objSID.Translate([System.Security.Principal.SecurityIdentifier])
$objUser.Value
$DName = ([adsi]"LDAP://<SID=$($ACL.IdentityReference.value)>").distinguishedName
$s = $DName
$s -replace "(CN=)(.*?),.*",'$2'
$dname.split("=")[1].split(",")[0]
#Show User
Write-Host “`r`nThe user mapped to SID $($objSID) is $($objUser.value)`r`n” -f “Red”
$OutInfo = $Folder.Fullname + "," + $dname.split("=")[1].split(",")[0] + "," + $ACL.AccessControlType + "," + $ACL.IsInherited + "," + $ACL.InheritanceFlags + "," + $ACL.PropagationFlags
Add-Content -Value $OutInfo -Path $OutFile
}
}
I get the following error messages.
You cannot call a method on a null-valued expression.
At I:\PermissionExporter.ps1:31 char:57
+ $OutInfo = $Folder.Fullname + "," + $dname.split <<<< ("=")[1].split(",")[0] + ","
+ $ACL.AccessControlType + "," + $ACL.IsInherited + "," + $ACL.InheritanceFlags + "," + $ACL.P
ropagationFlags
+ CategoryInfo : InvalidOperation: (split:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Does anyone have any idea whats going on here?

$DNName isn't doing what you expect. Add a breakpoint and verify that this:
$dname.split("=")[1].split(",")[0]
is working correctly.

Related

Color-code cells in HTML mail based on criteria [duplicate]

This question already has answers here:
Convertto-Html: Highlight the cells with special values?
(2 answers)
Closed 6 years ago.
I have a simple script which checks disk space on remote servers, it's got a simple output format which is as follows
What I'd like to do, is color code the individual cells based on their values. Specifically, I'd like to have the cell shaded RED if the free space is less than 300 on any of the drives. What's the easiest means to do this based on the results being sent by PowerShell Send-MailMessage in HTML format?
Here's the code i've got that collects the data from the remote servers:
function Set-AlternatingRows {
[CmdletBinding()]
Param(
[Parameter(Mandatory,ValueFromPipeline)]
[string]$Line,
[Parameter(Mandatory)]
[string]$CSSEvenClass,
[Parameter(Mandatory)]
[string]$CSSOddClass
)
Begin {
$ClassName = $CSSEvenClass
}
Process {
if ($Line.Contains("<tr><td>")) {
$Line = $Line.Replace("<tr>","<tr class=""$ClassName"">")
if ($ClassName -eq $CSSEvenClass) {
$ClassName = $CSSOddClass
} else {
$ClassName = $CSSEvenClass
}
}
return $Line
}
}
foreach ($Item in $InputCSV) {
Write-Host "Checking $($Item.Server) now"
$ShortDate = (Get-Date).ToString('MM/dd/yyyy')
$Ping = PingEM $Item.Server
### Check if server is online before proceeding
if ($Ping -eq "Online") {
Write-Host "$($Item.Server) is Online" -ForegroundColor Green
$FreeSpaceAll = Get-WmiObject Win32_LogicalDisk -filter "DriveType=3" -Computer $($Item.Server) |
Select SystemName, DeviceID, VolumeName,
#{Name="Total Size (GB)";E={"{0:N1}" -f($_.Size/1gb)}},
#{Name="Free Space (GB)";E={"{0:N1}" -f($_.Freespace/1gb)}}
$FreeSpaceC = $FreeSpaceAll | Where {$_.DeviceID -eq "C:"} | Select -expand "Free Space (GB)"
$FreeSpaceD = $FreeSpaceAll | Where {$_.DeviceID -eq "D:"} | Select -expand "Free Space (GB)"
$FreeSpaceE = $FreeSpaceAll | Where {$_.DeviceID -eq "E:"} | Select -expand "Free Space (GB)"
} # Ping
$outarray += New-Object PsObject -Property #{
Server = $Item.Server
FreeSpaceAll = $FreeSpaceAll
FreeSpaceC = $FreeSpaceC
FreeSpaceD = $FreeSpaceD
FreeSpaceE = $FreeSpaceE
PingResults = $Ping
} # OutArray New-Object
} # For
$Head = Get-Content "$Dir\CSS.txt"
$Pre = "This email contains info on C/D/E free space on remote servers<br> <br>"
$Pre += "The below report data is available in Excel .CSV format $Link1. After 7 days, the reports will be moved $Link2 <br><br>"
$Output = $OutArray |
Select Server, PingResults, #{E={$_.FreeSpaceC};Label="Free Space C: (GB)"},
#{E={$_.FreeSpaceD};Label="Free Space D: (GB)"},
#{E={$_.FreeSpaceE};Label="Free Space E: (GB)"} |
ConvertTo-Html -Head $Head -PreContent $Pre -As Table | Set-AlternatingRows -CSSOddClass odd -CSSEvenClass even |
Out-String
$Subject = "($ScriptVer) - file server C/D/E free space report"
$TSBody = ""
$TSBody += "<font face ='arial' color='black'>$Output </font><br><br>"
Send-NailMessage $To -Subject $Subject -Body $TSbody -BodyAsHtml -From $From -SmtpServer $Mailer
I have written this so that it generally does what you want. It is already set to go with HTML tables and an free relay server email provider. This script takes one text input file that you can specify at the top. This text file will contain all of the server names that you want to check, on their own line. Example below. If you want to use your own email provider, you can just replace all the smtp info. Otherwise, it DOES REQUIRE admin priviledges to be run successfully. These credentials can be entered at the top of the script where $cred is specified. You will also need to change the $servers variable to point to your list of server names to check against. It currently only selects the C drive, but you can easily edit that as well as add a column for ping results.
Example of C:\temp\servers.txt
hqdc01
hqdc02
hqdc03
hqfile01
hqfile02
hqmail01
hqservices01
hqsql01
hqsql02
The .ps1 script:
$cred = Get-Credential -Credential 'domain\domainadminuser'
$ServerName = Get-Content "C:\temp\servers.txt"
$ConvertToGB = (1024 * 1024 * 1024)
$enter1 = "`r"
$enter2 = "`r`n"
# Smtp deets
$smtpServer = "relay.appriver.com"
$smtpPort = "2525"
$smtpFrom = "from#from.com"
$smtpTo = "myself#myself.com"
$messageSubject = "Daily Server Report"
# Set up an SmtpClient
$smtpClient = New-Object Net.Mail.SmtpClient
$smtpClient.Host = $smtpServer
$smtpClient.Port = $smtpPort
# Create the MailMessage
$mailMessage = New-Object Net.Mail.MailMessage
$mailMessage.From = $smtpFrom
$mailMessage.To.Add($smtpTo)
$mailMessage.Subject = $messageSubject
$mailMessage.IsBodyHtml = $true
# style
$htmlReport += "<style>"
$htmlReport += "BODY{background-color:white;}"
$htmlReport += "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$htmlReport += "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
$htmlReport += "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
$htmlReport += "</style>"
# table
$htmlReport += "<table>"
$htmlReport += "`n"
$htmlReport += "<tr>"
$htmlReport += "<th>ServerName</th>"
$htmlReport += "<th>Total C:</th>"
$htmlReport += "<th>Free C:</th>"
$htmlReport += "<th>% Free C:</th>"
$htmlReport += "<th>Total D:</th>"
$htmlReport += "<th>Free D:</th>"
$htmlReport += "<th>% Free D:</th>"
$htmlReport += "<th>Total E:</th>"
$htmlReport += "<th>Free E:</th>"
$htmlReport += "<th>% Free E:</th>"
$htmlReport += "</tr>"
foreach($Server in $ServerName)
{
#Get info on all C: drives
$diskC = Get-WmiObject -Credential $cred Win32_LogicalDisk -ComputerName $Server -Filter "DeviceID='C:'" | Select-Object Size,FreeSpace
$htmlReport += "<tr>"
$htmlReport += "<td>$($Server)</td>"
try
{
$htmlReport += "<td>$([Math]::Truncate($diskC.Size / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
$htmlReport += "<td>$([Math]::Truncate($diskC.FreeSpace / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
if([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100) -le 10)
{
$htmlReport += "<td><font color=red> $([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100) -gt 10 -and [Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100) -le 20)
{
$htmlReport += "<td><font color=orange> $([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100) -gt 20)
{
$htmlReport += "<td><font color=green> $([Math]::Truncate(($diskC.FreeSpace / $diskC.size) * 100)) % </font></td>"
}
}
catch
{
$htmlReport += "<td>NA</td>"
}
#Get info on all D: drives
$diskD = Get-WmiObject -Credential $cred Win32_LogicalDisk -ComputerName $Server -Filter "DeviceID='D:'" | Select-Object Size,FreeSpace
try
{
$htmlReport += "<td>$([Math]::Truncate($diskD.Size / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
$htmlReport += "<td>$([Math]::Truncate($diskD.FreeSpace / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -le 10)
{
$htmlReport += "<td><font color=red> $([Math]::Truncate(($diskD.FreeSpace / $diskD.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -gt 10 -and [Math]::Truncate(($diskD.FreeSpace / $diskD.size) * 100) -le 20)
{
$htmlReport += "<td><font color=orange> $([Math]::Truncate(($diskD.FreeSpace / $diskD.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -gt 20)
{
$htmlReport += "<td><font color=green> $([Math]::Truncate(($diskD.FreeSpace / $diskD.size) * 100)) % </font></td>"
}
}
catch
{
$htmlReport += "<td>NA</td>"
}
#Get info on all E: drives
$diskE = Get-WmiObject -Credential $cred Win32_LogicalDisk -ComputerName $Server -Filter "DeviceID='E:'" | Select-Object Size,FreeSpace
try
{
$htmlReport += "<td>$([Math]::Truncate($diskE.Size / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
$htmlReport += "<td>$([Math]::Truncate($diskE.FreeSpace / $ConvertToGB)) GB </td>"
}
catch
{
$htmlReport += "<td>NA</td>"
}
try
{
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -le 10)
{
$htmlReport += "<td><font color=red> $([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -gt 10 -and [Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -le 20)
{
$htmlReport += "<td><font color=orange> $([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100)) % </font></td>"
}
if([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100) -gt 20)
{
$htmlReport += "<td><font color=green> $([Math]::Truncate(($diskE.FreeSpace / $diskE.size) * 100)) % </font></td>"
}
}
catch
{
$htmlReport += "<td>NA</td>"
}
$htmlReport += "</tr>"
}
$htmlReport += "</table>"
# Now create an AlternateView from the HTML contents
$messageBody = [Net.Mail.AlternateView]::CreateAlternateViewFromString($htmlReport, 'text/html')
# Add the HTML view to the MailMessage
$mailMessage.AlternateViews.Add($messageBody)
# And finally send the message
$smtpClient.Send($mailMessage)
pause
And last but not least, here is a picture of the sample email that you will get with this embedded table:

Powershell get disk space, convert to HTML and compare a value and if it is less than 15% change the color of the entire row

i have created a PowerShell script that reads from a list of servers, use the Get-WmiObject to get the drive capacity, freespace and then calculates the free space percentage and the converts it all to a nice little HTML.
the question is that i need to figure out how to have it look at the Percentage of free space and if it is below 15% color the entire row of the html table red.
is this even possible?
can anyone help with this?
below is my powershell
$servers = GC XX:\myservers.txt
$date = get-date -Format yyyyMMdd
$time = get-date -Format hhmm
$a = "<style>"
$a = $a + "BODY{background-color:white;}"
$a = $a + "TABLE{border-width: 2px;border-style: solid;border-color: black;border-collapse: separate;width:800px}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:lightblue}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:White}"
$a = $a + "</style>"
Foreach ($s in $servers)
{
Get-WmiObject -Class win32_volume -cn $s |
Select-Object #{LABEL='Computer';EXPRESSION={$s}},
driveletter, #{LABEL='GBfreespace';EXPRESSION={"{0:N2}" -f ($_.freespace/1GB)}},
#{LABEL='Capacity';EXPRESSION={"{0:N2}" -f ($_.capacity/1GB)}},
#{LABEL='Percentage';EXPRESSION={"{0:N2}" -f ($_.freespace/$_.capacity*100)+"%"}} |
ConvertTo-Html -head $a | Out-File -append "D:\Users\PLACE\Desktop\diskspace\Freespace$date-$time.htm"
}
I think you have two options: You can either create the HTML using the ConvertTo-Htmlcmdlet and format the rows afterwards, or you create the html for yourself using some string formats:
$html =
#'
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>BODY{{background-color:white;}}TABLE{{border-width: 2px;border-style: solid;border-color: black;border-collapse: separate;width:800px}}TH{{border-width: 1px;padding: 0px;border-style: solid;border
-color: black;background-color:lightblue}}TD{{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:White}}</style>
</head><body>
<table>
<colgroup><col/><col/><col/><col/><col/></colgroup>
<tr><th>Computer</th><th>driveletter</th><th>GBfreespace</th><th>Capacity</th><th>Percentage</th></tr>
{0}
</table>
</body></html>
'#
$trTemplate ='<tr><td>{0}</td><td>{1}</td><td>{2:N2}</td><td>{3:N2}</td><td {4}>{5:N2}%</td></tr>'
$redStyle = 'style="background-color:Red"'
$tr = #()
$servers = GC XX:\myservers.txt
Foreach ($s in $servers)
{
Get-WmiObject -Class win32_volume -cn $s | Where Capacity | ForEach-Object {
$freespaceInPercent = ($_.freespace/$_.capacity*100)
$red = ''
if ($freespaceInPercent -lt [double]15)
{
$red = $redStyle
}
$tr += ($trTemplate -f $s, $_.driveletter, ($_.freespace/1GB), ($_.capacity/1GB), $red, $freespaceInPercent)
}
}
$html -f ($tr -join '') | Out-File -append "D:\Users\<Your username here>\Desktop\diskspace\Freespace$date-$time.htm"
here is what i ended up with and it works exactly like i need it to
$server_file = "D:\location\Windowsservers.txt"
$html_file_dir = "\\netwolocation\DiskSpace"
$background_color = "#FFFFFF" # can be in rgb format (rgb(140,166,193)) or hex format (#FFFFFF)
$server_name_font = "Tahoma"
$server_name_font_size = "20px"
$server_name_bg_color = "#FFFFFF" # can be in rgb format (rgb(77,108,145)) or hex format (#FFFFFF)
$heading_font = "Tahoma"
$heading_font_size = "14px"
$heading_name_bg_color = "#0099FF" # can be in rgb format (rgb(95,130,169)) or hex format (#FFFFFF)
$data_font = "Tahoma"
$data_font_size = "11px"
$Critical_space = "#FF0000" # (Red) Critical low space
$very_low_space = "#FFFF00" # (Yellow) very low space
$low_space = "#FF9900" # (Orange) low space
$medium_space = "#99CC66" # (Green) medium
$ErrorActionPreference = "SilentlyContinue"
$date = Get-Date -UFormat "%Y%m%d-%H%m"
$html_file = New-Item -ItemType File -Path "$html_file_dir\WindowsServerDiskSpaceCheck_$date-$time.html" -Force
$html_file
Function ConvertBytes {
param($size)
If ($size -lt 1MB) {
$drive_size = $size / 1KB
$drive_size = [Math]::Round($drive_size, 2)
[string]$drive_size + ' KB'
}elseif ($size -lt 1GB){
$drive_size = $size / 1MB
$drive_size = [Math]::Round($drive_size, 2)
[string]$drive_size + ' MB'
}ElseIf ($size -lt 1TB){
$drive_size = $size / 1GB
$drive_size = [Math]::Round($drive_size, 2)
[string]$drive_size + ' GB'
}Else{
$drive_size = $size / 1TB
$drive_size = [Math]::Round($drive_size, 2)
[string]$drive_size + ' TB'
}
}
$html_header = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Server Drive Space</title>
<style type="text/css">
.serverName { text-align:center; font-family:"' + $server_name_font + '"; font-size:' + $server_name_font_size + `
'; font-weight:bold; background-color: ' + $server_name_bg_color + '; border: 1px solid black; width: 150px; }
.headings { text-align:center; font-family:"' + $heading_font + '"; font-size:' + $heading_font_size + `
'; font-weight:bold; background-color: ' + $heading_name_bg_color + '; border: 1px solid black; width: 150px; }
.data { font-family:"' + $data_font + '"; font-size:' + $data_font_size + '; border: 1px solid black; width: 150px; }
#dataTable { border: 1px solid black; border-collapse:collapse; }
body { background-color: ' + $background_color + '; }
#legend { border: 1px solid black; position:absolute; right:500px; top:10px; }
</style>
<script language="JavaScript" type="text/javascript">
<!--
function zxcWWHS(){
if (document.all){
zxcCur=''hand'';
zxcWH=document.documentElement.clientHeight;
zxcWW=document.documentElement.clientWidth;
zxcWS=document.documentElement.scrollTop;
if (zxcWH==0){
zxcWS=document.body.scrollTop;
zxcWH=document.body.clientHeight;
zxcWW=document.body.clientWidth;
}
}
else if (document.getElementById){
zxcCur=''pointer'';
zxcWH=window.innerHeight-15;
zxcWW=window.innerWidth-15;
zxcWS=window.pageYOffset;
}
zxcWC=Math.round(zxcWW/2);
return [zxcWW,zxcWH,zxcWS];
}
window.onscroll=function(){
var img=document.getElementById(''legend'');
if (!document.all){ img.style.position=''fixed''; window.onscroll=null; return; }
if (!img.pos){ img.pos=img.offsetTop; }
img.style.top=(zxcWWHS()[2]+img.pos)+''px'';
}
//-->
</script>
</head>
<body>'
$html_footer = '</body>
</html>'
Add-Content $html_file $html_header
Get-Content $server_file |`
ForEach-Object {
$hostname = Get-WmiObject -Impersonation Impersonate -ComputerName $_ -Query "SELECT Name From Win32_ComputerSystem"
$name = $hostname.Name.ToUpper()
Add-Content $html_file ('<Table id="dataTable"><tr><td colspan="3" class="serverName">' + $name + '</td></tr>
<tr><td class="headings">Drive Letter</td><td class="headings">Total Size</td><td class="headings">Free Space</td><td class="headings">Percent Free</td></tr>')
$drives = Get-WmiObject Win32_LogicalDisk -Filter "drivetype=3" -ComputerName $_ -Impersonation Impersonate
ForEach ($drive in $drives) {
$space_color = ""
$free_space = $drive.FreeSpace
$percent = ($drive.FreeSpace/$drive.size*100)
$percent = [Math]::Round($percent, 2)
If ($percent -le 1) {
$space_color = $Critical_space
}elseif ($percent -le 5) {
$space_color = $very_low_space
}elseif ($percent -le 10) {
$space_color = $low_space
}elseif ($percent -le 15) {
$space_color = $medium_space
}
Add-Content $html_file ('<tr><td class="data">' + $drive.deviceid + '</td><td class="data">' + (ConvertBytes $drive.size) + `
'</td><td class="data" bgcolor="' + $space_color + '">' + (ConvertBytes $drive.FreeSpace) + '</td><td class="data" bgcolor="' + $space_color + '">' + $percent + '</td></tr>')
}
Add-Content $html_file ('</table></br><div id="legend">
<Table><tr><td style="font-size:12px">Less then or equal to 1% Free Space</td><td bgcolor="' + $Critical_space + '" width="10px"></td></tr>
<tr><td style="font-size:12px">1% to 5% Free Space</td><td bgcolor="' + $very_low_space + '" width="10px"></td></tr>
<tr><td style="font-size:12px">5% to 10% Free Space</td><td bgcolor="' + $low_space + '" width="10px"></td></tr>
<tr><td style="font-size:12px">10% to 15% Free Space</td><td bgcolor="' + $medium_space + '" width="10px"></td></tr>
</table></div>')
}
Add-Content $html_file $html_footer

How to Apply Formatting to my Table?

I'm trying to apply a table formatting to my table when it is emailed out, but cannot seem to figure how to do so. I'm pretty sure the problem is related to how the $html variable is set, or maybe when I try to set $EmailTable to $html with $a as the table formatting. Any help is appreciated!
$ProgramA = "A-1"
$MonikerA = "A-2"
$CountA = "1"
$ProgramB = "B-1"
$MonikerB = "B-2"
$CountB = "2"
$ProgramC = "C-1"
$MonikerC = "C-2"
$CountC = "3"
# Create a DataTable
$table = New-Object system.Data.DataTable "TestTable"
$col1 = New-Object system.Data.DataColumn Program,([string])
$col2 = New-Object system.Data.DataColumn Moniker,([string])
$col3 = New-Object system.Data.DataColumn Cases,([string])
$table.columns.add($col1)
$table.columns.add($col2)
$table.columns.add($col3)
# Add content to the DataTable
$row = $table.NewRow()
$row.Program = $ProgramA
$row.Moniker = $MonikerA
$row.Cases = $CountA
$table.Rows.Add($row)
$row = $table.NewRow()
$row.Program = $ProgramB
$row.Moniker = $MonikerB
$row.Cases = $CountB
$table.Rows.Add($row)
$row = $table.NewRow()
$row.Program = $ProgramC
$row.Moniker = $MonikerC
$row.Cases = $CountC
$table.Rows.Add($row)
$row = $table.NewRow()
# Create an HTML version of the DataTable
$html = "<table><tr><td>Program</td><td>Moniker</td><td>Cases</td></tr>"
foreach ($row in $table.Rows)
{
$html += "<tr><td>" + $row[0] + "</td><td>" + $row[1] + "</td><td>" + $row[2] + "</td></tr>"
}
$html += "</table>"
# Here is the formatting I'm trying to apply (which doesn't work)
$a = "<style>BODY{font-family: Verdana; font-size: 9pt;}"
$a = $a + "BODY{background-color:white;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; }"
$a = $a + "TH{border-width: 2px;padding: 7px;border-style: solid;border-color: black;background-color:lightblue;padding-right: 2px;}"
$a = $a + "TD{border-width: 2px;padding: 5px;border-style: solid;border-color: black;background-color:white; padding-right: 2px;}"
$a = $a + "</style>"
$EmailTable = $html ConvertTo-HTML -head $a
$EmailTable = $html ConvertTo-HTML -head $a
This is not how ConvertTo-Html works. Not only is the statement missing a | before ConvertTo-Html, but $html already contains a (manually constructed) HTML table. The purpose of ConvertTo-Html is to do the conversion of a list of objects into an HTML table for you. Change the above to this:
$EmailTable = $table | ConvertTo-Html -Head $a

Powershell functions executed regardless of being called

I'm having trouble understanding how Powershell treats functions. In the following script all functions are called, even if I never actually call the main function. Does powershell not have a concept of call chain?
param([string]$directory)
[string]$global:sqlscript;
$global:types = #{
"double" = "DOUBLE PRECISION";
"int" = "INTEGER";
"QString" = "INTEGER";
"Ignored" = "1";
"Normal" = "2";
"Critical" = "3" }
function resultToSql($element)
{
$global:sqlscript += ('"')
$global:sqlscript += ($element.name + '" ')
$global:sqlscript += ($global:types.Get_Item($element.type))
$global:sqlscript += (',' + [Environment]::Newline)
$global:sqlscript += ('"' + $element.name + "_metric_group" + " " + $global:types.Get_Item($element.metric_group.type))
$global:sqlscript += (',' + [Environment]::Newline)
}
function xmlToSql($source)
{
Write-Host "Parsing...";
$global:sqlscript += "CREATE TABLE IF NOT EXISTS " + '"' + $source.spec.task.ToLower() + '"'+ [Environment]::NewLine
$global:sqlscript += '"' + "id" + '"' + " SERIAL NOT NULL" + [Environment]::NewLine
foreach ($node in $source.spec.measure) {
resultToSql $node
}
foreach ($m in $source.spec.error) {
resultToSql $m
}
$global:sqlscript += '"' + "weighted_sum" + '" ' + $global:types.Get_Item("double") + [Environment]::Newline;
}
function main
{
if ($directory -eq $null) { exit }
else
{
$xmlfiles = Get-ChildItem -Path $directory -include *Spec.xml
foreach ($xmlfile in $xmlfiles)
{
Write-Host "Filename:" $xmlfile;
[xml]$spec = Get-Content $file;
xmlToSql $spec;
Write-Host $script;
}
}
}
PowerShell cant magically detect changes to scripts, close the ISE and re-open it then run your script again. If that fails take the contents of your script paste it in the ISE and click the execute button, i just did that and main didnt run.
Unlike a C/C++/C# program, "you" need to call the Main function - at the bottom of this script. When you run the script above all it does is create the functions you've defined. It doesn't run any of them. You have to do that by calling them in the script and one of those calls has to be at the script level (outside any functions).
Remove the main function container so it resembles the code below:
if ($directory -eq $null) { exit }
else
{
$xmlfiles = Get-ChildItem -Path $directory -include *Spec.xml
foreach ($xmlfile in $xmlfiles)
{
Write-Host "Filename:" $xmlfile;
[xml]$spec = Get-Content $file;
xmlToSql $spec;
Write-Host $script;
}
}
Powershell doesn't execute from Main like C#/C++. It executes what statements are first received outside functions. In this case above it will execute the if statement first as it appears outside the function box.

Script Exporting Policies and Conditions

I would like to automatically script out all SQL Server 2008 policies and conditions on a server each night and compare the files to my version control system. In the UI, I can script out individual policies by right-clicking the policy and selecting Export Policy. Is it possible to script out policies and conditions via SMO or PowerShell?
Ideally, I would like to incorporate this into my existing PowerShell script that generates scripts for all of my other server and database objects. Here's the script that currently does this action:
# Load needed assemblies
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMOExtended")| Out-Null;
#Specify target server and databases.
$sql_server = "SomeServerName"
$SMOserver = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList "$sql_server"
$databases = $SMOserver.Databases
$BaseSavePath = "T:\SomeFilePath\" + $sql_server + "\"
#Remove existing objects.
Remove-Item $BaseSavePath -Recurse
#Script server-level objects.
$ServerSavePath = $BaseSavePath
$ServerObjects = $SMOserver.BackupDevices
$ServerObjects += $SMOserver.Endpoints
$ServerObjects += $SMOserver.JobServer.Jobs
$ServerObjects += $SMOserver.LinkedServers
$ServerObjects += $SMOserver.Triggers
foreach ($ScriptThis in $ServerObjects | where {!($_.IsSystemObject)})
{
#Need to Add Some mkDirs for the different $Fldr=$ScriptThis.GetType().Name
$scriptr = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($SMOserver)
$scriptr.Options.AppendToFile = $True
$scriptr.Options.AllowSystemObjects = $False
$scriptr.Options.ClusteredIndexes = $True
$scriptr.Options.DriAll = $True
$scriptr.Options.ScriptDrops = $False
$scriptr.Options.IncludeHeaders = $False
$scriptr.Options.ToFileOnly = $True
$scriptr.Options.Indexes = $True
$scriptr.Options.Permissions = $True
$scriptr.Options.WithDependencies = $False
<#Script the Drop too#>
$ScriptDrop = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($SMOserver)
$ScriptDrop.Options.AppendToFile = $True
$ScriptDrop.Options.AllowSystemObjects = $False
$ScriptDrop.Options.ClusteredIndexes = $True
$ScriptDrop.Options.DriAll = $True
$ScriptDrop.Options.ScriptDrops = $True
$ScriptDrop.Options.IncludeHeaders = $False
$ScriptDrop.Options.ToFileOnly = $True
$ScriptDrop.Options.Indexes = $True
$ScriptDrop.Options.WithDependencies = $False
<#This section builds folder structures. Remove the date folder if you want to overwrite#>
$TypeFolder=$ScriptThis.GetType().Name
if ((Test-Path -Path "$ServerSavePath\$TypeFolder") -eq "true") `
{"Scripting Out $TypeFolder $ScriptThis"} `
else {new-item -type directory -name "$TypeFolder"-path "$ServerSavePath"}
$ScriptFile = $ScriptThis -replace ":", "-" -replace "\\", "-"
$ScriptDrop.Options.FileName = $ServerSavePath + "\" + $TypeFolder + "\" + $ScriptFile.Replace("]", "").Replace("[", "") + ".sql"
$scriptr.Options.FileName = $ServerSavePath + "\" + $TypeFolder + "\" + $ScriptFile.Replace("]", "").Replace("[", "") + ".sql"
#This is where each object actually gets scripted one at a time.
$ScriptDrop.Script($ScriptThis)
$scriptr.Script($ScriptThis)
} #This ends the object scripting loop at the server level.
#Script database-level objects.
foreach ($db in $databases)
{
$DatabaseObjects = $db.ApplicationRoles
$DatabaseObjects += $db.Assemblies
$DatabaseObjects += $db.ExtendedStoredProcedures
$DatabaseObjects += $db.ExtendedProperties
$DatabaseObjects += $db.PartitionFunctions
$DatabaseObjects += $db.PartitionSchemes
$DatabaseObjects += $db.Roles
$DatabaseObjects += $db.Rules
$DatabaseObjects += $db.Schemas
$DatabaseObjects += $db.StoredProcedures
$DatabaseObjects += $db.Synonyms
$DatabaseObjects += $db.Tables
$DatabaseObjects += $db.Triggers
$DatabaseObjects += $db.UserDefinedAggregates
$DatabaseObjects += $db.UserDefinedDataTypes
$DatabaseObjects += $db.UserDefinedFunctions
$DatabaseObjects += $db.UserDefinedTableTypes
$DatabaseObjects += $db.UserDefinedTypes
$DatabaseObjects += $db.Users
$DatabaseObjects += $db.Views
#Build this portion of the directory structure out here. Remove the existing directory and its contents first.
$DatabaseSavePath = $BaseSavePath + "Databases\" + $db.Name
new-item -type directory -path "$DatabaseSavePath"
foreach ($ScriptThis in $DatabaseObjects | where {!($_.IsSystemObject)})
{
#Need to Add Some mkDirs for the different $Fldr=$ScriptThis.GetType().Name
$scriptr = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($SMOserver)
$scriptr.Options.AppendToFile = $True
$scriptr.Options.AllowSystemObjects = $False
$scriptr.Options.ClusteredIndexes = $True
$scriptr.Options.DriAll = $True
$scriptr.Options.ScriptDrops = $False
$scriptr.Options.IncludeHeaders = $False
$scriptr.Options.ToFileOnly = $True
$scriptr.Options.Indexes = $True
$scriptr.Options.Permissions = $True
$scriptr.Options.WithDependencies = $False
<#Script the Drop too#>
$ScriptDrop = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($SMOserver)
$ScriptDrop.Options.AppendToFile = $True
$ScriptDrop.Options.AllowSystemObjects = $False
$ScriptDrop.Options.ClusteredIndexes = $True
$ScriptDrop.Options.DriAll = $True
$ScriptDrop.Options.ScriptDrops = $True
$ScriptDrop.Options.IncludeHeaders = $False
$ScriptDrop.Options.ToFileOnly = $True
$ScriptDrop.Options.Indexes = $True
$ScriptDrop.Options.WithDependencies = $False
<#This section builds folder structures. Remove the date folder if you want to overwrite#>
$TypeFolder=$ScriptThis.GetType().Name
if ((Test-Path -Path "$DatabaseSavePath\$TypeFolder") -eq "true") `
{"Scripting Out $TypeFolder $ScriptThis"} `
else {new-item -type directory -name "$TypeFolder"-path "$DatabaseSavePath"}
$ScriptFile = $ScriptThis -replace ":", "-" -replace "\\", "-"
$ScriptDrop.Options.FileName = $DatabaseSavePath + "\" + $TypeFolder + "\" + $ScriptFile.Replace("]", "").Replace("[", "") + ".sql"
$scriptr.Options.FileName = $DatabaseSavePath + "\" + $TypeFolder + "\" + $ScriptFile.Replace("]", "").Replace("[", "") + ".sql"
#This is where each object actually gets scripted one at a time.
$ScriptDrop.Script($ScriptThis)
$scriptr.Script($ScriptThis)
} #This ends the object scripting loop.
} #This ends the database loop.
You have a couple of choices from SMO/Powershell.
1: SQLPS/PowerShell with SQL loaded
SQLSERVER:\SQLPolicy\\DEFAULT\Policies
you can then dig through it, i didnt see an "export" but you can certianly get the info out of it.
2: SMO
basically SMO has a Microsoft.SqlServer.Management.DMF namespace that has severial policy objects (what you end up with in the PowerShell side of things) Policy, PolicyStore, PolicyCondition etc, rather than write out an example, you can find one here.
http://rdbmsexperts.com/Blogs/archives/295
again i didnt see an "export" method anywhere, but you could probably spit out what you needed easily enough.