LUM.AudioCodes.psm1

Function New-AcManipulation{
    <#
    .SYNOPSIS
       Sets or updates a number manipulation
    .DESCRIPTION
       This script will create or update an SBC number manipulation on Audiocodes.
    .PARAMETER remoteHost
        The LAN IP address of the AudioCodes Mediant
    .PARAMETER type
        Type of manipulation;
            - a mask creates an outbound manipulations which changes the source number
            - a forward creates an inbound manipulation which changes the target number
    .PARAMETER name
        Name of the manipulation.
    .PARAMETER originalNumber
        Number to be manipulated
    .PARAMETER manipulatedNumber
        New Number
    .PARAMETER Credential
        Credentials for Audiocodes. Use "$x = Get-Credential" to store the credential in a variable. Then use "-Credential $x" to pass this to the script
    .PARAMETER Overwrite
        When specified any existing records with the same source number will be overwritten
    .PARAMETER Port
        Port of the telnet interface
    .PARAMETER WaitTime
        Wait in between of issuing telnet commands
    .EXAMPLE
       Set-AcManipulation -remotehost 10.0.0.12 -name fJohnsonMask -originalNumber +41441234567 -manipulatedNumber +41448888888
    .EXAMPLE
       $x = Get-Credential
       Set-AcManipulation -remotehost 10.0.0.12 -name fJohnsonMask -originalNumber +41441234567 -manipulatedNumber +41448888888 -credential $x
    .INPUTS
       Parameters
    .OUTPUTS
       Script results in output / verbose stream
    .NOTES
    +---------------------------------------------------------------------------------------------+
    | ORIGIN STORY |
    +---------------------------------------------------------------------------------------------+
    | DATE : 2018.07.02
    | AUTHOR : Michael L�rsen
    | E-Mail : michael.luersen@mondaycoffee.com
    | DESCRIPTION : First PSM1 Function
    +---------------------------------------------------------------------------------------------+
    #>

    Param(
    [CmdletBinding(
                  SupportsShouldProcess=$false, 
                  PositionalBinding=$false,
                  ConfirmImpact='Low')]
    [Parameter(Mandatory=$true)]
    [ipaddress]$remoteHost,
    [ValidateSet('forward','mask')][String]$type = 'mask',
    [Parameter(Mandatory=$true)]
    [string]$name,
    [Parameter(Mandatory=$true)]
    [ValidateLength(12,12)]
    [string]$originalNumber,
    [Parameter(Mandatory=$true)]
    [ValidateLength(12,12)]
    [string]$manipulatedNumber,
    [System.Management.Automation.CredentialAttribute()]$Credential,
    [switch]$overwrite,
    [string]$Port = "23",
    [int]$WaitTime = 500
    )

    if($null -eq $Credential){
        $credential = Get-Credential Admin -Message "Enter credentials for $remotehost"
    }

    Write-Output "### Configuring $remotehost ###"

    Write-Verbose "Attaching to the remote device, setup streaming requirements"
    $Socket = New-Object System.Net.Sockets.TcpClient($RemoteHost, $Port)
    If ($Socket)
    {   $Stream = $Socket.GetStream()
        $Writer = New-Object System.IO.StreamWriter($Stream)
        $Buffer = New-Object System.Byte[] 1024 
        $Encoding = New-Object System.Text.AsciiEncoding
        $global:Result = ""

         Function TelnetCmd{
            Param (
                [string]$cmd
            )
            Write-Verbose "issuing command: $cmd" 
            $Writer.WriteLine($cmd) 
            $Writer.Flush()
            Start-Sleep -Milliseconds $WaitTime
            While($Stream.DataAvailable) 
            {   $Read = $Stream.Read($Buffer, 0, 1024) 
                $global:result += ($Encoding.GetString($Buffer, 0, $Read))
            }
        }


        Write-Verbose "Now start issuing the commands: username"
        
       
        $Writer.WriteLine($credential.GetNetworkCredential().username) 
        $Writer.Flush()
        Start-Sleep -Milliseconds $WaitTime
        While($Stream.DataAvailable) 
        {   $Read = $Stream.Read($Buffer, 0, 1024) 
            $global:result += ($Encoding.GetString($Buffer, 0, $Read))
        }

        Write-Verbose "entering password"
        $Writer.WriteLine($credential.GetNetworkCredential().password) 
        $Writer.Flush()
        Start-Sleep -Milliseconds $WaitTime
        While($Stream.DataAvailable) 
        {   $Read = $Stream.Read($Buffer, 0, 1024) 
            $global:result += ($Encoding.GetString($Buffer, 0, $Read))
        }

        if($global:result -notlike "*>*"){
            write-host $global:result
            
            $Writer.WriteLine("quit") 
            $Writer.Flush()
            Write-Error "Error on enter password for $remotehost"
            Return 
        }

        Write-Verbose "enabling advanced options"
        TelnetCmd -cmd 'enable'
        
        Write-Verbose "entering Enable password"
        TelnetCmd -cmd 'Admin'
        Write-verbose "Configuring Voip menu"
        TelnetCmd -cmd 'configure voip'

        Write-Verbose "query for existing masks / forwards for this number"
        #find the users row
        if($type -eq 'mask'){
            TelnetCmd -cmd ('sbc manipulations ip-outbound-manipulation find-by src-user-name-prefix "' + $originalNumber + '"')

            if($global:result -match "(?smi)(ip-outbound-manipulation \d+)\n*.*?(src-user-name-prefix \D$originalNumber\D)"){
                $global:result -match '(ip-outbound-manipulation) \d+'
                $row = $matches[0].substring(25)
                $blnMatch = $true
            }
            
        }
        if($type -eq 'forward'){
            TelnetCmd -cmd ('sbc manipulations ip-inbound-manipulation find-by dst-user-name-prefix "' + $originalNumber + '"')

            if($global:result -match "(?smi)(ip-inbound-manipulation \d+)\n*.*?(dst-user-name-prefix \D$originalNumber\D)"){
                $global:result -match '(ip-inbound-manipulation) \d+'
                $row = $matches[0].substring(24)
                $blnMatch = $true
            }
        }

        # somehow the updating mask was kind of unreliable, testing now with a bit of wait time.
        # if this doesn't work try increasing wait or changing the updating mask cmd to 'sbc sbc manipu.....'
        # start-sleep -Seconds 3

        if($blnMatch -eq $true){
            if($overwrite -ne $true){
                Write-Warning "$name already exists (with index number $row). `nUse the parameter -overwrite to update this."
                break
            }
            #select the correct row
            if($type -eq 'mask'){
                Write-Verbose "Updating Mask"
                TelnetCmd -cmd (' sbc manipulations ip-outbound-manipulation "' + $row + '"')
            }
            if($type -eq 'forward'){
                Write-Verbose "Updating Forward"
                TelnetCmd -cmd (' sbc manipulations ip-inbound-manipulation "' + $row + '"')
            }
        }else{
            if($type -eq 'mask'){
                Write-Verbose "Creating Mask"
                TelnetCmd -cmd ' sbc manipulations ip-outbound-manipulation new'
            }
            if($type -eq 'forward'){
                Write-Verbose "Creating Forward"
                TelnetCmd -cmd ' sbc manipulations ip-inbound-manipulation new'
            }
        }

        if($type -eq 'mask'){
            TelnetCmd -cmd ('manipulation-name "' + $name + '"')
            TelnetCmd -cmd ('src-user-name-prefix "' + $originalNumber + '"')
            TelnetCmd -cmd 'manipulated-uri source'
        }
        if($type -eq 'forward'){
            TelnetCmd -cmd ('manipulation-name "' + $name + '"')
            TelnetCmd -cmd ('dst-user-name-prefix "' + $originalNumber + '"')
            TelnetCmd -cmd 'manipulated-uri destination'
        }

        TelnetCmd -cmd 'remove-from-left 12'
        #TelnetCmd -cmd 'remove-from-right 0'
        #TelnetCmd -cmd 'leave-from-right 255'
        TelnetCmd -cmd ('prefix-to-add "' + $manipulatedNumber + '"')
        TelnetCmd -cmd 'activate'
        TelnetCmd -cmd 'exit'
        TelnetCmd -cmd 'exit'
        TelnetCmd -cmd 'write'
        TelnetCmd -cmd 'quit'

    }Else{  
        Write-Verbose $global:result 
        Write-Error "Unable to connect to host: $($RemoteHost):$Port"
    }
    Write-Verbose $global:result 
    Write-Output "### Finished item $name ###"
}



Function New-AcManipulationCsv{
    <#
    .SYNOPSIS
       Sets or updates a number manipulation by CSV file
    .DESCRIPTION
       This script will create or update an SBC number manipulation on Audiocodes.
    .PARAMETER csvFile
        Full path to the CSV file to be used.
        csvFile contains headers and must look like this:
             
            remotehost,name,originalNumber,manipulatedNumber,type
            10.0.0.10,johnsonsmask,+41441234567,+41441234999,mask
            10.0.0.10,billyforward,+41441234567,+41441234999,forward
 
    .PARAMETER Credential
        Credentials for Audiocodes. Used stored credential from variable or -Credential (Get-Credential)
    .PARAMETER Overwrite
        When specified any existing records with the same source number will be overwritten
    .EXAMPLE
       Set-AcManipulation -remotehost 10.0.0.12 -name fJohnsonMask -originalNumber +41441234567 -manipulatedNumber +41448888888 -Overwrite
    .EXAMPLE
       $x = Get-Credential
       Set-AcManipulation -remotehost 10.0.0.12 -name fJohnsonMask -originalNumber +41441234567 -manipulatedNumber +41448888888 -credential $x
    .INPUTS
       Parameters
    .OUTPUTS
       Script results in output / verbose stream
    .NOTES
    +---------------------------------------------------------------------------------------------+
    | ORIGIN STORY |
    +---------------------------------------------------------------------------------------------+
    | DATE : 2018.07.02
    | AUTHOR : Michael L�rsen
    | E-Mail : michael.luersen@mondaycoffee.com
    | DESCRIPTION : First PSM1 Function
    +---------------------------------------------------------------------------------------------+
    #>

    Param(
    [CmdletBinding(
                  SupportsShouldProcess=$false, 
                  PositionalBinding=$false,
                  ConfirmImpact='Low')]
    [System.Management.Automation.CredentialAttribute()]$Credential,
    [string]$csvFile,
    [switch]$overwrite
    )

    if($null -eq $Credential){
        $credential = Get-Credential Admin -Message "Credentials for $remotehost"
    }

    $list = Import-Csv -Delimiter "," -Path $csvFile
    write-verbose "configuring $($list.count) items"
    foreach($item in $list){
        write-verbose "configuring $item"
        if($overwrite -eq $true){
            New-AcManipulation `
                -remotehost $item.remotehost `
                -name $item.name `
                -originalNumber $item.originalNumber `
                -manipulatedNumber $item.manipulatedNumber `
                -type $item.type `
                -credential $Credential `
                -overwrite
        }else{
            New-AcManipulation `
                -remotehost $item.remotehost `
                -name $item.name `
                -originalNumber $item.originalNumber `
                -manipulatedNumber $item.manipulatedNumber `
                -type $item.type `
                -credential $Credential
        }
    }
    write-output "### Finished configuring host(s) ###"
}



Function Backup-Ini
{   Param (
        [CmdletBinding(
                  SupportsShouldProcess=$false, 
                  PositionalBinding=$false,
                  ConfirmImpact='Low')]
        [Parameter(ValueFromPipeline=$true)]
        [string]$RemoteHost,
        [string]$BackupPath,
        [string]$Port = "23",
        [int]$WaitTime = 1000,
        [string]$Pwd
    )
 
    write-verbose "Attach to the remote device, setup streaming requirements"
    $Socket = New-Object System.Net.Sockets.TcpClient($RemoteHost, $Port)
    If ($Socket)
    {   $Stream = $Socket.GetStream()
        $Writer = New-Object System.IO.StreamWriter($Stream)
        $Buffer = New-Object System.Byte[] 1024
        $Encoding = New-Object System.Text.ASCIIEncoding
 
        write-verbose "Now start issuing the commands: username"
        $Result = ""
        $Writer.WriteLine("Admin") 
        $Writer.Flush()
        Start-Sleep -Milliseconds $WaitTime
        While($Stream.DataAvailable) 
        {   $Read = $Stream.Read($Buffer, 0, 1024) 
            $Result += ($Encoding.GetString($Buffer, 0, $Read))
        }
 
        write-verbose "enter password"
        $Writer.WriteLine($Pwd) 
        $Writer.Flush()
        Start-Sleep -Milliseconds $WaitTime
        While($Stream.DataAvailable) 
        {   $Read = $Stream.Read($Buffer, 0, 1024) 
            $Result += ($Encoding.GetString($Buffer, 0, $Read))
        }
        
        write-verbose "Retrieving INI file"
        $Writer.WriteLine("show ini-file") 
        $Writer.Flush()
        Start-Sleep -Milliseconds $WaitTime
        While($Stream.DataAvailable) 
        {   $Read = $Stream.Read($Buffer, 0, 1024) 
            $ini = -join ($Encoding.GetString($Buffer, 0, $Read))
            while($ini.substring($ini.length - 8) -notlike "*>*"){
                $Writer.WriteLine(" ") 
                $Writer.Flush()
                Start-Sleep -Milliseconds $WaitTime
                While($Stream.DataAvailable) 
                {   $Read = $Stream.Read($Buffer, 0, 1024) 
                    $ini += -join ($Encoding.GetString($Buffer, 0, $Read))
                }
            }
        }
 
    }Else{  
        write-error "Unable to connect to host: $($RemoteHost):$Port"
    }
 
    $Writer.WriteLine("quit") 
    $Writer.Flush()
 
    #modify this part with the text which appears in the ini file, if it fails to remove it.
    write-verbose "replacing --MORE-- xxxxx from strings"
    $ini = $ini.replace(" --MORE-- ","")
    $ini = $ini.replace("show ini-file","")
 
    $filename = (Get-Date -format "ddMMyyyy") + '_' + $RemoteHost + '.ini'
    if(!(test-path "$BackupPath\$remotehost")){
        new-item -path $BackupPath -Name $remotehost -ItemType directory
    }
    if(test-path "$BackupPath\$remotehost\$filename"){
        $filename = $filename.Substring(0,$filename.Length -4) + "_.ini"
    }
    $ini | Out-File -FilePath "$BackupPath\$remotehost\$filename" -Force -Encoding ascii
 
    write-verbose "Creating delta file in case of differences with last file"
    $files = Get-ChildItem -path $BackupPath\$remotehost -filter "*.ini"| Sort-Object LastWriteTime | select -last 2
    if($files.count -eq 2){
        $oldIni = Get-Content $files[1].FullName | %{$i = 1} { new-object psobject -prop @{Line=$i;Text=$_}; $i++}
        $newIni = Get-Content $files[0].FullName | %{$i = 1} { new-object psobject -prop @{Line=$i;Text=$_}; $i++}
        $difference = Compare-Object $oldIni $newIni -Property Text -PassThru |  Sort-Object -Property Line | fl
        if($difference.count -ne 0){
            write-verbose "Creating delta file as there are differences with last file"
            $difference | Out-File -FilePath "$BackupPath\$remotehost\$filename.changes.txt" -Force -Encoding ascii
        }else{
            write-verbose "removing the new ini file as it is the same as previous"
            Remove-Item "$BackupPath\$remotehost\$filename" -Force
        }
    }
}

Function Backup-AudioCodes{
    <#
    .SYNOPSIS
       Creates Backup of Audiocodes mediant
    .DESCRIPTION
       This cmdlet will export the ini file via a telnet session to the AudioCodes Mediant
    .PARAMETER Hosts
        IP Address or array of IP addresses from which to backup the INI files
    .PARAMETER BackupDir
        Directory where to write backup files as well as encrypted password strings for accessing the audiocodes.
    .EXAMPLE
       Backup-Audiocodes -hosts "10.0.0.10","10.0.0.12" -BackupDir D:\Backups\AudioCodes
    .INPUTS
       Parameters
    .OUTPUTS
       Key Directory
        Keys
       Host Directory
        Ini Files
        Delta Files
    .NOTES
    +---------------------------------------------------------------------------------------------+
    | ORIGIN STORY |
    +---------------------------------------------------------------------------------------------+
    | DATE : 2018.07.02
    | AUTHOR : Michael L�rsen
    | E-Mail : michael.luersen@mondaycoffee.com
    +---------------------------------------------------------------------------------------------+
    #>

    Param(
    [CmdletBinding(
                  SupportsShouldProcess=$false, 
                  PositionalBinding=$false,
                  ConfirmImpact='Low')]
    [Parameter(Mandatory=$true)]
    [string]$hosts,
    [Parameter(Mandatory=$true)]
    [string]$BackupDir
    )

    $BackupDir = $BackupDir.trim()

    if($BackupDir.Substring($BackupDir.Length -1) -ne "\"){
        $BackupDir = $BackupDir + '\'
    }
    if(!(test-path "$backupdir\Keys")){
        New-Item "$backupdir\Keys" -ItemType Directory | Out-Null
    }
    

    foreach($item in $hosts){
        $passwordfile = "$BackupDir\Keys\$($item)key.txt" 
        if(!(test-path $passwordfile)){
            [byte[]] $key = (1..16)   
            $credential = Get-Credential Admin -Message "Enter Credentials for $item"
            $credential.Password | ConvertFrom-SecureString -key $key | Set-Content $passwordfile
        }
 
        [byte[]] $key = (1..16)
        $securePassword = Get-Content $passwordfile | ConvertTo-SecureString -Key $key
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securepassword)
        $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
 
        Write-Output "### Backup up $item ###"
        if($verbose -eq $true){
            Backup-Ini -RemoteHost $item -BackupPath $BackupDir -Pwd $UnsecurePassword -Verbose
        }else{
            Backup-Ini -RemoteHost $item -BackupPath $BackupDir -Pwd $UnsecurePassword
        }
    }
 
    write-output "done" 
}