Quantcast
Channel: PowerShell.org » All Posts
Viewing all articles
Browse latest Browse all 13067

Reply To: Managing Hostnames vs. GUIDs on a Pull Server

$
0
0

I've been playing around a little with this issue, trying to keep track of both what configurations are tied to what GUIDs, and then which machines are configured to pull those GUIDs. I opted for a SQL database approach, and came up with a couple of scripts:

Function Publish-DSCConfiguration{
    Param (
        [Parameter(Mandatory=$True)]
        [ValidateScript({test-path $_ })]
        [System.IO.FileInfo]$Path,
 
        [String]$Description,
 
        [Parameter(Mandatory=$True)]
        [Guid]$Guid
    )
    Write-Verbose "Validating parameters"
    If (!($Path.name -like "*.mof")){
        Write-Verbose "Specified path not a MOF file.  Searching directory"
        $path = Get-ChildItem $path -filter *.mof
        if ($path.count -eq 0){
            Write-error "MOF file not found in specified Path" -ErrorAction Stop
        }
        Elseif ($path.count -gt 1){
            Write-error "Multiple MOF files found in specified Path.  Please specify correct file" -ErrorAction Stop
        }
        else {
            Write-Verbose "MOF file found at $path"
        }
    }
    $dest = "\\PULLSERVER\c`$\Program Files\WindowsPowerShell\DscService\Configuration\$guid.mof"
    if(!(Test-Path -path (split-path -Path $dest -Parent))){
        Write-Error "Cannot access destination directory $(split-path -path $dest.fullname -parent)" -erroraction Stop
    }
    Write-Verbose "Attempt database connection before updating files"
    Try{
        $SQLReadConn = New-Object System.Data.SqlClient.SqlConnection
        $SQLReadConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
        $SQLReadConn.Open()
        $SQLWriteConn = New-Object System.Data.SqlClient.SqlConnection
        $SQLWriteConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
        $SQLWriteConn.Open()
    } Catch{
        Write-Error "Unable to connect to database, halting script." -ErrorAction Stop
    }
    If (test-path $dest -ea SilentlyContinue){
        Write-Verbose "File exists, creating backup"
        Copy $dest "$(split-path $dest -parent)\Backup\$(Split-path $dest -Leaf)-$(get-date -UFormat "%m.%d.%y-%H.%M")" -Force -ErrorAction Stop
    }
    copy $Path $dest -Force -ErrorAction Stop
    New-DscCheckSum $dest -force -ErrorAction Stop
    #Once publishing is complete, write data to tracking database
    #If new config, write Configname ($path.name), GUID, creation date
    #If updated config, update/write configname ($Path.name), GUID, modify date
    $ConfigName = $((split-path $path -parent).split('\')[-1])
    $SQLReadCmd = New-Object System.Data.SqlClient.SqlCommand
    $SQLReadCmd.Connection = $SQLReadConn
    $SQLReadCmd.CommandText = "SELECT * FROM ConfigList WHERE Guid='$guid'"
    $result = $SQLReadCmd.ExecuteReader()
 
    If ($result.length -eq $Null){
        Write-Verbose "GUID not found in database, writing entry as new configuration"
        $SQLString = "INSERT INTO ConfigList (Guid,ConfigName,CreationDate,Description) VALUES('{0}','{1}','{2}','{3}')" -f $Guid,$ConfigName,$(get-date),$Description
    } Else {
        Write-Verbose "Entry found in database for GUID, updating record"
        $SQLString = "UPDATE ConfigList SET ConfigName = '$ConfigName', UpdateTime = '$(Get-date)', Description = '$Description' WHERE Guid = '$guid'"
    }
    $SQLWriteCmd = New-Object System.Data.SqlClient.SqlCommand
    $SQLWriteCmd.Connection = $SQLWriteConn
    $SQLWriteCMD.CommandText = $SQLString
    $SQLWriteCmd.executenonquery() | Out-Null
    $SQLReadConn.Close()
    $SQLWriteConn.Close()
}

I'll generate my mof like normal, then use this script to move it and rename it with a guid, then write the info into a sql database, recording GUID, creation date/time, last updated date/time, and the name of the configuration before it was moved. So for a new config the command might be this:

Publish-DSCConfiguration -Path C:\dsc\BaselineServer\localhost.mof -Guid $([GUID]::NewGuid()) -Verbose

Then once it is out on the pull server, I'll use this code to tell a server to pull that file:

Configuration SetPullMode{
    Param(
        [parameter(Mandatory=$True)]
        [string]$guid
    )
    LocalConfigurationManager{
        ConfigurationMode = "ApplyAndAutoCorrect"
        ConfigurationID=$guid
        RefreshMode='Pull'
        DownloadManagerName='WebDownloadManager'
        DownloadManagerCustomData=@{
            ServerUrl = 'https://PSDSCPullServerCert:8080/PSDSCPullServer.svc';
        }
    }
}
 
Function Set-DSCPullConfig{
    Param(
        [Parameter(Mandatory=$True,
                    ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True)]
        [Alias('Computername','Computer')]
        [String[]]$NodeName,
 
        [Parameter(Mandatory=$True)]
        [Guid]$Guid
    )
    Begin{
        write-verbose "Generating MOF"
        $MofPath = SPlit-path -path $(SetPullMode -guid $guid -OutputPath C:\DSC\SetPullMode) -Parent
        Write-Verbose "Testing database access"
        Try{
            $SQLReadConn = New-Object System.Data.SqlClient.SqlConnection
            $SQLReadConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
            $SQLReadConn.Open()
            $SQLReadConn.Close()
            $SQLWriteConn = New-Object System.Data.SqlClient.SqlConnection
            $SQLWriteConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
            $SQLWriteConn.Open()
            $SQLWriteConn.Close()
        } Catch{
            Write-Error "Unable to connect to database, halting script." -ErrorAction Stop
        }
 
    }
    Process{
        Foreach ($Computer in $NodeName){
            Write-Verbose "Pushing LCM config to $computer"
            Copy-Item "$mofpath\localhost.meta.mof" "$mofpath\$computer.meta.mof"
            Set-DscLocalConfigurationManager -ComputerName $Computer -path $MofPath -ErrorAction Stop
            Write-Verbose "Updating configuration database"
            $SQLReadConn = New-Object System.Data.SqlClient.SqlConnection
            $SQLReadConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
            $SQLReadConn.Open()
            $SQLWriteConn = New-Object System.Data.SqlClient.SqlConnection
            $SQLWriteConn.ConnectionString = "server=.\SQLExpress;database=DSCData;trusted_connection=true;"
            $SQLWriteConn.Open()
            $SQLReadCmd = New-Object System.Data.SqlClient.SqlCommand
            $SQLReadCmd.Connection = $SQLReadConn
            $SQLReadCmd.CommandText = "SELECT * FROM AssignList WHERE ComputerName='$Computer'"
            $result = $SQLReadCmd.ExecuteReader()
 
            If ($result.length -eq $Null){
                Write-Verbose "Computer $computer not found in database, writing entry as new configuration"
                $SQLString = "INSERT INTO AssignList (Guid,ComputerName,AssignDate) VALUES('{0}','{1}','{2}')" -f $Guid,$Computer,$(get-date)
            } Else {
                Write-Verbose "Entry found in database for $computer, updating record"
                $SQLString = "UPDATE AssignList SET Guid = '$guid', AssignDate = '$(Get-date)' WHERE ComputerName = '$Computer'"
            }
            $SQLWriteCmd = New-Object System.Data.SqlClient.SqlCommand
            $SQLWriteCmd.Connection = $SQLWriteConn
            $SQLWriteCMD.CommandText = $SQLString
            $SQLWriteCmd.executenonquery() | Out-Null
            Write-Verbose "Closing database connections"
            $SQLReadConn.Close()
            $SQLWriteConn.Close()
 
        }
        #After setting the configuration update a database table
        #include computername, assigned guid, date the config was pushed
    }
    End{
    }
}

I just parameterized the GUID portion of the LCM configuration so I can pass it whatever, then I generate the MOF, push it out to the server (a push to tell it to pull, still mixes me up a bit) then I write an entry in a second table that lists the server name, the assigned guid and the date it was set.

Not super elegant or anthing, and I still need to clean up the code a bit, document, etc. but so far it seems to work.


Viewing all articles
Browse latest Browse all 13067

Trending Articles