Error catching mechanism in powershell - sql-server

Just a broad question here - is there a generic error catching mechanism in powershell? I'm having issue where by connection to MSSSQL server times out randomly via a powershell script and then re-running it would be fine.
Just want to know if there is any try-catch or similar error capturing in powershell available. Or if any one has better solution to catch connection timeouts let me know.
Thank you.
Zulfiqar

Yes, you could use Try-Catch block.
Here is an example:
Write-Host "Disabling IP v6 - Reboot required after installation/update!"
$breboot = $True
try
{
New-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\" -Name "DisabledComponents" -Value 0xffffffff -PropertyType "DWord" -ErrorAction Stop
}
catch
{
Write-Host ("IPv6 already disabled - no reboot required!")
$breboot = $False
}

I recommend the blogpost by kevin marquette:
https://kevinmarquette.github.io/2017-04-10-Powershell-exceptions-everything-you-ever-wanted-to-know/.

Related

How can I use the previous return to determine if I can send the alert by Powerhsell?

I'm going to use Dbatools to check my job is running or not. If it isn't running I need to send out an email alert.
I only have a few backgrounds with PowerShell programming.
# Import-Module D:\Tools\dbatools\dbatools.psd1 if it isn't loaded
If ( ! (Get-module dbatools )) {
Import-Module D:\Tools\dbatools\dbatools.psd1
}
# Get the job status
Get-DbaAgentJob -SqlInstance My_SQL_Instance -Job My_Job_Name | Out-File C:\DBA\Result.txt
# Send the email alert if the job is not running
Send-MailMessage -From My_Email_Address -Subject "My_Job_Name job is not running..." -To User_Email_Address -Attachments C:\DBA\Result.txt -Body "The MiantoEDW replication job is not running..." -BodyAsHtml -SmtpServer My_SmtpServer
I need to verify the property of CurrentRunStatus to determine to send an email alert or not.
I would do something like the following:
$jobStatus = Get-DbaAgentJob -SqlInstance My_SQL_Instance -Job My_Job_Name
$jobStatus | Select-Object Name,CurrentRunStatus | Export-Csv C:\DBA\Result.csv -NoTypeInformation
if ($jobStatus.CurrentRunStatus -ne "Executing") {
# Run some code if job is not running
Send-MailMessage -From My_Email_Address -Subject "My_Job_Name job is not running..." -To User_Email_Address -Attachments C:\DBA\Result.csv -Body "The MiantoEDW replication job is not running..." -BodyAsHtml -SmtpServer My_SmtpServer
}
else {
# Run some code if job is running
}
Get-DbaAgentJob doesn't display the CurrentRunStatus property by default. You will need to retrieve it, which is done by Select-Object CurrentRunStatus. Since the command outputs an object, I chose to use Export-Csv to export a cleaner output that aligns the object properties and values. $jobStatus stores the output of the Get-DbaAgentJob command. Accessing the $jobStatus.CurrentRunStatus property for value Executing will verify if a job is currently running.
I've not used dbatools but I assume the CurrentRunStatus is available in the Result.txt file you're outputting to?
If so, assign the result of Get-DbaAgentJob to a variable and then Out-File from that variable. Then access the CurrentRunStatus property from the variable to determine whether or not to send the alert.

Powershell Script to Start Service if it is Stopped and wait for minute and send an email notification

I am very new to Powershell and in learning stage, I have tried to create an script to do automate below task. This script is not working as i am expected. Could you please review it and give me some help on it.
My task is,
I am trying to find out SQL Services (more than one SQL services in multiple servers) which are in stopped state and trying to start it, Waiting for an minute to complete the service start and verifying the service status again. If still it is stopped state i am trying to sending an email to setup of people for an action.
Could you please review the below code and correct the mistake, i tried to find it but unable to do
#Define servers & Services Variables
$Servers = GC "E:\Bhanu\SQLServer.txt"
$Services = GC "E:\Bhanu\SQLService.txt"
#Function Call
Function ServiceStatus ($Servers, $Services)
{
foreach ($Server in $Servers)
{
foreach ($Service in $Services)
{
$Servicestatus = get-service -ComputerName $Server -Name $Service
if ($Servicestatus.Status -eq "Stopped")
{
Start-service $Service
Start-Sleep -Seconds 60
$ServiceStatus1 = Get-Service -ComputerName $Server -Name $Service
if ($Servicestatus1.Status -eq "Stopped")
{
FuncMail -To “abc#gmail.com” -From “abc#gmail.com” -Subject $Server + $Service "fails to Start, Take immediate Action to avoid Impact” -Body $ServiceName "Service fails to Start, Take immediate Action to avoid Impact” -smtpServer “servername”
}
}
}
}
}
function FuncMail
{
#param($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
param($To, $From, $Subject, $Body, $smtpServer)
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $From
$msg.To.Add($To)
$msg.Subject = $Subject
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)
}
servicestatus $Servers $Services
Please let me know if you need anything here from my end
Hi this isn't the best approach and i'm doing it in quick way.
note %=foreach-object; ?=Where-Object.
You have to save your password on one file if your smtp-server require authentication otherwise don't run it using read-host -assecurestring | convertfrom-securestring | out-file "C:\Secure\Password.txt"
I'm also assuming you have your servers saved on one file.
My solution is to start all sql server service if you want to start specific just save the service name on one file on separate line.
The code to execute bellow.
#Loading Server and service details
$Services=Get-content C:\PS\Service.txt
$servidores=get-content C:\PS\Servers\Servers.txt
#Loading Mail credential
$Mailpasswordpath="C:\PS\Securestring.txt"
$Mailusername="DOmain\User"
$password=cat $Mailpasswordpath |ConvertTo-Securestring
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Mailusername,$password
$servidores|Foreach-Object{Get-Service -ComputerName $_ -Name $Services }| #Get the services running on all servers
Where-Object{$_.Status -eq "Stopped"}| #Which status is equal stopped
Foreach-Object{
$_.Start(); #try to start
Start-Sleep -Seconds 60; #wait one minute
$_.Refresh(); #refresh then service to update status
#validate is status still stopped
if($_.Status -eq "Stopped")
{
#LOADING Mail details
$To="user#domain.com"
$subject="$($_.MachineName) $($_.Name) fails to Start, Take immediate Action to avoid Impact"
$From="ServiceStatus#domain.com"
$smtp="Server.domain.com"
$body="$($_.Name) Service fails to Start, Take immediate Action to avoid Impact"
#Sending email to notify
Send-MailMessage -To $To -Subject $subject -From $From -SmtpServer $smtp -Body $body -Credential $Cred
}
}
P.S: It's not the best approach I only decide to solve this problem. if you want we can create a function together later just test it first.

How to capture DacSevices.Deploy output?

So I've managed to deploy our DACPAC schema via Octopus. I'm using a Deploy.ps1 script interacting with .Net objects just like the article describes.
I'd like to make the deployment process more transparent by including the "standard output" you get from sqlcmd in our Octopus logs. I'm looking for the the generated schema modification messages as well as any custom migration migration messages our developers have put into the pre/post scripts.
The only workaround I can think of is to first generate the script with the DACPAC services and then run it with sqlcmd.exe. Any ideas?
Found the solution, posting in case someone else runs across this. You simply need to subscribe to the your DacService's Message event.
C# sample:
var services = new Microsoft.SqlServer.Dac.DacServices("data source=machinename;Database=ComicBookGuy;Trusted_connection=true");
var package = Microsoft.SqlServer.Dac.DacPackage.Load(#"C:\Database.dacpac");
var options = new Microsoft.SqlServer.Dac.DacDeployOptions();
options.DropObjectsNotInSource = true;
options.SqlCommandVariableValues.Add("LoginName", "SomeFakeLogin");
options.SqlCommandVariableValues.Add("LoginPassword", "foobar!");
services.Message += (object sender, Microsoft.SqlServer.Dac.DacMessageEventArgs eventArgs) => Console.WriteLine(eventArgs.Message.Message);
services.Deploy(package, "ComicBookGuy", true, options);
Powershell sample (executed by the Octopus Tentacle):
# This script is run by Octopus on the tentacle
$localDirectory = (Get-Location).Path
$tagetServer = $OctopusParameters["SQL.TargetServer"]
$databaseName = "ComicBookGuy"
Add-Type -path "$localDirectory\lib\Microsoft.SqlServer.Dac.dll"
$dacServices = New-Object Microsoft.SqlServer.Dac.DacServices ("data source=" + $tagetServer + ";Database=" + $databaseName + "; Trusted_connection=true")
$dacpacFile = "$localDirectory\Content\Unity.Quotes.Database.dacpac"
$dacPackage = [Microsoft.SqlServer.Dac.DacPackage]::Load($dacpacFile)
$options = New-Object Microsoft.SqlServer.Dac.DacDeployOptions
$options.SqlCommandVariableValues.Add("LoginName", $OctopusParameters["SQL.LoginName"])
$options.SqlCommandVariableValues.Add("LoginPassword", $OctopusParameters["SQL.LoginPassword"])
$options.DropObjectsNotInSource = $true
Register-ObjectEvent -InputObject $dacServices -EventName "Message" -Action { Write-Host $EventArgs.Message.Message } | out-null
$dacServices.Deploy($dacPackage, $databaseName, $true, $options)
In the powershell version I couldn't get the handy "Add_EventName" style of event notification working so I had to use the clunky cmdlet. Meh.
Use sqlpackage instead of sqlcmd to deploy dacpac.
Get Latest version here : https://msdn.microsoft.com/en-us/mt186501
$sqlpackage = "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\120\sqlpackage.exe"
It will automatically output errors on the console. We use TFS build definition and call powershell and it is able to display errors that happened during a deploy.
Usage:
& $sqlpackage /Action:Publish /tsn:$dbServer /tdn:$database /sf:$mydacpac/pr:$dbProfile /variables:myVariable=1
This variation captures output but also allows you to capture and react to deploy failures by catching the exception
function Load-DacPacAssembly()
{
$assemblyName = "Microsoft.SqlServer.Dac.dll"
$packageFolder = <some custom code to find our package folder>
$dacPacAssembly = "$packageFolder\lib\net46\$assemblyName"
Write-Host "Loading assembly $assemblyName"
Add-Type -Path "$dacPacAssembly"
}
function Publish-Dacpac($dacpac, $publishProfile){
Load-DacPacAssembly
Write-Host "Loading profile $publishProfile..."
$dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($publishProfile)
$dacService = New-Object Microsoft.SqlServer.dac.dacservices ($dacProfile.TargetConnectionString)
Write-Host "Loading dacpac $dacpac"
$dacPackage = [Microsoft.SqlServer.Dac.DacPackage]::Load($dacpac)
$event = Register-ObjectEvent -InputObject $dacService -EventName "Message" -Action {
$message = $EventArgs.Message
$colour = "DarkGray"
if ($message -contains "Error SQL")
{
$colour = "Red"
}
Write-Host $message -ForegroundColor $colour
}
Write-Host "Publishing...."
try {
$dacService.deploy($dacPackage, $dacProfile.TargetDatabaseName, $true, $dacProfile.DeployOptions)
}
catch [Microsoft.SqlServer.Dac.DacServicesException]
{
$message = $_.Exception.Message
Write-Host "SQL Publish failed - $message" -ForegroundColor Red # Customise here for your build system to detect the error
exit;
}
finally
{
Unregister-Event -SourceIdentifier $event.Name
}
}

How do I search through multiple domain controller's security logs and limited dates in Powershell?

Good Morning, Good Afternoon, Good Evening, or Goodnight!
I'm trying to undertake a project for my internship. The purpose of the script is how to search for lockout events for a specific user within certain (given) time constraints throughout the different security logs.
$ComputerName =
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().FindDomainController().Name
$EventList = Get-Eventlog –ComputerName $ComputerName -LogName Security
-InstanceID 4740 -Message *Username*
$EventList | Format-List -Property TimeGenerated,Message
So I'm curious on how to use the date class to output error messages from a specific date. For example, if I want to only view error messages from 5/05/2014 to 5/20/2014. Also as far as how to loop through each of the Domain Controllers on our network. I figure it'd be easy to just set up some type of loop construct for that. (Once I know the proper syntax)
So there's my code initially. If you would like a similiar code for your own jolly kicks to find whatever type of error you'd like, then take this.
$ComputerName = "REPLACEWITHYOURCOMPUTERNAME"
$EventList = Get-Eventlog –ComputerName $ComputerName -LogName Security -Message *REPLACEWITHYOURUSERNAME*
$EventList | Format-List -Property TimeGenerated,Message
To search for 2 days ago max:
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-2))
To search yesterday only:
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-2)) -Before ((Get-Date).AddDays(-1))
I think you should grasp the idea by now. Of course, for a final script to be functional you would have to wrap it around in parameters and so on.
Edit: You also asked how to create a loop.
Example:
$ComputerList = Get-Content ComputerList.txt
foreach ($Computer in $Computerlist) {
Get-EventLog -Logname Security -Computername $Computer
Write-host "$Computer has been checked"
}
As a side note for several-DC environment this is going to be nasty network-killer script. Parsing Event logs remotely is generally a bad idea. Instead, think about Invoke-Command to launch the queries and wait until DCs send you the output. This way they are executed locally and all you receive is an output. Much faster in general in conjunction with Start-Job for example.

Release Management 12 - Create Web Site with Host Header

Is there a way to create a web site with Release Management v12 that will include a host header option?
My goal is to be able to host multiple sites on a single server, all binding to port 80 with different host headers. i.e. http://project1.development.local/, http://project2.development.local/
I'm able to create a web site with a host header from the AppCmd.exe, yet this requires an administration rights. Thought about using powershell, yet a UAC prompt will be triggered.
For right now, I'm having to manually create the server's web site to include the host header and I'd like to have a totally automated release process.
TIA!
There's nothing in-the-box for it, but as luck would have it, I've hacked something together to handle site bindings:
param(
$SiteName=$(throw "Site Name must be entered"),
$HostHeader,
$IpAddress,
$Port,
$RemoveDefault=$(throw "You must specify true or false")
)
Import-Module WebAdministration
try {
$bindingExists = (Get-WebBinding "$SiteName" -Port "$Port" -Protocol "http" -HostHeader "$HostHeader" -IPAddress "$IpAddress")
if (!$bindingExists) {
Write-host "Creating binding for $SiteName : Host header $HostHeader and IP Address $IpAddress"
New-WebBinding "$SiteName" -Port $Port -Protocol "http" -HostHeader "$HostHeader" -IPAddress "$IpAddress"
}
else {
Write-host "Site $SiteName already has binding for host header $HostHeader and IP Address $IpAddress"
}
if ($RemoveDefault -eq "true") {
$defaultBinding = Get-WebBinding "$SiteName" | where {$_.bindingInformation -eq "*:80:" }
if ($defaultBinding -ne $null) {
Write-Host "Default binding exists... removing."
$defaultBinding | Remove-WebBinding
}
else {
Write-Host "Default binding does not exist"
}
}
}
catch {
Write-host $_
exit 1
}
exit 0
You can create a custom tool in RM to leverage this script, just pass it the parameters specified in the param block.
You should never have to use AppCmd.exe... If the built-in tools don't meet your needs, the WebAdministration PowerShell module should be able to do everything else.

Resources