bin/Public/New-sqmAgentProxy.ps1

<#
.SYNOPSIS
    Erstellt einen SQL Server Credential und einen SQL Agent Proxy und verbindet beide.

.DESCRIPTION
    Legt in einem Schritt einen neuen SQL Server Credential an und erstellt darauf
    basierend einen SQL Server Agent Proxy. Der Proxy wird automatisch fuer die
    Subsysteme CmdExec, PowerShell und SSIS aktiviert.

    Ablauf:
      1. Pruefen ob Credential bereits existiert (Fehler oder -Force zum Ueberschreiben)
      2. Credential anlegen (CREATE CREDENTIAL) via SMO
      3. Pruefen ob Proxy bereits existiert
      4. Agent Proxy anlegen und mit dem Credential verbinden via SMO
      5. Subsysteme zuweisen: CmdExec (1), PowerShell (12), SSIS (11)
      6. Protokoll-Objekt zurueckgeben

.PARAMETER SqlInstance
    SQL Server-Instanz. Standard: lokaler Computername.

.PARAMETER SqlCredential
    PSCredential fuer die SQL-Verbindung (Windows-Auth wenn nicht angegeben).

.PARAMETER CredentialName
    Name des neuen SQL Server Credentials (z.B. "DOMAIN\ServiceAccount").

.PARAMETER ProxyName
    Name des neuen SQL Agent Proxys.

.PARAMETER ProxyDescription
    Optionale Beschreibung fuer den Proxy.

.PARAMETER WindowsCredential
    PSCredential mit Windows-Account (DOMAIN\User + Passwort) der im Credential
    hinterlegt wird. Pflichtparameter.

.PARAMETER Force
    Ueberschreibt bestehenden Credential und/oder Proxy wenn vorhanden.

.PARAMETER EnableException
    Ausnahmen sofort ausloesen statt Write-Error.

.EXAMPLE
    $winCred = Get-Credential "DOMAIN\SqlServiceAccount"
    New-sqmAgentProxy -SqlInstance "SQL01" -CredentialName "DOMAIN\SqlServiceAccount" `
        -ProxyName "SSIS Proxy" -WindowsCredential $winCred

.EXAMPLE
    # Mit Force - ueberschreibt bestehende Objekte
    $winCred = Get-Credential
    New-sqmAgentProxy -SqlInstance "SQL01\INST1" -CredentialName "DOMAIN\SvcSSIS" `
        -ProxyName "SSIS Execution Proxy" -ProxyDescription "Fuehrt SSIS-Pakete aus" `
        -WindowsCredential $winCred -Force

.NOTES
    Erfordert: SMO (Microsoft.SqlServer.Smo), Invoke-sqmLogging
    Benoetigt: sysadmin auf der Instanz
    Subsysteme: CmdExec (1), SSIS (11), PowerShell (12)
#>

function New-sqmAgentProxy
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(Mandatory = $false, Position = 0)]
        [string]$SqlInstance,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$SqlCredential,

        [Parameter(Mandatory = $true)]
        [string]$CredentialName,

        [Parameter(Mandatory = $true)]
        [string]$ProxyName,

        [Parameter(Mandatory = $false)]
        [string]$ProxyDescription = '',

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]$WindowsCredential,

        [Parameter(Mandatory = $false)]
        [switch]$Force,

        [Parameter(Mandatory = $false)]
        [switch]$EnableException
    )

    begin
    {
        $functionName = $MyInvocation.MyCommand.Name

        if (-not $PSBoundParameters.ContainsKey('SqlInstance') -or [string]::IsNullOrWhiteSpace($SqlInstance))
        {
            $SqlInstance = $env:COMPUTERNAME
        }

        # SMO Assembly laden
        try
        {
            [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Smo')
            [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SqlEnum')
        }
        catch
        {
            $msg = "SMO konnte nicht geladen werden: $($_.Exception.Message)"
            Invoke-sqmLogging -Message $msg -FunctionName $functionName -Level 'ERROR'
            if ($EnableException) { throw $msg }
            Write-Error $msg
            return
        }

        Invoke-sqmLogging -Message "Starte $functionName auf $SqlInstance" -FunctionName $functionName -Level 'INFO'
    }

    process
    {
        try
        {
            # ---------------------------------------------------------------
            # 1. SMO-Verbindung aufbauen
            # ---------------------------------------------------------------
            $serverConn = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlInstance)

            if ($SqlCredential)
            {
                $serverConn.ConnectionContext.LoginSecure = $false
                $serverConn.ConnectionContext.Login       = $SqlCredential.UserName
                $serverConn.ConnectionContext.SecurePassword = $SqlCredential.Password
            }

            # Verbindung testen
            $null = $serverConn.Databases.Count
            Invoke-sqmLogging -Message "SMO-Verbindung zu $SqlInstance hergestellt (Version: $($serverConn.VersionString))." -FunctionName $functionName -Level 'INFO'

            # ---------------------------------------------------------------
            # 2. Credential anlegen
            # ---------------------------------------------------------------
            $existingCred = $serverConn.Credentials | Where-Object { $_.Name -eq $CredentialName }

            if ($existingCred)
            {
                if ($Force)
                {
                    Invoke-sqmLogging -Message "Credential '$CredentialName' existiert bereits - wird mit -Force geloescht und neu angelegt." -FunctionName $functionName -Level 'WARNING'
                    $existingCred.Drop()
                }
                else
                {
                    throw "Credential '$CredentialName' existiert bereits auf '$SqlInstance'. Verwende -Force zum Ueberschreiben."
                }
            }

            if (-not $PSCmdlet.ShouldProcess($SqlInstance, "Credential '$CredentialName' anlegen"))
            {
                return $null
            }

            $credential = New-Object Microsoft.SqlServer.Management.Smo.Credential($serverConn, $CredentialName)
            $credential.Identity = $WindowsCredential.UserName

            # Passwort als SecureString uebergeben
            $credential.Create($WindowsCredential.Password)

            Invoke-sqmLogging -Message "Credential '$CredentialName' (Identity: $($WindowsCredential.UserName)) erfolgreich angelegt." -FunctionName $functionName -Level 'INFO'

            # ---------------------------------------------------------------
            # 3. Agent Proxy anlegen
            # ---------------------------------------------------------------
            $jobServer      = $serverConn.JobServer
            $existingProxy  = $jobServer.ProxyAccounts | Where-Object { $_.Name -eq $ProxyName }

            if ($existingProxy)
            {
                if ($Force)
                {
                    Invoke-sqmLogging -Message "Proxy '$ProxyName' existiert bereits - wird mit -Force geloescht." -FunctionName $functionName -Level 'WARNING'
                    $existingProxy.Drop()
                }
                else
                {
                    throw "Proxy '$ProxyName' existiert bereits auf '$SqlInstance'. Verwende -Force zum Ueberschreiben."
                }
            }

            if (-not $PSCmdlet.ShouldProcess($SqlInstance, "Agent Proxy '$ProxyName' anlegen"))
            {
                return $null
            }

            $proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount(
                $jobServer,
                $ProxyName,
                $CredentialName,
                $true,   # IsEnabled
                $ProxyDescription
            )

            $proxy.Create()
            Invoke-sqmLogging -Message "Agent Proxy '$ProxyName' mit Credential '$CredentialName' angelegt." -FunctionName $functionName -Level 'INFO'

            # ---------------------------------------------------------------
            # 4. Subsysteme zuweisen
            # ---------------------------------------------------------------
            # AgentSubSystem Enum-Werte:
            # CmdExec = 1
            # Ssis = 11
            # PowerShell = 12
            $subsystems = @(
                [Microsoft.SqlServer.Management.Smo.Agent.AgentSubSystem]::CmdExec,
                [Microsoft.SqlServer.Management.Smo.Agent.AgentSubSystem]::Ssis,
                [Microsoft.SqlServer.Management.Smo.Agent.AgentSubSystem]::PowerShell
            )

            $assignedSubsystems = [System.Collections.Generic.List[string]]::new()

            foreach ($subsystem in $subsystems)
            {
                $proxy.AddSubSystem($subsystem)
                $assignedSubsystems.Add($subsystem.ToString())
                Invoke-sqmLogging -Message "Subsystem '$subsystem' dem Proxy '$ProxyName' zugewiesen." -FunctionName $functionName -Level 'INFO'
            }

            # ---------------------------------------------------------------
            # 5. Ergebnis
            # ---------------------------------------------------------------
            $result = [PSCustomObject]@{
                SqlInstance          = $SqlInstance
                CredentialName       = $CredentialName
                CredentialIdentity   = $WindowsCredential.UserName
                ProxyName            = $ProxyName
                ProxyDescription     = $ProxyDescription
                AssignedSubsystems   = $assignedSubsystems -join ', '
                IsEnabled            = $true
                Success              = $true
            }

            Write-Host ""
            Write-Host " Agent Proxy erfolgreich erstellt" -ForegroundColor Green
            Write-Host " --------------------------------" -ForegroundColor DarkGray
            Write-Host " Instanz : $SqlInstance"       -ForegroundColor Cyan
            Write-Host " Credential : $CredentialName"    -ForegroundColor Cyan
            Write-Host " Identity : $($WindowsCredential.UserName)" -ForegroundColor Cyan
            Write-Host " Proxy : $ProxyName"         -ForegroundColor Cyan
            Write-Host " Subsysteme : $($assignedSubsystems -join ', ')" -ForegroundColor Cyan
            Write-Host ""

            return $result
        }
        catch
        {
            $errMsg = "Fehler in $functionName`: $($_.Exception.Message)"
            Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level 'ERROR'
            if ($EnableException) { throw }
            Write-Error $errMsg
            return [PSCustomObject]@{ Success = $false; ErrorMessage = $errMsg }
        }
    }

    end
    {
        Invoke-sqmLogging -Message "$functionName abgeschlossen." -FunctionName $functionName -Level 'INFO'
    }
}