none
Проверить логинился ли пользователь в течении 90 дней RRS feed

  • Вопрос

  • Доброго времени суток.

    Есть сверхзадача - проверить подключался ли пользователь хоть раз за 90 дней по vpn.

    Суть вопроса:

    VPN предоставляется по срадствам Cisco ASA 5520. Логи её складываются на СХД. Имя файла лога имеет вид:

    asa5520-20140915.gz(архивный) asa5520-20140916.gz asa5520-20140917.gz ... и т.д. 90 файлов за 90 дней . Хотелось бы для пользователей группы VPN в AD (Windows Server 2012 уровень функциональности 2012) проверить логинился ли он хоть раз в течении 90 дней. Строка коннекта в логе имеет вид:

    Sep 14 21:03:41 10.206.25.6 Sep 14 2014 21:03:40: %ASA-4-722051: Group <GrPol_business-2> User <ivan.ivanov> IP <xxx.xxx.xxx.xxx> Address <10.200.224.125> assigned to session 

    Как я себе это вижу:

    получить всех пользователей группы, получить текущую дату и просмотреть все 90 файлов коннектился ли пользователь со своим SamAccountName хоть раз за это время и результат выдать в файл и отправиь по почте. Дело в том что логи очень большие и пользователей порядка 100 да к тому же логи архивированы...

    Возможно ли это реализовать на PoSh 4.0. Прошу не ругать за наглость - вы уж помогите кто чем может.

     

    30 сентября 2014 г. 7:20

Ответы

  • Примерно так можно получить список всех пользователей, присутствующих в логах:

    -89..0|ForEach-Object {
        $Date=Get-Date
    }{
        $FileName=$Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
        $File=[System.IO.File]::OpenRead($filename)
        try{
            $ByteStream=New-Object System.IO.Compression.GZipStream $File,([System.IO.Compression.CompressionMode]::Decompress)
            try{
                $StringStream=New-Object System.IO.StreamReader $ByteStream
                try{
                    while(($String=$StringStream.ReadLine())-ne$null){
                        $String
                    }
                }finally{
                    $StringStream.Close()
                }
            }finally{
                $ByteStream.Close()
            }
        }finally{
            $File.Close()
        }
    }|ForEach-Object {
        if($_-match'User <([^>]+)>'){
            $Matches[1]
        }
    }|Select-Object -Unique

    • Предложено в качестве ответа Vector BCOModerator 1 октября 2014 г. 7:23
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:23
    30 сентября 2014 г. 18:00
  • $group = Get-ADGroupMember ADministrators -Recursive | Foreach {$_.SamAccountName}
    
    $vpn = -89..0|ForEach-Object {Остальная часть кода выше
    
    Compare-Object $group $vpn | Where {$_.SideIndicator -eq "<="}

    • Предложено в качестве ответа Vector BCOModerator 1 октября 2014 г. 7:23
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:23
    1 октября 2014 г. 7:01
    Отвечающий
  • Можно при первом запросе кешировать результат в файл, а при повторном просто брать из кеша.
    -89..0|ForEach-Object {
        $LogsPath='C:\Logs'
        $CachePath=$LogsPath
        $Date=Get-Date
    }{
        $FileName=$Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
        $CacheFile=Join-Path $CachePath ($FileName+'.cache')
        if((Test-Path $CacheFile)){
            Get-Content $CacheFile
        }else{
            $LogFile=Join-Path $LogsPath $FileName
            &{
                $File=[System.IO.File]::OpenRead($LogFile)
                try{
                    $ByteStream=New-Object System.IO.Compression.GZipStream $File,([System.IO.Compression.CompressionMode]::Decompress)
                    try{
                        $StringStream=New-Object System.IO.StreamReader $ByteStream
                        try{
                            while(($String=$StringStream.ReadLine())-ne$null){
                                $String
                            }
                        }finally{
                            $StringStream.Close()
                        }
                    }finally{
                        $ByteStream.Close()
                    }
                }finally{
                    $File.Close()
                }
            }|ForEach-Object {
                if($_-match'User <([^>]+)>'){
                    $Matches[1]
                }
            }|Select-Object -Unique|Tee-Object $CacheFile
        }
    }|Select-Object -Unique
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:24
    2 октября 2014 г. 6:07

Все ответы

  • Примерно так можно получить список всех пользователей, присутствующих в логах:

    -89..0|ForEach-Object {
        $Date=Get-Date
    }{
        $FileName=$Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
        $File=[System.IO.File]::OpenRead($filename)
        try{
            $ByteStream=New-Object System.IO.Compression.GZipStream $File,([System.IO.Compression.CompressionMode]::Decompress)
            try{
                $StringStream=New-Object System.IO.StreamReader $ByteStream
                try{
                    while(($String=$StringStream.ReadLine())-ne$null){
                        $String
                    }
                }finally{
                    $StringStream.Close()
                }
            }finally{
                $ByteStream.Close()
            }
        }finally{
            $File.Close()
        }
    }|ForEach-Object {
        if($_-match'User <([^>]+)>'){
            $Matches[1]
        }
    }|Select-Object -Unique

    • Предложено в качестве ответа Vector BCOModerator 1 октября 2014 г. 7:23
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:23
    30 сентября 2014 г. 18:00
  • А как прикрутить сюда выборку из группы безопасности SamAccountName пользователей и проверку тех кого нет???
    1 октября 2014 г. 5:16
  • $group = Get-ADGroupMember ADministrators -Recursive | Foreach {$_.SamAccountName}
    
    $vpn = -89..0|ForEach-Object {Остальная часть кода выше
    
    Compare-Object $group $vpn | Where {$_.SideIndicator -eq "<="}

    • Предложено в качестве ответа Vector BCOModerator 1 октября 2014 г. 7:23
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:23
    1 октября 2014 г. 7:01
    Отвечающий
  • получился такой скрипт:

    $group = Get-ADGroupMember vpn-business -Recursive | Foreach {$_.SamAccountName}

    $vpn = -89..0|ForEach-Object
    {
    $Date=Get-Date

    {
    $FileName=$Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
    $File=[System.IO.File]::OpenRead($filename)
    try{
    $ByteStream=New-Object System.IO.Compression.GZipStream $File,([System.IO.Compression.CompressionMode]::Decompress)
    try{
    $StringStream=New-Object System.IO.StreamReader $ByteStream
    try{
    while(($String=$StringStream.ReadLine())-ne$null){
    $String
    }
    }finally{
    $StringStream.Close()
    }
    }finally{
    $ByteStream.Close()
    }
    }finally{
    $File.Close()
    }
    }|ForEach-Object {
    if($_-match'User <([^>]+)>'){
    $Matches[1]
    }
    }|Select-Object -Unique

    Compare-Object $group $vpn | Where {$_.SideIndicator -eq "<="
    }
    }

    но он выводит:

    cmdlet ForEach-Object at command pipeline position 1
    Supply values for the following parameters:
    Process[0]:

    не подскажите как указать ему путь к требуемым файлам?

    1 октября 2014 г. 8:46
  • Ему требуется не путь, а указать scriptblock. Это от того, что скрипт даже правильно скопировать не смогли.

    $vpn = -89..0|ForEach-Object { - Фигурная скобка должна быть на той же строке, а не на новой.

    1 октября 2014 г. 9:03
    Отвечающий
  • Ваша правда!

    А не подскажете как указать ему путь где хрантся файлы? А то он у меня исчет файлы в профиле пользователя от которого запускается скрипт...

    1 октября 2014 г. 9:56
  • $FileName= "C:\folder\" + $Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
    1 октября 2014 г. 10:05
    Отвечающий
  • Отлично! Работает!!!

    Не сочтите за нахальство но уважаемый Kazun а Вы не подскажите как распаралелить процесс а то поиск занимает часы т.к. в файлах в среднем 400 000 строк... Ещё раз огромное спасибо!!!!

    1 октября 2014 г. 12:38
  • Вопрос в том, что является узким местом в данном случае. Если это дисковый ввод/вывод, то его вряд ли получится распараллелить. Возможно имеет смысл по каждому лог-файлу один раз построить выжимку, которая бы содержала только имена пользователей, а потом с этими выжимками работать.
    1 октября 2014 г. 19:14
  • а как это реализовать не подскажите?
    2 октября 2014 г. 5:25
  • Можно при первом запросе кешировать результат в файл, а при повторном просто брать из кеша.
    -89..0|ForEach-Object {
        $LogsPath='C:\Logs'
        $CachePath=$LogsPath
        $Date=Get-Date
    }{
        $FileName=$Date.AddDays($_).ToString('a\sa5520-yyyyMMdd.\g\z')
        $CacheFile=Join-Path $CachePath ($FileName+'.cache')
        if((Test-Path $CacheFile)){
            Get-Content $CacheFile
        }else{
            $LogFile=Join-Path $LogsPath $FileName
            &{
                $File=[System.IO.File]::OpenRead($LogFile)
                try{
                    $ByteStream=New-Object System.IO.Compression.GZipStream $File,([System.IO.Compression.CompressionMode]::Decompress)
                    try{
                        $StringStream=New-Object System.IO.StreamReader $ByteStream
                        try{
                            while(($String=$StringStream.ReadLine())-ne$null){
                                $String
                            }
                        }finally{
                            $StringStream.Close()
                        }
                    }finally{
                        $ByteStream.Close()
                    }
                }finally{
                    $File.Close()
                }
            }|ForEach-Object {
                if($_-match'User <([^>]+)>'){
                    $Matches[1]
                }
            }|Select-Object -Unique|Tee-Object $CacheFile
        }
    }|Select-Object -Unique
    • Помечено в качестве ответа KazunEditor 9 октября 2014 г. 7:24
    2 октября 2014 г. 6:07
  • Выдаёт ошибку:

    Exception calling "OpenRead" with "1" argument(s): "The given path's format is not supported."
    At line:15 char:13
    + $File=[System.IO.File]::OpenRead($LogFile)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : NotSupportedException

    You cannot call a method on a null-valued expression.
    At line:31 char:17
    + $File.Close()
    + ~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    New-Object : Exception calling ".ctor" with "2" argument(s): "Value cannot be null.
    Parameter name: stream"
    At line:17 char:29
    + $ByteStream=New-Object System.IO.Compression.GZipStream $File,([ ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    2 октября 2014 г. 6:27
  • Покажите конкретный путь, который получается в переменной $LogFile.
    2 октября 2014 г. 6:31
  • PS C:\Users\administrator> $LogsPath
    C:\111\Logs
    2 октября 2014 г. 6:59
  • Не $LogsPath, а $LogFile.
    2 октября 2014 г. 7:03
  • Пардон

    Вот:

    PS C:\Users\administrator> $LogFile
    C:\111\Logs\C:\111\asa\asa5520-20141002.gz

    2 октября 2014 г. 8:52
  • Путь к папке с логами я вынес в отдельную переменную $LogsPath, так что его не нужно добавлять к переменной $FileName. Переменная $FileName должна содержать только имя файла.
    • Изменено PetSerAl 2 октября 2014 г. 8:59
    2 октября 2014 г. 8:58
  • Мне кажется можно сделать проще, если циска авторизуется на AD то можно взять параметр LastLogon из AD

    function Get-ADUserLastLogon([string]$userName)
    {
      $dcs = Get-ADDomainController -Filter {Name -like "*"}
      $time = 0
      foreach($dc in $dcs)
      {
        $hostname = $dc.HostName
        $user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
        if($user.LastLogon -gt $time)
        {
          $time = $user.LastLogon
        }
      }
      $dt = [DateTime]::FromFileTime($time)
      Write-Host $username "last logged on at:" $dt }

    Get-ADUserLastLogon -UserName SaraDavis


    MCITP, MCTS, OCA.

    2 октября 2014 г. 9:09
  • Я думал над этим.

    Дело в том что эта функция не всегда корректно отрабатывает

    2 октября 2014 г. 9:17
  • Ой да!!!

    Написал Вам а потом полез смотреть - действительно я лоханулся!!!

    2 октября 2014 г. 9:19
  • get-adgroupMember GROUPNAME | foreach {
    $user = Get-ADUser $_.SamAccountName
    $user.SamAccountName
    }


    MCITP, MCTS, OCA.

    2 октября 2014 г. 9:28
  • Обычно когда берется время входа оно берется с одного контроллера домена, а пользователь мог авторизоваться на другом. В этом скрипте опрашиваются все контроллеры и сравнивается время входа.

    MCITP, MCTS, OCA.

    2 октября 2014 г. 9:32
  • Спасибо за ответ.

    Но дело в том что они могут так же  логинится не только через впн но и на работе в нутри сети. Так что это не подходит

    2 октября 2014 г. 10:50
  • Ну скрипт отработал.Не скажу что стало быстрее. А подскажите как теперь сравнить вывод скрипта с членами группы впн???
    3 октября 2014 г. 7:10
  • При первом вызове он и не должен работать быстрее, зато последующие вызовы должны брать результат из кэша, если Вы его не удалили. Как сравнивать Вам уже отвечали:
    $group = Get-ADGroupMember ADministrators -Recursive | Foreach {$_.SamAccountName}
    
    $vpn = -89..0|ForEach-Object {Остальная часть кода выше
    
    Compare-Object $group $vpn | Where {$_.SideIndicator -eq "<="}

    3 октября 2014 г. 20:48