Monday, April 6, 2020

SQL Agent Job Generator: Building the Base Rig and Managing Categories

In the previous entry in this series, I implemented Environment Variables that will let me have one code base that can easily generate jobs in DEV, PROD, or whatever environment you desire. In this entry, we are going to implement the basic building blocks of the process. I will build:

  1. A file with defaults for my configuration
  2. The file to launch the job creation process
  3. A function to create a job, which will call multiple other functions
  4. A function to check for the existence of a job category, and add it if it is new

The code, as it stands in this blog, can be downloaded here: https://github.com/drsqlgithub/SSISDeployTool/tree/edbe4c2b13ea77d1a3ac3e4a32ee0561d3e732fd

The file for default configuration details will be named LIBRARY_Defaults.ps1, and will start out containing:

#When no category is included on the job, use this one
$Global:G_DefaultJobCategory = "ManagedAgentJob"

#display lots of Write-Host messages to help the user see progress
$global:G_VerboseDetail = $True;

##############################################
#Location of code used in processing
##############################################

#The template of the shapes used for graphing
$Global:G_VisioTempate = "$BaseDirectory\VisioShapes\SSISDeployShapes.vssx"

#The location of SMO being used. 
$Global:G_Smo = "C:\Program Files\Microsoft SQL Server\150\Setup Bootstrap\SQL2019\x64\Microsoft.SqlServer.Smo.dll"

You will notice that the Visio template uses back in the first entry on the series on controlling Visio to diagram the jobs is in here, the verbose output control, along with two new items we need for our new task. One to make sure that SMO is configured (this being the one from SQL Server 2019 install, while at work I am using 2016 for my SSIS server.) The other to define the default Job Category that we will use if one is not defined explicitly by the job.

The next file is TOOL_JobsDeploy_BASE.ps1. It is the file that has the base details for launching the creation process. The first line will actually be moved out to become a parameter later, but it is much easier to just do this as one file now for building purposes.

$Environment = 'DEV' #later, this will be moved to a file named
                     #TOOL_JobsDeploy_DEV.ps1 and Environment will be a param
$BaseDirectory = Get-Location #This script should be portable, so I added the path
                              #dynamically
                              
#get the function scripts
. $BaseDirectory\LibraryFiles\LIBRARY_Defaults.ps1
. $BaseDirectory\LibraryFiles\LIBRARY_Functions.ps1
. $BaseDirectory\LibraryFiles\LIBRARY_Variables_$Environment.ps1
. $BaseDirectory\LibraryFiles\LIBRARY_Variables_Global.ps1

#get the json documents that you will work with
#defines the jobs we have
$DefinitionItemFile = "$BaseDirectory\JOBS_Definition.json"

#defines the dependencies between jobs
$DependencyItemFile = "$BaseDirectory\JOBS_Dependencies.json" 

#defines the schedules to run jobs
$ScheduleItemFile = "$BaseDirectory\JOBS_Schedules.json"

#Make sure SMO is in path
Add-Type -Path "$G_Smo"

#Get the servername from the variable
$ServerName = environmentvariable_decode('SSISServer');

#call the function 
agent_CreateJobsFromJson -P_ServerName $ServerName -P_DefinitionJsonFile $DefinitionItemFile `
                -P_DependencyJsonFile $DependencyItemFile -P_ScheduleJsonFile $ScheduleItemFile

Now, to the LIBRARY_Functions.ps1 file, I am adding the agent_CreateJobsFromJson function that I will be growing over time. It is used to loop through the Json files and generate the details that have been defined for our jobs. You can see the basic design in the first entry here, but now we are going to extend the JOBS_Definition.Json file by allowing you to define an addition, optional element:

{  
   "Jobs": [
     {
       "SystemName": "SalesProcessing",
       "SubsystemName": "_MasterSalesProcessing",
       "EnvironmentName": "GeneralOperation",
       “JobCategory”: “CategoryName” <<-- (And no, you can't put comments in Json :))
      },

If the CategoryName is not present, we will use the value defined in $Global:G_DefaultJobCategory in the LIBRARY_Defaults.ps1 file. Note that I have shimmed in the dependency and schedule parts of the code for future steps so we can skip over these details in the future.

function agent_CreateJobsFromJson ($P_ServerName,`
        $P_DefinitionJsonFile, $P_DependencyJsonFile, `
        $P_ScheduleJsonFile){

    #Open the JSON files
    $DefinitionItems = Get-Content $P_DefinitionJsonFile | ConvertFrom-Json 
    $ScheduleItems = Get-Content $P_ScheduleJsonFile | ConvertFrom-Json 
    $DependencyItems = Get-Content $P_DependencyJsonFile | ConvertFrom-Json 

    #Loop through the nodes items, and create a node on the diagram
    if ($G_VerboseDetail) {
        Write-Host "Creating Jobs"
    }
    $itemsI = $DefinitionItems.Jobs.Count
        
    for ($i = 0; $i -lt $itemsI ; $i++) {
        #fetch the three name parts (if your folder and project names differ,
        #     you can easily add that)
        #$L1_SystemName = $DefinitionItems.Jobs[$i].SystemName
        #$L1_SubsystemName = $DefinitionItems.Jobs[$i].SubsystemName
        #$L1_EnvironmentName = $DefinitionItems.Jobs[$i].EnvironmentName
        $L1_JobCategory = $DefinitionItems.Jobs[$i].JobCategory
        #if JobCategory is not included, use the default
        if (!$L1_JobCategory){
            $L1_JobCategory = $G_DefaultJobCategory;
        }
        if ($G_VerboseDetail) {
            Write-Host "Handling Job Category: [$L1_JobCategory]"
        }
        #check for existence/create category
        agent_maintainCategory -P_AgentServerName $P_ServerName `
                               -P_CategoryName $L1_JobCategory
    }
    
    $itemsI = $DependencyItems.JobDependency.Count
        
    for ($i = 0; $i -lt $itemsI ; $i++) {
        #fetch the three name parts (if your folder and project 
        #                         names differ, you can easily add that)
        #$L2_SystemName = $DependencyItems.JobDependency[$i].SystemName
        #$L2_SubsystemName = $DependencyItems.JobDependency[$i].SubsystemName
        #$L2_EnvironmentName = $DependencyItems.JobDependency[$i].EnvironmentName
        
    }
    $itemsI = $ScheduleItems.JobSchedule.Count
        
    for ($i = 0; $i -lt $itemsI ; $i++) {
        #fetch the three name parts (if your folder and project 
        #              names differ, you can easily add that)
        #$L2_SystemName = $ScheduleItems.JobSchedule[$i].SystemName
        #$L2_SubsystemName = $ScheduleItems.JobSchedule[$i].SubsystemName
        #$L2_EnvironmentName = $ScheduleItems.JobSchedule[$i].EnvironmentName
    }
}

Finally, the function (agent_maintainCategory) to add the category that was passed in. Simple enough function, though it took me multiple hours to figure out. There is a JobServer.JobCategories collection, and it seems like you can add an item. But none of the Alter methods would make it stick. But just creating a category actually adds it to the JobCategories collection too, but in a permanent manner.

The lack of documentation/examples of all of these objects that you need to use can get really frustrating, but it is really fun when you actually find the way to make it happen!

function agent_maintainCategory ($P_AgentServerName, $P_CategoryName) {
    Try {
        #Connect to the SQL Server, you will need to be using a trusted connection here.            
        $ssisServer = New-Object -TypeName  Microsoft.SQLServer.Management.Smo.Server("$P_AgentServerName") 
        
        #variable for the jobserver
        $JobServer = $ssisServer.JobServer
        #grab the job category by name that was passed in
        $Category = $JobServer.JobCategories["$P_CategoryName"] 
        #if it wasn't found, add it
        if (!$Category) {
            #create the new category
            $NewCategory = New-Object ('Microsoft.SqlServer.Management.Smo.Agent.JobCategory')`
                                                     ($JobServer, "$P_CategoryName")
            #This was really hard for me. There is a JobCategories collection too... But you add
            #the new Category here.
            $NewCategory.Create()
        
            if ($G_VerboseDetail) {
                Write-Host "Added category name: $NewCategory"
            }
        }
    }      
    catch {
        Write-Error $_
        Write-Host "Something failed handling the category $P_CategoryName"
        Throw;
    }
}

In the next blog, we will start to generate actual basic T-SQL SQL Agent jobs.

The post SQL Agent Job Generator: Building the Base Rig and Managing Categories appeared first on Simple Talk.



from Simple Talk https://ift.tt/2XfHQQ3
via

No comments:

Post a Comment