среда, 17 марта 2021 г.

VsDesk на nginx

 Ставим убунту 16 по инструкции, ставим fcgiwrap, nginx, php7.0-fpm, ставим все зависимости, кроме апача.

apt install -y htop mc wget sudo iptables nano ncdu dnsutils net-tools psmisc curl nginx php-fpm php-gd php-imap php-ldap php-imagick php-mbstring php-mcrypt php-curl php-mysqli php-zip php-xml zip mysql-server php-ldap fcgiwrap


Распаковываем дистр в каталог /var/www/vsdesk

копируем php.ini в /etc/php/7.0/fpm, копируем модуль ioncube в каталог /usr/lib/php, копируем mysqld.cnf в /etc/mysql/mysql.conf.d

Можно вообще ничего не исправлять, кроме имени домена. Ниже конфиг для nginx

nano /etc/nginx/sites-enabled/default


server { listen 80 default_server;

server_name vsdesk vsdesk.yourdomain.com;

rewrite ^ https://$host$request_uri permanent; }

server { listen 443 ssl http2 default_server; ssl_certificate /etc/nginx/ssl/public.crt; ssl_certificate_key /etc/nginx/ssl/private.key; root /var/www/vsdesk; index index.php;

server_name vsdesk vsdesk.yourdomain.com;

location ~\.(css|js|png|ttf|woff|ico|woff2|jpg|pdf|tif|tiff|jpeg|bmp)$ { try_files $uri /index.php; }

location / { rewrite / /index.php?$request_uri; }

location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php7.0-fpm.sock; }

location /cgi-bin/ { gzip off; add_header 'X-Accel-Buffering' 'no' always; root /usr/lib; fastcgi_pass  unix:/run/fcgiwrap.socket; include /etc/nginx/fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib$fastcgi_script_name; }

}
если не будет работать (404 ошибка), то можно сделать:
chmod -R 777 /var/www/vsdesk && chown -R root:root /var/www/vsdesk

Секьюрность, HSTS и прочие свистелки/перделки добавите по вкусу. Задача была просто запустить vsdesk на nginx без регистрации и смс.

понедельник, 18 января 2021 г.

Проверим в домене активацию Windows и Office

Скрипт бежит по домену, подключается к компьютерам, считывает информацию о лицензировании, выводит в консоль в формате CSV

Наздоровье

[code]strComputer=""

strFilter=""

strQuery=""



On Error Resume Next

Set objDomain=Getobject("WinNT://DOMAIN")


For Each object In objDomain

If object.Class="Computer" Then

strComputer=object.Name

strWin=GetWindowsStatus

strOff=GetOfficeStatus

Wscript.echo strComputer & ";" & strWin & ";" & strOff

End If

Next

Function GetActivationStatus

On Error Resume Next

On Error Goto 0

Err.Clear

Status="Not found"

Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

If Err.Number=0 Then

i=0

Set col = objWMI.ExecQuery(strQuery)

Status="Not activated"

For Each obj in col

i=i+1

If obj.LicenseStatus=1 Then Status="Activated": Exit For

Next

If i=0 Then Status="Not Found"

End If

GetActivationStatus=Status

End Function


Function GetWindowsStatus

strFilter="Windows"

strQuery="SELECT LicenseStatus FROM SoftwareLicensingProduct WHERE Description like '%" & strFilter & "%'"

strGWS=GetActivationStatus

GetWindowsStatus=strGWS

End Function

Function GetOfficeStatus

strFilter="Office"

strQuery="SELECT LicenseStatus FROM SoftwareLicensingProduct WHERE Description like '%" & strFilter & "%'"

strGOS=GetActivationStatus

GetOfficeStatus=strGOS

End Function

[/code]

понедельник, 29 июня 2020 г.

Проверяем актуальность версии СБИС оффлайн

Так получилось, что мне досталась внагрузку СБИС оффлайновая версии 2.4
Ну и приходится мониторить актуальность версии и регулярно ее обновлять.
Можно, конечно, каждое утро заходить на сайт СБИС в специальный раздел, но после трех раз эту задачу уже следует автоматизировать.
Вот Вам код сравнения актуальности Вашей СБИС и на сайте.
При необходимости корректируйте под Ваши нужды.
Для отправки почты используем старый добрый blat.
Сохраняем в sbis_ver_check.vbs, в эту же папку кладем blat, запускаем планировщиком или любым другим удобным способом.
На здоровье.

[code]On Error Resume Next

strNew=SbisNewVersion
strCur=SbisCurrentVersion

If strNew>strCur Then
'Wscript.Echo "Есть новая версия СБИС"
SendMail
'ElseIf strNew<strCur Then
' Wscript.Echo "Бред. У Вас версия новее, чем на сайте"
'Else
' Wscript.Echo "У Вас актуальная версия СБИС"
End If


Function SbisNewVersion
On Error Resume Next
strVersionCheck="https://sbis.ru/download?tab=ereport&innerTab=history"
a=GetText(strVersionCheck)
arr=Split(a,"<h2 class=" & chr(34) & "sbis_ru-header-h3" & chr(34) & ">")
If ubound(arr)<1 Then Wscript.Quit
b=arr(1)
i=Instr(1,b,"</h2>")
If i=0 Then Wscript.Quit
c=Ltrim(rtrim(Mid(b,1,i-1)))
If c="" Then Wscript.Quit
arrVer=Split(c," ")
strVer=arrVer(1)
SbisNewVersion=strVer
End Function

Function SbisCurrentVersion
On Error Resume Next
Set objFSO=Createobject("Scripting.Filesystemobject")
tmpVer=objFSO.GetFileVersion ("\\sbisserver\sbissharename\sbis.exe")
arrTmp=Split(tmpVer,".")
For j=0 to 2
Ver=Ver & arrTmp(j) & "."
Next
If Right(Ver,1)="." Then Ver=Left(Ver,Len(Ver)-1)
SbisCurrentVersion=Ver
End Function

Function GetText(strURL)
iTimeout=10
On Error Resume Next
GetText = ""
Set objXML = CreateObject("MSXML2.XMLHTTP")
objXML.Open "GET", strURL, False
objXML.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0"
objXML.Send
x = Timer
Do
If Timer - x >= iTimeOut Then Exit Do
If objXML.ReadyState = 4 Then
If objXML.Status = 200 Then Exit Do
End If
Loop
strResult = objXML.ResponseText
GetText = strResult
End Function
Function FindText(strWhere, strFindStart, strFindEnd)
'
' Выделить кусок текста между выражениями strFindStart и strFindEnd в предоставленном тексте strWhere
'
On Error Resume Next
FindText=""
iFind=Instr(1,strWhere,strFindStart)
If iFind=0 Then Exit Function
iFindEnd=Instr(iFind +Len(strFindStart),strWhere,strFindEnd)
If iFindEnd<=iFind Then Exit Function
FindText=Mid(strWhere,iFind+len(strFindStart),iFindEnd-(iFind+len(strFindStart)))
End Function
Function GetPath
On Error Resume Next
arrPath=Split(Wscript.ScriptFullName,"\")
strPath=""
For i=0 to ubound(arrPath)-1
strPath=strPath & arrPath(i) & "\"
Next
strPath=Left(strPath,Len(strPath)-1)
GetPath=strPath
End Function
Sub SendMail
On Error Resume Next
Set objShell=Createobject("Wscript.Shell")
Set objFSO=Createobject("Scripting.Filesystemobject")
Set objFile=objFSO.OpenTextFile (GetPath & "\send.txt",2, True)
objFile.WriteLine "Добрый день." & vbcrlf & vbcrlf & "Ваша версия СБИС: " & strCur & vbcrlf & "Версия СБИС на сайте: " & strNew & vbcrlf & "Запланируйте обновление"
objFile.Close
strCmd=GetPath & "\blat.exe " & GetPath & "\send.txt -to recipient@domain.com -f sender@domain.com -u authsender@domain.com -pw P@s$W0rD -server mailserver.domain.com -charset windows-1251 -s " & chr(34) & "Новая версия СБИС" & chr(34)
objShell.Run strCmd,1,True
End Sub
[/code]

четверг, 15 августа 2019 г.

Нужно быстро на всех включенных компьютерах включить учетку администратора (гарантированно) и поменять пароль.

Понятно, что Вы можете включить учетку администратора с помощью групповых политик, но поменять пароль на учетку Вы не сможете (раньше можно было, да).
И да, LAPS мне не подходит.

Зато подходит такой вот скрипт:
Если хотите - можете часть функций обрезать и запихнуть в автозагрузку от лица машины.

[code]
Set objDomain=Getobject("WinNT://DOMAIN")
For Each object in objDomain
If object.Class="Computer" Then
If Ping(object.Name)=1 Then
print CheckAdmin(object.Name)
End If
End If
Next


Function Ping(strComputer)
Ping=0
On Error Resume Next
Err.Clear
Set objWMI = GetObject("winmgmts:\\.\ROOT\cimv2")
strQuery="select * from Win32_PingStatus Where address='" & strComputer & "'"
Set col = objWMI.ExecQuery(strQuery)
For Each obj in col
If obj.StatusCode=0 Then Ping=1
Next
' If Ping=1 Then print strComputer & " have ping" Else print strComputer & " have no ping"
End Function


Function CheckAdmin(strComputer)
On Error Resume Next
CheckAdmin=""
Set objComputer=Getobject("WinNT://" & strComputer)
For Each obj In objComputer
If lcase(obj.Class)="group" Then
If lcase(obj.Name)="администраторы" or lcase(obj.Name)="administrators" Then
Set objGroup=Getobject("WinNT://" & strComputer & "/" & obj.Name)
For Each objGroupMember in objGroup.Members
If Lcase(objGroupMember.Name)="администратор" Or LCase(objGroupMember.Name)="administrator" Then
Enable strComputer,objGroupMember.Name
Set objUser=Getobject("WinNT://" & strComputer & "/" & objGroupMember.Name)
bState=objUser.AccountDisabled
If bState=True Then
CheckAdmin=strComputer & "|" & objGroupMember.Name & "|disabled"
else
CheckAdmin=strComputer & "|" & objGroupMember.Name & "|enabled"
End If
End If
Next
End If
End If
Next
End Function
Sub print(strWhat)
On Error Resume Next
Wscript.Echo strWhat
End Sub
Sub Enable(strComputer,strUser)
Set objShell=Createobject("Wscript.Shell")
objShell.Run "c:\psutils\psexec.exe \\" & strComputer & " net user " & strUser & " Str0nGP@$$w)rD",0,True
Set objAdm=Getobject("WinNT://" & strComputer & "/" & strUser)
objAdm.AccountDisabled=False
objAdm.SetInfo
objShell.Run "c:\psutils\psexec.exe \\" & strComputer & " /s gpupdate /force /target:computer",0,False
End Sub
[/code]

понедельник, 29 июля 2019 г.

Хочу прописывать базы 1с через членство в AD

Копипастим скрипт, создаем в AD группы с именем 1cbase_bla-bla-bla
Добавляем туда пользователей.
В описание (поле info) пишем информацию о подключении к БД (никаких русских буков):
[DBCaption]
Connect=Srvr="1сservername";Ref="dbname";
OrderInList=-1
Folder=/
OrderInTree=-1
External=0
ClientConnectionSpeed=Normal
App=Auto
WA=1

Скрипт  добавляем на вход пользователя и все - радуемся.
Если пользователь не в группе - ibases.v8i не будет изменен.

Все! Радуемся, пьем пиво и никуда больше не бегаем.

[code]
On Error Resume Next
Set objNet=Createobject("Wscript.Network")
Set objUser=Getobject("WinNT://" & objNet.UserDomain & "/" & objNet.UserName)
Set objFSO=Createobject("Scripting.Filesystemobject")
Set objShell=Createobject("Wscript.Shell")
strUP=objShell.ExpandEnvironmentStrings("%USERPROFILE%")
strFile=strUP & "\AppData\Roaming\1c\1cestart\ibases.v8i"
arrGroup=Split(GroupList,"|")
For i=0 to Ubound(arrGroup)
If IsMember(arrGroup(i),objUser.Name)=True Then
strBL=strBL & GetBaseInfo(arrGroup(i)) & vbcrlf
End If
Next
If strBL<>"" Then
Set objFile=objFSO.OpenTextFile(strFile,2,True)
objFile.Write strBL
objFile.Close
Set objFile=Nothing
End If
Set objShell=Nothing
Set objFSO=Nothing
Set objUser=Nothing
Set objNet=Nothing
Wscript.Quit


Function GetBaseInfo(strGroup)
On Error Resume Next
GetBaseInfo = GetLDAPValue("info", "cn ='" & strGroup & "'")
End Function


Function GetLDAPValue(strValue, strFilter)
On Error Resume Next
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
strQuery = "SELECT " & strValue & " FROM 'LDAP://" & GetLDAPDomain & "' WHERE " & strFilter
objCommand.CommandText = strQuery
Set objRecordSet = objCommand.Execute
Do Until objRecordSet.EOF
strVar = strVar & objRecordSet.Fields(strValue).Value & "|"
objRecordSet.MoveNext
Loop
If Len(strVar) > 1 Then strVar = Left(strVar, Len(strVar) - 1)
GetLDAPValue = strVar
End Function

Function GetLDAPDomain()
    Set iAdRootDSE = GetObject("LDAP://RootDSE")
    GetLDAPDomain = iAdRootDSE.Get("defaultNamingContext")
End Function

Function GroupList
On Error Resume Next
strGrouplist=""
Set objDomain=Getobject("WinNT://" & objNet.UserDomain)
For Each object in objDomain
If object.Class="Group" Then
If Left(Lcase(object.Name),7)="1cbase_" Then
strGroupList=strGroupList & object.name & "|"
End If
End If
Next
If Right(strGroupList,1)="|" Then strGroupList=Left(strGroupList,Len(strGroupList)-1)
GroupList=strGroupList
End Function
Function IsMember(strGroup,strUser)
On Error Resume Next
IsMember=False
Set objGroup=Getobject("WinNT://" & objNet.UserDomain & "/" & strGroup)
For Each objMember In objGroup.Members
If Lcase(objMember.Name)=Lcase(strUser) Then IsMember=True
Next
End Function
[/code]

пятница, 31 мая 2019 г.

Хочу на FreePBX динамические конференции с одноразовым паролем

Имеем: FreePBX, Exchange.
Задача: хочу динамические конференции, чтобы постоянно пароль менялся сам на произвольный и чтобы оповещение участников по почте приходило.

yum update -y && yum install epel-release -y && yum install ssmtp -y

Далее пишем файлы:
/var/www/html/conf.php:
<html>
<meta><title>It's easy to create new conference</title></meta>
<body>
<form method='POST' action='/makeconf.php'>
Please, enter emails of conference participants:<br>
<textarea name='emails' cols=80 rows=10>
username@domain.com
</textarea><br>
Enter here any additional text to include in email message:<br>
<textarea name='msg' cols=80 rows=10>
</textarea><br>
<input type='submit' value='Create'><br>
</form>
<br><br><br>
<font color=red><center><h3>WARNING!!!</h3>Conferences is valid only in today!!!<br>This conference will be automatically removed in 00:00</center></font>
</body>
</html>

/var/www/html/makeconf.php:
<?php
#
# Read values from conf.php
#
$emails=$_POST['emails'];
$msg=$_POST['msg'];
if (empty($emails)) {
echo "No emails were sent";
exit();
}
if ($email="") { echo "No emails were sent"; exit (); }
#
# Set default timezone
#
date_default_timezone_set('Asia/Yekaterinburg');
#
# Set database connection variables
#
$dbuser="freepbxuser";
$dbpass="5f2ecaef00a5bda7e7f16f00c616e484";
$dbname="asterisk";
#
# Trying to connect
#
echo "Connecting DB...";
$conn=mysqli_connect('localhost',$dbuser,$dbpass,$dbname);
if (!$conn) {
        die('Error connecting MySQL DB');
        exit();
} else {
        echo "OK<br>";
}
#
# Get MAX of extension number
#
echo "Get new conference number...";
$res=mysqli_query($conn,"SELECT MAX(exten) FROM meetme;");
if ( $res ) {
        echo "OK<br>";
} else {
        echo "Error";
        exit();
}
$row=mysqli_fetch_row($res);
#
# +1
#
$conf_num=$row[0];
$conf_num++;
#$res=mysqli_close($conn);
#
# Show conference number to user
#
echo "Conference Number: " .$conf_num ."<br>";
#
# Generate random PIN
#
echo "Generate new PIN...";
$newpin1=mt_rand(1000,9999);
$newpin=$newpin1;
echo $newpin."<br>";
#
# Convert current date/time to YYYY-MM-DD format
#
$date=date('Y-m-d');
#
# And insert it to the database
#
echo "Inserting values to MySQL DB for new conference...";

$query="insert into meetme (exten,options,userpin,description,timeout,adminpin,music) values (".$conf_num.",\"oTc\",".$newpin.",\"".$date."\",21600,\"\",\"inherit\");";
$res=mysqli_query($conn,$query);
#echo $query ."<br>";
if ( $res == TRUE ) {
        echo "OK<br>";
} else {
        echo "Error<br>";
        exit();
}
exec("asterisk -rx 'database put CONFERENCE/".$conf_num." userpin ".$newpin."'");
exec('mysqladmin refresh');
exec("fwconsole reload");
$pin=$newpin;
#
# Prepare email message
#
$message="Hello\nYou are invited to join conference ".$conf_num." with PIN:".$pin."\n\n".$msg;
$email=explode("\n",$emails);
$z=0;
#
# And send the emails
#
echo "Sending emails...<br>";
while ($email[$z]<>"") {
echo $email[$z]."...";
$res=mail($email[$z],"Conference invitation",$message);
if ( $res == TRUE ) {
        echo "OK<br>";
} else {
        echo "Error<br>";
}
$z++;
}
echo "Done<br>";
#
# Reload FreePBX configuration
#
echo "Reloading configuration of FreePBX<br>";
exec("fwconsole reload");
?>
<a href="/conf.php">Create another conference</a>


/var/www/html/remove_old_conf.php:
<?php
#
# This will remove all conferences by filter
# We are writing current date to the description field in makeconf.php
# So, if date from conference is lower than current date - we should remove it
#

#
# Set default timezone
#
date_default_timezone_set('Asia/Yekaterinburg');
#
# Database connection variables
#
$dbuser="freepbxuser";
$dbpass="freepbxuser_mysql_password";
$dbname="asterisk";
#
# And connecting
#
$conn=mysqli_connect('localhost',$dbuser,$dbpass,$dbname);
if (!$conn) {
        die('Error connecting MySQL DB');
        exit();
}
#
# Get conf numbers and descriptions
# If you have static conferences you won't remove - you should exclude it here or below or some other way
#
$res=mysqli_query($conn, "SELECT description,exten from meetme");
#
# Convert current date/time to YYYY-MM-DD format
#
$date=date('Y-m-d');
$z=0;
#
# And compare with exten description in cycle
#
while ($row=mysqli_fetch_row($res)) {
        $d=$row[0];
        $e=$row[1];
        if ($date > $d ) {
                #
                # If match - remove it and increment counter
                #
                echo "Remove ".$e."<br>\n";
                mysqli_query($conn, "DELETE FROM `meetme` where exten=".$e);
                $z++;
        }
}
#
# Show counter to user
#
echo "Removed ".$z ." items<br>\n";
#
# Reload FreePBX configuration
#
if ($z > "0") { exec("fwconsole reload"); } else { echo "nothing to reload<br>\n"; }
?>

/etc/crontab:
  59 23 *  *  * root php /var/www/html/remove_old_conf.php

/etc/ssmtp/revaliases:
asterisk:voip@domain.com:mail.domain.com:25

/etc/ssmtp/ssmtp.conf:
root=voip@domain.com
mailhub=mail.domain.com
RewriteDomain=domain.com
Hostname=voip.domain.com
FromLineOverride=Yes
UseStartTLS=No
Debug=No
AuthUser=voip
AuthPass=P@$$w)rD

Теперь сделаем, чтобы из IVR можно было добраться до конференции. У меня IVR, с которого я хочу сделать возможность звонить в конференцию - ivr-1.
/etc/asterisk/extensions_custom.conf:
[ivr-1-custom]
include =>ext-meetme

Делаем: mv /usr/sbin/sendmail /usr/sbin/sendmail.bak && ln -s /usr/sbin/ssmtp /usr/sbin/sendmail && service asterisk restart && service crond restart
И все должно заработать.
Это не совсем готовая инструкция, применяйте голову.
Теперь это готовый код. Проблема была в том, что нужно вручную записывать значение userpin через обращение к AstDB (ищите в коде database put)

Авторизацию на эту "админку" сами прикручивайте какую хотите.

понедельник, 27 марта 2017 г.

Опять Zabbix

Есть VPN-клиенты с УТМ(Мерката).
Захотелось мониторить некоторую информацию с этих машин.
Написал скрипты:
{{{
#!/bin/sh
hosts=`cat /usr/local/etc/zabbix32/zabbix/externalscripts/3-15`
for host in $hosts
do
host_end=`echo $host | cut -d "." -f 4`
hst=`echo $host_end"-10" | bc -ql`
if [ $hst -lt "10" ]; then
    hst=`echo "0"$hst`
fi
loss=`ping -s 32 -c 5 $host | grep "packet loss" | cut -d "," -f 3 | cut -d "%" -f 1 | cut -d " " -f 2`
code=`curl --connect-timeout 2 -s -I http://$host:8080 | grep HTTP | cut -d " " -f 2`
if [ -z $code ]; then
    code="0"
fi
warn=`curl -s --connect-timeout 2 http://$host:8080 | grep warn`
if [ -z $warn ]; then
    warn="0"
fi
if [ ! -z $warn ]; then
        echo $warn
        zabbix_sender -z 127.0.0.1 -s "Zabbix server" -k remote$hst.warnings -o $warn
fi
done
echo $loss
zabbix_sender -z 127.0.0.1 -s "Zabbix server" -k remote$hst.ping -o $loss
echo $code
zabbix_sender -z 127.0.0.1 -s "Zabbix server" -k remote$hst.httpcode -o $code
}}}
{{{
#!/bin/sh
hosts=`cat /usr/local/etc/zabbix32/zabbix/externalscripts/3-15`
for host in $hosts
do
host_end=`echo $host | cut -d "." -f 4`
hst=`echo $host_end"-10" | bc -ql`
if [ $hst -lt "10" ]; then
    hst=`echo "0"$hst`
fi
fsrar_id=`curl --connect-timeout 2 -s http://$host:8080 | grep FSRAR | grep PKI | cut -d ":" -f 2 | cut -d "-" -f 3 | cut -d " " -f 1`
version=`curl --connect-timeout 2 -s http://$host:8080 | grep version | cut -d ":" -f 2`
serial=`curl --connect-timeout 2 -s http://$host:8080/info/certificate/RSA | grep "SerialNumber" | cut -d "[" -f 2 | cut -d "]" -f 1`
exp=`curl --connect-timeout 2 -s http://$host:8080/info/certificate/RSA | grep "To:" | sed -e 's/]//g' | sed -e 's/  //g' | sed -e 's/To: //g'`
echo "\"Zabbix server\" remote$hst.version \"$version\"" >/tmp/315_daily.tmp
echo "\"Zabbix server\" remote$hst.fsrarid \"$fsrar_id\"" >>/tmp/315_daily.tmp
echo "\"Zabbix server\" remote$hst.serial \"$serial\"" >>/tmp/315_daily.tmp
echo "\"Zabbix server\" remote$hst.expire \"$exp\"" >>/tmp/315_daily.tmp
zabbix_sender -z 127.0.0.1 -i /tmp/315_daily.tmp
done
}}}
Ну ок, думаю, что надо создавать элементы данных для каждого клиента VPN. Один, второй, третий, данунахер!!! Их (клиентов) там 30 штук и внутри каждого по 5-6 элементов данных и прибывают.
Ок, что нам предложит заббикс? Zabbix trapper + discovery.
Для начала напишу скрипт для discovery, подумал я.
{{{
#!/bin/sh
names=`cat /3-15`
data="{\"data\":["
num=0
for name in $names
do
num=`echo $num +1|bc`
if [ $num -lt "10" ]; then
        num=`echo "0"$num`
fi
#echo $num
data=`echo $data"{\"{#REMOTE}\":\"$name\",\"{#NUM}\":\"$num\"},"`
done
data=`echo $data | sed "s/.$//"`
data=`echo $data"]}"`
echo $data
#zabbix_sender -z localhost -p 10051 -s "Zabbix server" -k remote.name -o '$data'
}}}
Ок, создаем шаблон. Делаем новое дисковери, даже называем его как-то. Переходим к созданию прототипов элементов данных и тут заббикс подкладывает свинью - он не умеет в качестве ключа и названия прототипа использовать макросы. Все, приехали.
 Как вариант - надо попробовать прикрутить через API.

Спасибо за внимание.