I am new to powershell and I am trying to script out all SQL agent jobs. I have found a piece of code that does that thanks to ENRIQUE at SOLID QUALITY MENTORS.
My question is, how do I script a if exists, drop for every job?
Options.ScriptJobs does not seem to do what I think it should do?
param([string]$serverName,[string]$jobNameFile)
function script-SQLJobs([string]$server,[string]$jobNameFile)
{
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
$srv = New-Object Microsoft.SqlServer.Management.Smo.Server("$server")
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$scrp = New-Object Microsoft.SqlServer.Management.Smo.Scripter($srv)
$scrp.Options.ScriptDrops = $TRUE
$scrp.Options.WithDependencies = $TRUE
$jobNameFile = "C:\SQLJOBS\Jobs.sql"
remove-item $jobNameFile
$jobs = $srv.JobServer.get_Jobs()
$jobs=$jobs | Where-Object {$_.Name -notlike "sys*"}
foreach($job in $jobs)
{
$script=$job.Script()
$script >> $jobNameFile
"GO" >> $jobNameFile
}
}
script-SQLJobs $serverName $jobNameFile
Many Thanks.
You could exclude scripts that do not match the words 'drop table'. For example:
$srv.JobServer.Jobs | Where-Object {$_.Name -notlike "sys*"} | Foreach-Object{
$script = $_.Script()
if($script -notmatch 'DROP TABLE')
{
$script+ "`nGO`n"
}
} | Out-File $jobNameFile
Another (cosmetic) option would be to check all job steps command:
$srv.JobServer.Jobs | Where-Object {$_.Name -notlike "sys*"} | Foreach-Object{
$cmd = $_.JobSteps | select -expand Command
if($cmd -match 'DROP TABLE')
{
$_.script()+ "`nGO`n"
}
} | Out-File $jobNameFile
You need to provide your script options object to the script method:
$script=$job.Script($scrp)
Here is a Powershell script copied from http://www.johnsansom.com/script-sql-server-agent-jobs-using-powershell/ which has been extended to do what you want.
# Date: 16/02/14
# Author: John Sansom
# Description: PS script to generate all SQL Server Agent jobs on the given instance.
# The script accepts an input file of server names.
# Version: 1.1
#
# Example Execution: .\Create_SQLAgentJobSripts.ps1 .\ServerNameList.txt
param([String]$ServerListPath)
#Load the input file into an Object array
$ServerNameList = get-content -path "Servers.txt"
#$ServerNameList = get-content -path $ServerListPath
#Load the SQL Server SMO Assemly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
#Create a new SqlConnection object
$objSQLConnection = New-Object System.Data.SqlClient.SqlConnection
#For each server in the array do the following..
foreach($ServerName in $ServerNameList)
{
Try
{
$objSQLConnection.ConnectionString = "Server=$ServerName;Integrated Security=SSPI;"
Write-Host "Trying to connect to SQL Server instance on $ServerName..." -NoNewline
$objSQLConnection.Open() | Out-Null
Write-Host "Success."
$objSQLConnection.Close()
}
Catch
{
Write-Host -BackgroundColor Red -ForegroundColor White "Fail"
$errText = $Error[0].ToString()
if ($errText.Contains("network-related"))
{Write-Host "Connection Error. Check server name, port, firewall."}
Write-Host $errText
continue
}
#IF the output folder does not exist then create it
$OutputFolder = ".\$ServerName"
$DoesFolderExist = Test-Path $OutputFolder
$null = if (!$DoesFolderExist){MKDIR "$OutputFolder"}
#Create a new SMO instance for this $ServerName
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $ServerName
#Script out each SQL Server Agent Job for the server
foreach($job in $srv.JobServer.Jobs)
{
Write-Host $job.Name
$script = ""
$script = $script + "-- Uninstall the job" + "`r`n"
$script = $script + "DECLARE #jobId binary(16)" + "`r`n"
$script = $script + "SELECT #jobId = job_id FROM msdb.dbo.sysjobs WHERE (name = N'$job')" + "`r`n"
$script = $script + "IF (#jobId IS NOT NULL)" + "`r`n"
$script = $script + "BEGIN" + "`r`n"
$script = $script + " EXEC msdb.dbo.sp_delete_job #job_id=#jobId, #delete_unused_schedule=1" + "`r`n"
$script = $script + "END" + "`r`n"
$script = $script + "GO`r`n"
$script = $script + "`r`n"
$script = $script + "-- Install the job" + "`r`n"
$script = $script + $job.Script()
$script = $script + "GO`r`n"
$fileName = $job.Name -replace '\\', ''
$script | out-file ".\$OutputFolder\$fileName.sql"
}
}
NOTE: You don't actually want to use the SMO created DROP command because it is dependent on the job id, which makes the resulting script non-reusable.
Related
I'm new to scripting any I am trying to make a script that reads PC list from a CSV file and check if a specific process is runnuing.
the following code is:
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\to\log.txt" # Location of local log file name
$CsvLocation = "\\path\to\CSV\File\Table.csv" #Location of the CSV file
$csvdata = Import-Csv $ipmCsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp.exe"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
$Processes = get-process | Where-Object {$_.ProcessName -Like "nscp*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -eq $IcingaProc){
Write-Host "Program installed succefully"
LogWrite ("Program installed succefully in "+$pcname)
LogWrite `r`n
}
else{
LogWrite ("Could not find any CINIGA process")
LogWrite `r`n
}
}
}
This is the output I get:
PS User> powershell.exe -ExecutionPolicy Bypass –Noprofile -file "Path\to\script.ps1"
Looking for process nscp.exe in PC1...
Looking for process nscp.exe in PC2...
Looking for process nscp.exe in PC3...
Looking for process nscp.exe in PC4...
it doesn't seems to enter the 2nd foreach loop..
I was indeed pointing to localhost and changed to point to $pcName.
When i "turned" the if command with -ne instead of -eq it worked well!
Thank you for all your help !
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\To\LogFIle.txt" # Location of local log file name
$CsvLocation = "\\Path\To\Referance\CSV\Table.csv"
$csvdata = Import-Csv $CsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
if (Test-Connection -ComputerName $pcname -Count 1 -Quiet) {
$Processes = get-process -ComputerName $pcname | Where-Object {$_.ProcessName -Like "ns*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -ne $IcingaProc){
Write-Host ("Could not find any process")
}
else{
Write-Host "Program is installed succefully"
LogWrite ("Program is installed on "+$pcname)
}
}
}
else{
Write-Host ("The PC is not reachable!")
}
}
I have the following powershell with the aim of collecting a reg value from a list of remote machines, Ive tried using a test-connection to speed up process where devices are not online but the test-connection fails for all....
Here is the script:
clear
$servers = Get-Content -Path C:\ukdeviceswin10.txt
$PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$'
$ProfileList = ForEach ($server in $servers)
{ if(test-connection $server -quiet -count 1)
{
invoke-command -computername $server {gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} |
Select #{name="SID";expression={$_.PSChildName}}}}
foreach ($profile in $profilelist) {
$profile = $profile -replace ".$"
$profile = $profile.substring(6,46)
$profile = $profile -replace "[;]"
$profile = $profile -replace "[ ]"
$profiletest = $profile.substring(0,8)
if ($profiletest -eq "S-1-5-21"){
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::Users, "$server")
$profilekey = $profile + "\\SOFTWARE\\Microsoft\\Office\\Outlook\\Addins\\CofenseOutlookReporter.AddinModule"
$RegKey= $Reg.OpenSubKey("$profilekey")
if ($regKey -eq $null){} else {$LoadbehaviourVersion = $RegKey.GetValue("LoadBehaviour")
$results = $server + " " + $profile + " " + $LoadbehaviourVersion | out-file -filepath c:\dell\regresults.txt -Append
}
$RegKey=""
$LoadbehaviourVersion=""
}
}
}
I have a database server not in a domain, that the task scheduler is corrupt and I don't have the time or resources to repair it yet so I created a script that runs from another non-domain server using task scheduler that activates a second script located on the database server to copy files. On the database server the script when activated manually sees everything and does its job but when I try remotely activating the script it runs and sees everything but the network drives (W:). I am using credssp in the script which connects fine see below - Question how can I get the script to see the network share on the remote server
-------------Script A---------------------------------------------
$username = "Administrator"
$computerA = "<addressA>"
$computerB = "<addressB>"
$PwdLocation = "c:\test\password.txt"
#enable-wsmancredssp -role client -delegatecomputer $computerB
$password = Get-Content $PwdLocation | ConvertTo-SecureString
$credential = new-object -typename System.Management.Automation.PSCredential - argumentlist $username,$password
Invoke-Command -ComputerName $computerB -ScriptBlock {\\<remote server>\test\delete.ps1} -ArgumentList $ComputerA -Credential $credential
exit
----------------------Remote Script B ---------------------------------------
Function mailer {
$recipients = "<names>"
$smtp = "smtp.org"
$emailTo = $recipients
$emailFrom = "no-reply#org.org"
$smtpserver="smtp.org"
$smtp=New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $message)
}
Function StopEverything {
$subject = "Stopped Script Delete.PS1 becuase of no connection"
$message = ""
for($i=0;$i -le $tot-1;$i++)
{
$path = $bfs[$i]
if (Exists-Dir($path))
{
$message += [string]::concat($path, " Connected`n")
}
else
{
$message += [string]::concat($path, " Cannot Connect`n")
}
}
mailer
Exit
}
Function Exists-Dir($path) {
if ([IO.Directory]::Exists($path))
{
return $true;
}
else
{
return $false;
}
}
$ScriptStart = (Get-Date)
[array]$bfs = "F:\Backups\NetPerfMon","F:\Backups\NetPerfMon_Aux","W:\Backups\NetPerfMon_Aux","W:\Backups\NetPerfMon"
$tot = $bfs.count
for($i=0;$i -le $tot-1;$i++)
{
$path = $bfs[$i]
if (Exists-Dir($path))
{
$message += [string]::concat($path, " Connected`n")
$subject = "Start Script Delete.PS1 on " + $ScriptStart
}
else
{
$message += [string]::concat($path, " Cannot Connect`n")
StopEverything
}
}
$message += " "
$message +=
mailer
$limit = (Get-Date).AddDays(-7)
$limit1 = (Get-Date).AddDays(-14)
$limit2 = (Get-Date).AddDays(-1)
$FB = "F:\Backups\NetPerfMon"
$FBAux = "F:\Backups\NetPerfMon_Aux"
$WBAux = "W:\Backups\NetPerfMon_Aux"
$WBBak = "W:\Backups\NetPerfMon"
Get-ChildItem -Path $FB | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime - lt $limit } | Remove-Item -Force | OUT-NULL #Remove items greater than 7 days
Get-ChildItem -Path $FBAux | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt $limit } | Remove-Item -Force | OUT-NULL #Remove items greater than 7 days
Get-ChildItem -Path $WBBak | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt $limit1} | Remove-Item -Force | OUT-NULL #Remove items greater than 14 days
Get-ChildItem -Path $FB | where {$_.extension -eq ".bak"} | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -gt $limit2} | Copy-Item -destination $WBBak | OUT- NULL #Copy items within 1 day that have extension .bak
Get-ChildItem -Path $FBAux | where {$_.extension -eq ".bak"} | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -gt $limit2} | Copy-Item -destination $WBAux | OUT- NULL #Copy items within 1 day that have extension .bak
$ScriptEnd = (Get-Date)
$RunTime = New-Timespan -Start $ScriptStart -End $ScriptEnd
"Elapsed Time: {0}:{1}:{2}" -f $RunTime.Hours,$Runtime.Minutes,$RunTime.Seconds
$subject = "Stop Script Delete.PS1 on " + $ScriptEnd
$message = ""
$message += " "
$message += "Time to completion: {0}:{1}:{2}" -f $RunTime.Hours,$Runtime.Minutes,$RunTime.Seconds
mailer
Use the complete UNC paths not drive letters OR do a "net use X: \my\unc\share" at the top of your script mapping the drives right in the script.
edit: if you do this you will have to explicitly specify the password in the net use command: net use x: \\my\share /user:mydom\myuser mypassword
I have a powershell script that accepts parameters in the form of "sender-ip=10.10.10.10" and that runs perfectly with elevated credentials
#script.ps1
$userID=$NULL
$line_array = #()
$multi_array = #()
[hashtable]$my_hash = #{}
foreach ($i in $args){
$line_array+= $i.split(" ")
}
foreach ($j in $line_array){
$multi_array += ,#($j.split("="))
}
foreach ($k in $multi_array){
$my_hash.add($k[0],$k[1])
}
$Sender_IP = $my_hash.Get_Item("sender-ip")
<#Gather information on the computer corresponding to $Sender_IP#>
$Win32OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Sender_IP
<#Determine the build number#>
$Build = $Win32OS.BuildNumber
<#Running Windows Vista with SP1 and later, i.e. $Build is greater than or equal to 6001#>
if($Build -ge 6001){
$Win32User = Get-WmiObject -Class Win32_UserProfile -ComputerName $Sender_IP
$Win32User = $Win32User | Sort-Object -Property LastUseTime -Descending
$LastUser = $Win32User | Select-Object -First 1
$UserSID = New-Object System.Security.Principal.SecurityIdentifier($LastUser.SID)
$userId = $UserSID.Translate([System.Security.Principal.NTAccount])
$userId = $userId.Value
}
<#Running Windows Vista without SP1 and earlier, i.e $Build is less than or equal to 6000#>
elseif ($Build -le 6000){
$SysDrv = $Win32OS.SystemDrive
$SysDrv = $SysDrv.Replace(":","$")
$ProfDrv = "\\" + $Sender_IP + "\" + $SysDrv
$ProfLoc = Join-Path -Path $ProfDrv -ChildPath "Documents and Settings"
$Profiles = Get-ChildItem -Path $ProfLoc
$LastProf = $Profiles | ForEach-Object -Process {$_.GetFiles("ntuser.dat.LOG")}
$LastProf = $LastProf | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1
$userId = $LastProf.DirectoryName.Replace("$ProfLoc","").Trim("\").ToUpper()
}
else{
$userId = "Unknown/UserID"
}
if ($userId -ne $NULL){
return "userId=" + $userId
}
elseif ($userID -eq $NULL)
{
$userId = "Unknown/UserID"
return "userId=" + $userId
}
Since this script will be invoked by a third party program that doesn't use elevated credentials, I had to create a second powershell script that includes the elevated privileges (which the third party program will invoke)
#elevated.ps1
[string]$abc = $args
<#Previously created password file in C:\Script\cred.txt, read-host -assecurestring | convertfrom-securestring | out-file C:\Script\cred.txt#>
$password = get-content C:\Script\cred.txt | convertto-securestring
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "DOMAIN\Username",$password
[string]$output = start-process powershell -Credential $credentials -ArgumentList '-noexit','-File', 'C:\script\script.ps1', $abc
return $output
And I can manually invoke elevated.ps1 with
.\elevated.ps1 "sender-ip=10.10.10.10"
Instead of having two scripts, i.e. one script with the start-process calling the other script, how to make this in one single script? I believe this would simplify the parameter passing because the third party program has to call elevate.ps1 which calls script.ps1 and some error is happening somewhere.
Windows does not allow a process to elevate while running. There are some tricks but in one way or another they will spawn a new process with the elevated rights. See here for more info. So the easiest way for PowerShell is still using one script to Start-Process the other script elevated.
Contrary to my man Dilbert, I say this can be done. Just use the builtin "$myInvocation.MyCommand.Definition" which is the secret variable to get the full path of a running script. Write a loop statement to verify if running as Admin, then if not, start-process with the $myIvocation.MyCommand.Definition as an argument.
Plop this at the top of your script. If in UAC mode you will get a confirmation prompt. Add your elevated scripts at the end where the Write-Host starts.
$WID=[System.Security.Principal.WindowsIdentity]::GetCurrent();
$WIP=new-object System.Security.Principal.WindowsPrincipal($WID);
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator;
If ($WIP.IsInRole($adminRole)){
}else {
$newProcess = new-object System.Diagnostics.ProcessStartInfo 'PowerShell';
$newProcess.Arguments = $myInvocation.MyCommand.Definition
$newProcess.Verb = 'runas'
[System.Diagnostics.Process]::Start($newProcess);Write-Host 'Prompting for Elevation'
exit
}
Write-Host 'ElevatedCodeRunsHere';
Write-Host 'Press any key to continue...'
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
Here are some more start process examples
I have a powershell script that parses a lotus notes INI file and replaces text inside the file. But only the replaced text is showing up in the output file.
# Example of PowerShell -replace parameter
## Get-DistinguishedName -- look up a DN from a user's (login) name
function Get-DistinguishedName {
Param($UserName)
$ads = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$ads.filter = "(&(objectClass=Person)(samAccountName=$UserName))"
$s = $ads.FindOne()
return $s.GetDirectoryEntry().DistinguishedName
}
clear-Host
set-executionpolicy remotesigned
$original_file = '.\notes.ini'
$destination_file = '.\notes2.ini'
$OS = Get-WmiObject -Class win32_OperatingSystem -namespace "root\CIMV2" -ComputerName .
$username = [Environment]::UserName
$userprofile = $env:userprofile
$fullname = Get-DistinguishedName($username) | %{$data = $_.split(","); $data[0].Substring(3)}
write-Host "Creating $userprofile"
if (($OS.Version -eq "5.1.2600") -or ($OS.Version -eq "5.2.3790")) {
$lookupTable = #{
'^SU_FILE_CLEANUP=' = 'SU_FILE_CLEANUP=' + $userprofile + '\Local Settongs\Application Data\smkits'
'%username%' = $username
'%fullname%' = $fullname
'%userprofile%' = $userprofile
'^Directory=' = 'Directory=' + $userprofile + '\Local Settongs\Application Data\Lotus\Notes\Data'}
} else {
$lookupTable = #{
'SU_FILE_CLEANUP=' = 'SU_FILE_CLEANUP=' + $userprofile + '\AppData\Roaming\smkits'
'%username%' = $username
'%fullname%' = $fullname
'%userprofile%' = $userprofile
'Directory=' = 'Directory=' + $userprofile + '\AppData\Local\Lotus\Notes\Data'}
}
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line -replace $_.Key, $_.Value
#break
}
}
write-Host $line
} | Set-Content -Path $destination_file
What am I missing
On this line, you are writing he output of the replace operator onto the pipeline, this will then get picked up by Set-Content
$line -replace $_.Key, $_.Value
whereas on this line, you are writing the output to the host (i.e. the powershell console) it will not end up on the pipeline and will not get picked up up Set-Content:
write-Host $line
To fix this, just replace write-host with write-output:
Write-Output $line