##/*********************************************************************************************
##Author                :  Guravareddy T
##Purpose               : It is the base script for some SBO tests and it will get skype call sessions data of all users in the domain and save the data
##Created               :  11/08/2018
##Modified By		    :
##reference link        :(https://msconfiggallery.cloudapp.net/packages/CxdCallData/1.2.2.0/Content/CxdCallData.psm1)
                         #(https://gallery.technet.microsoft.com/Get-CSUserSessionDomains-c8b0257c)

clear
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$egInstallpath=Get-ChildItem Env:EGURKHA_INSTALL_DIR |Select Value
$egurkhaPath=$egInstallpath.Value.ToString()
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$egEncryPath=$egurkhaPath+'\lib\EGFileEncryption.psm1'
$egDatnKy=$egurkhaPath+'\lib\GetDatnKeyFiles.psm1'
Import-Module $egEncryPath,$egDatnKy

$testargs=$args
$userName=$testargs[0]
$Password=Eg-O365Dcr -EncStr $testargs[1]
$prxycrdlsServer=($testargs[2]).ToString().Split("#")
$proxyUsr=$prxycrdlsServer[0]
$proxyPass=Eg-O365Dcr -EncStr $prxycrdlsServer[1]
$proxyserverip=$prxycrdlsServer[2]
$rNamefreq=$testargs[3]
$rptnamefrq=$rNamefreq.split('#')
$rName=$rptnamefrq[0]
$Testfreqency=$rptnamefrq[1]
$overrideUri=$testargs[4]
$rptPath='SBO\'+$rName

$credential = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $userName, $(convertto-securestring $Password -asplaintext -force)
$wratSyallsessions=Eg-WriteFile -ComntRptPath $rptPath -FileName "Syallsessions" -keyFileName "kSyallsessions" -EgPath $egurkhaPath
$wratMycalFeedBack=Eg-WriteFile -ComntRptPath $rptPath -FileName "SyRatMycalFeedBack" -keyFileName "kSyRatMycalFeedBack" -EgPath $egurkhaPath
$wratSyVideoSes=Eg-WriteFile -ComntRptPath $rptPath -FileName "SyVideoSessions" -keyFileName "kSyVideoSessions" -EgPath $egurkhaPath
$wratSyAppshrngSes=Eg-WriteFile -ComntRptPath $rptPath -FileName "SyAppshrngSessions" -keyFileName "kSyAppshrngSessions" -EgPath $egurkhaPath
$wratSyalldomains=Eg-WriteFile -ComntRptPath $rptPath -FileName "Syalldomains" -keyFileName "kSyalldomains" -EgPath $egurkhaPath

$domins=New-Object System.Collections.ArrayList
$WriteLog=$true
$LogFile = $egurkhaPath+'/agent/SBO/'+$rName+"\sbousersesn.log"
$LogFile1 = $egurkhaPath+'/agent/SBO/'+$rName+"\sbousersesn1.log"
$isFrstTimLog=$true
function Get-CurrentLineNumber {
    $MyInvocation.ScriptLineNumber
}

Function Write-Log {
	Param ([string]$string)

	[string]$date = Get-Date -Format G
	
    if ($WriteLog) {
       ( "[" + $date + "] - [" +$rName+"] mTime -  " + $Testfreqency + " - " + $string ) | Out-File -FilePath $LogFile -Append } 

    if($isFrstTimLog){     
        if ($WriteLog -eq $true){ #if flag is true 
            if ([System.IO.File]::Exists($LogFile) -and (Get-Item $LogFile).length -gt 2mb) {  #if the size of file is greater than 1MB 
                if([System.IO.File]::Exists($LogFile1)){  #if logfile1 already exists, delete logfile1 
                    Remove-Item $LogFile1 
                } 
                Rename-Item $LogFile $LogFile1
            }
        }
        $isFrstTimLog=$false
    }

}

function Get-CxdCallData{
    [cmdletbinding()]
        Param
        (
            [Parameter(Mandatory=$true)][int]$NumberOfMinutesToSearch,
            [Parameter(Mandatory=$false)][string]$rptPath,
            [Parameter(Mandatory=$false)][PSCredential]$Credential
        )

    begin {
        try{
            Set-WinRMNetworkDelayMS -value "60000" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable WinRMError
        }catch{
            Write-log "[ERROR]Unable to set the WinRM Network Delay. This may be due to User Account Control settings. If you're not encountering timeouts you can safely ignore this warning, otherwise try running the script as an elevated user (Administrator)."
        }
    }

    process {
        $startTime = (Get-Date).AddMinutes(-$NumberOfMinutesToSearch) #note the .AddDays is subtracting the number of days to search
        $endTime = Get-Date
        $global:sessionStartTime = $null
        $numBuckets = 0
        $numUsers = 0
        $enabledUsers = $null
        $arrNotUsingSkype = $null

        if (!$Credential){
            $Credential = Get-Credential -Message "Authenticate to Skype for Business Online"
        }
        Write-Log '.................Strating the execution of the script..................'
        #create initial SFBO Connection
        Invoke-CxdSkypeOnlineConnection


        try{
            [array]$enabledUsers = ProcessSkypeOnlineUsers $rptPath
        }catch{
            Write-Log " [Error] Exception encounterd while getting users. Attempting to remove the PowerShell PSSession and will retry."
            Invoke-CxdSkypeOnlineConnection -RepairPSSession
            [array]$enabledUsers = ProcessSkypeOnlineUsers $rptPath
        }
        
        if (!$enabledUsers.Count){
            Write-Log  "We didn't find any users matching your query. Exiting..."
            Write-Log "Aborting the Script"
            break
        }

        $userTotal = $enabledUsers.Count
        $userBuckets = [math]::Ceiling($userTotal /10)
        $arrUserBuckets = @{}

        #create users and put them into buckets
        Write-Verbose -Message "Putting $($enabledUsers.Count) users into buckets..."
        $count = 0
        $enabledUsers | ForEach-Object {
            $arrUserBuckets[$count % $userBuckets] += @($_)
            $count++
        }
        Write-Log "Placed $($enabledUsers.Count) users into $($arrUserBuckets.Count) bucket(s)."
        $enabledUsers=$null
        #process all discovered users into buckets
        $arrUserBuckets | ProcessBuckets


    }
    end{
        Write-Log "Removing PowerShell Sessions......"
        Get-PSSession | Remove-PSSession
        Write-Log "Script completed Successfully"
    }   
}

function ProcessBuckets{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]$arrUserBuckets
    )
    begin{}
    process{
        foreach ($userItem in $arrUserBuckets.Values){
            #progress bar for total buckets
            $numBuckets++
            ProcessUsersInBuckets -userItem $userItem
        }
    }
    end{
    }   
}

function ProcessUsersInBuckets{
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$false)]$userItem
    )
    begin{

        #before we process this bucket of users we need to check the global session timer to prevent access token expiration
        Invoke-CxdSkypeOnlineConnection
    }
    process{
        foreach($userI in $userItem){
            #set variables
            $userSession=$null
            $Sessions=$null
            $getUserSessionError = $null
            $sipAddress = $userI.SipAddress.Replace("sip:","")
            Try{
                [array]$userSession = ObtainUserSessionData -startTime $startTime -endTime $endTime -sipAddress $sipAddress
            }Catch{
                Write-Log "Unable to retrieve user session information. Attempting to repair the Skype Online connection and will try again."
                Invoke-CxdSkypeOnlineConnection -RepairPSSession
                [array]$userSession = ObtainUserSessionData -startTime $startTime -endTime $endTime -sipAddress $sipAddress
            }
             if ($userSession){
                $Sessions=$userSession | where-object {$_.FromUri -ne $_.ToUri}
                if($Sessions){
                    UsersDomains -userSession $Sessions
                    SeperateSessions -userSession $Sessions
                } 
            }
            $userSession=$null;$Sessions=$null

        }
    }
    end{
    }
            
}

function ObtainUserSessionData{
    [cmdletbinding()]
    Param(
        $startTime,
        $endTime,
        $sipAddress
    )

    begin {}
    process {
        $usrPath=$egurkhaPath+'\agent\'+$rptPath
        if(!(Test-Path -Path $usrPath )){
            $null=New-Item -ItemType directory -Path $usrPath
        }
        Try{
            $userSession=$null
            $Error.Clear()

           $ScriptBlock = {
                param($startTime,$endTime,$sipAddress)
                    Get-CsUserSession -StartTime $startTime -EndTime $endTime -User $sipAddress
                }
            [array]$userSession=invoke-command -scriptblock $ScriptBlock -ArgumentList $startTime,$endTime,$sipAddress -session (get-pssession)
           # [array]$userSession = Get-CsUserSession -StartTime $startTime -EndTime $endTime -User $sipAddress # -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable $userSessionError
           if($Error)
           {
            Write-Log ("[ "+$(Get-CurrentLineNumber) +" ] "+ "[ERROR] "+$Error)
           # throw $_
           }
        }Catch{
             $ErrorMessage = $_.Exception.Message
             Write-Log ("[ "+$(Get-CurrentLineNumber) +" ] "+ "ErrorMessage while running Get-CsUserSession: "+$ErrorMessage)
             #there was an exception and the command did not execute
             #throw $_
        }Finally{
            if ($userSessionError){
                #the command executed but returned an error
                #throw $getUserSessionError
            }
        }

        #sort data by oldest to newest to check the array for the oldest call in preparation for recursion
        $userSession = $userSession | Sort-Object -Property EndTime

        #verify record count hasn't been exceeded and perform recursion if necessary to retrieve additional records
        if ($userSession.Count -ge 1000){
            $newStart = $userSession[-1].endTime
            Write-Information -Message "Found more than 1000 records for $sipAddress. Performing recursive query to obtain more data using revised start date of $newStart"
            $userSession += ObtainUserSessionData -startTime $newStart -endTime $endTime -sipAddress $sipAddress
        }   
    }
    end {
        return $userSession
    }
}

function SeperateSessions{
    [cmdletbinding()]
    Param(
        [Parameter()]$userSession
    )

    begin {}
    process {

            [array]$allsessions = $userSession | Where-Object {$_.MediaTypesDescription -like "*IM*" -or $_.MediaTypesDescription -eq ""}
            [array]$audioSessions = $userSession | Where-Object MediaTypesDescription -like "*Audio*"
            [array]$videosessions = $userSession | Where-Object MediaTypesDescription -like "*Video*"
            [array]$Appshrngsessions = $userSession | Where-Object MediaTypesDescription -like "*AppSharing*"
    }
    end {

        if ($allsessions){
            Export-CxdData -DataInput $allsessions -ReportType 'Syallsessions' -Append
        }
        if ($audioSessions){
            Export-CxdData -DataInput $audioSessions -ReportType 'SyRatMycalFeedBack' -Append
        }
        if ($videosessions){
            Export-CxdData -DataInput $videosessions -ReportType 'SyVideoSessions' -Append
        }
        if ($Appshrngsessions){
            Export-CxdData -DataInput $Appshrngsessions -ReportType 'SyAppshrngSessions' -Append
        }
        $allsessions=$null;$audioSessions=$null;$videosessions=$null;$Appshrngsessions=$null
    }
}

function UsersDomains{
    [cmdletbinding()]
    Param(
        [Parameter()]$userSession
    )

    begin {}
    process {
        [array]$allSessions = $userSession | Select-Object FromUri,ToUri
        if($allSessions){
            foreach($entry in $allSessions)
            {
                $domain=($entry.FromUri).split('@')[1].trim()
                if(!$domins.Contains($domain)) { $domins.Add($domain) }
                $domain=($entry.ToUri).split('@')[1].trim()
                if(!$domins.Contains($domain)){  $domins.Add($domain) }
            }

        }
    }
    end {
    }
}

function Export-CxdData{
    
    [cmdletbinding()]
    Param
    (
        [Parameter(mandatory=$true, valuefrompipeline=$false)][PSObject]$DataInput,
        [Parameter(mandatory=$true, valuefrompipeline=$false)][string]$ReportType,
        [Parameter(mandatory=$false, valuefrompipeline=$false)][switch]$Append
    )

    begin{}
    process{           
            switch($ReportType){ 
            
                'Syallsessions'{$dataInput | Export-Csv $wratSyallsessions[1] -NoTypeInformation -Append}               
                'SyRatMycalFeedBack'{$dataInput | Export-Csv $wratMycalFeedBack[1] -NoTypeInformation -Append}
                'SyVideoSessions'{$dataInput | Export-Csv $wratSyVideoSes[1] -NoTypeInformation -Append}
                'SyAppshrngSessions'{$dataInput | Export-Csv $wratSyAppshrngSes[1] -NoTypeInformation -Append}
                'Syalldomains'{$dataInput | Export-Csv $wratSyalldomains[1] -NoTypeInformation -Append}
                'CSOnlineUsr'{$dataInput | Export-Csv $wratCSOnlineUsr[1] -NoTypeInformation -Append}
            }
    }
    end{
        
    }

}

function Invoke-CxdSkypeOnlineConnection{
    [cmdletbinding()]
    Param
    (
    [Parameter(mandatory=$false, valuefrompipeline=$false)]
    [switch]$RepairPSSession
    )
    begin{}
    process{
        #calculate session timer to handle access token expiration
        if ($global:sessionStartTime){
            $global:sessionTotalTime = ((Get-Date) - $global:sessionStartTime)
        }

        #determine if Skype for Business PsSession is loaded in memory
        $sessionInfo = Get-PsSession

        #need to loop through each session a user might have opened previously
        foreach ($sessionItem in $sessionInfo){
            #check session timer to know if we need to break the connection in advance of a timeout. Break and make new after 40 minutes.
            if ($sessionItem.ComputerName.Contains(".online.") -and $sessionItem.State -eq "Opened" -and $global:sessionTotalTime.TotalSeconds -ge "2400"){
                Write-Log "The PowerShell session has been running for $($global:sessionTotalTime.TotalMinutes) minutes. We need to shut it down and create a new session due to the access token expiration at 60 minutes."
                $sessionItem | Remove-PSSession
                Start-Sleep -Seconds 2
                $SessionFound = $false
                $global:sessionTotalTime = $null #reset the timer
            }

            #try to repair PSSession
            if ($sessionItem.ComputerName.Contains(".online.") -and $sessionItem.State -ne "Opened" -and $RepairPSSession){
                Write-Verbose -Message "Attempting to repair broken PowerShell session to Skype for Business Online using cached credential."
                $sessionItem | Remove-PSSession
                Start-Sleep -Seconds 3
                $SessionFound = $false
                $global:sessionTotalTime = $null
            }elseif ($sessionItem.ComputerName.Contains(".online.") -and $sessionItem.State -eq "Opened"){
                $SessionFound = $true
            }
        }

        if (!$SessionFound){
            Write-Log "Creating new Skype Online PowerShell session..."
            try{
                 $sessionOption = New-PSSessionOption -SkipRevocationCheck 
	             if($proxyUsr -ne 'none' -and $proxyPass -ne 'none'){
	                 $proxyCred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $proxyUsr, $(convertto-securestring $proxyPass -asplaintext -force)
	                 $sessionOption = New-PSSessionOption -SkipRevocationCheck -ProxyAccessType WinHttpConfig -ProxyAuthentication basic -ProxyCredential $proxyCred
	                    #[System.Net.WebRequest]::DefaultWebProxy.Credentials = $proxyCred    
	                    #$prx=(netsh winhttp show proxy)[3].Split(":")
	                    #$proxyserverip=($prx[1]+':'+$prx[2]).Trim()
	                    $proxyserver='http://'+$proxyserverip
	                    [system.net.webrequest]::defaultwebproxy = new-object system.net.webproxy($proxyserver)
	                    [system.net.webrequest]::defaultwebproxy.credentials =$proxyCred   #[System.Net.CredentialCache]::DefaultNetworkCredentials
	                    [system.net.webrequest]::defaultwebproxy.BypassProxyOnLocal = $true	             
                 }
                 if($overrideUri -ne 'none'){
                    $lyncsession = New-CsOnlineSession -Credential $Credential -SessionOption $sessionOption –OverridePowershellUri $overrideUri # -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable $newOnlineSessionError 
                 }
                 else{ $lyncsession = New-CsOnlineSession -Credential $Credential -SessionOption $sessionOption }

                 Write-Log 'Session got created successfully.'

            }catch{
             $ErrorMessage = $_.Exception.Message
             Write-Log ("[ "+$(Get-CurrentLineNumber) +" ] "+ "ErrorMessage while creating session: "+$ErrorMessage)         
             throw;
            }finally{
                if ($newOnlineSessionError){
                    throw $newOnlineSessionError
                }
            }
            Write-Verbose -Message "Importing remote PowerShell session..."
            $global:sessionStartTime = (Get-Date)
            Import-PSSession $lyncsession -AllowClobber | Out-Null
        }
    }
    end{}
}


function ProcessSkypeOnlineUsers{
    [cmdletbinding()]
    Param(
        [Parameter()]$reportingname
    )
    process{
            $allusers=""
            try{
                $readfiles=Eg-ReadFile -ComntRptPath $reportingname -FileName "CSOnlineUsr" -keyFileName "kCSOnlineUsr" -EgPath $egurkhaPath
                $datafile=$readfiles[1] -replace (".csv",".dat") 
                $csvfile=Unprotect-File $datafile -Algorithm AES -KeyAsPlainText $readfiles[0]
                $allusers= import-csv $csvfile | Select-Object *
                write-log ('readed users csv file '+$readfiles[3])                                  			 			 					
                Remove-Item $csvfile
                $readtime = (Get-Item $readfiles[1]).LastWriteTime
    		    $readfiledif=(NEW-TIMESPAN –Start $readtime –End (Get-Date) | Select-Object TotalMinutes).TotalMinutes
                if($readfiledif -gt 1440)
                {
                   $allusers=""
                   write-log ' Readed CSOnlineUsr csv file is older..... so we are creating new file' 
                }
            }
            catch{
               write-log ' CSOnlineUsr csv file is not found going to creating the file'   
               $allusers=""
             }

            if($allusers)
            {  try{
                    write-log  "getting all users of Skype for Business Online from input CSV file"
                    $enabledUsers = $allusers | Select-Object Enabled,SipAddress | Where-Object {$_.Enabled -eq 'TRUE'}
                    return $enabledUsers
                  }
                  catch{
                        $ErrorMessage = $_.Exception.Message                   
                        Write-Log ("[ "+$(Get-CurrentLineNumber) +" ] "+ "Exception while reading the users from csv:"+$ErrorMessage)
                        return ""
                  }

            }else{
                $wratCSOnlineUsr=Eg-WriteFile -ComntRptPath $rptPath -FileName "CSOnlineUsr" -keyFileName "kCSOnlineUsr" -EgPath $egurkhaPath
                $allusers=Get-CSOnlineUser |Select-Object AcpInfo,AssignedPlan,DisplayName,RegistrarPool,SoftDeletionTimestamp,UserPrincipalName,WhenChanged,WhenCreated,Enabled,SipAddress
                Export-CxdData -DataInput $allusers -ReportType 'CSOnlineUsr' -Append
                $null=Protect-File  $wratCSOnlineUsr[1] -Algorithm AES -KeyAsPlainText $wratCSOnlineUsr[0] -RemoveSource 
                $enabledUsers = $allusers | Select-Object Enabled,SipAddress | Where-Object {$_.Enabled -eq '$True'}
                Return $enabledUsers
            }
    }
    end{}
}


function Set-WinRMNetworkDelayMS{

  param(
    [Parameter(Mandatory = $false)] [string] $value="30000"
  )

  $networkDelay = Get-Item WSMan:\localhost\Client\NetworkDelayms

  if($networkDelay -eq $null)
  {
    # If cannot get NetworkDelayms due to permission or other reason, just return.
    return
  }

  $oldValue = $networkDelay.Value
  $newValue = $value

  if($newValue -ne $oldValue)
  {
    Set-Item WSMan:\localhost\Client\NetworkDelayms $newValue

  }
}

try{

    Get-CxdCallData -NumberOfMinutesToSearch $Testfreqency -Credential $credential -rptPath $rptPath

}catch{
    $ErrorMessage = $_.Exception.Message
    Write-Log ("[ "+$(Get-CurrentLineNumber) +" ] "+ "ErrorMessage while running Get-CsUserSession: "+$ErrorMessage)
    exit
}


$uPath=$egurkhaPath+'\agent\'+$rptPath

if($domins){
    foreach($domain in $domins){        
            $newObject = [PSCustomObject][ordered]@{
            domain=$domain
        }        
        Export-CxdData -DataInput $newObject -ReportType 'Syalldomains' -Append                  
        
    }
}

if(Test-Path $wratSyallsessions[1] -PathType Leaf){
    $null=Protect-File  $wratSyallsessions[1] -Algorithm AES -KeyAsPlainText $wratSyallsessions[0] -RemoveSource
}else{
    $temp=Split-Path $wratSyallsessions[1] -leaf
    $temp='\k'+$temp.Replace('.csv','.dat')
    Remove-Item -Path $uPath$temp
}

if(Test-Path $wratMycalFeedBack[1] -PathType Leaf){
    $null=Protect-File  $wratMycalFeedBack[1] -Algorithm AES -KeyAsPlainText $wratMycalFeedBack[0] -RemoveSource
}else{
    $temp=Split-Path $wratMycalFeedBack[1] -leaf
    $temp='\k'+$temp.Replace('.csv','.dat')
    Remove-Item -Path $uPath$temp
}

if(Test-Path $wratSyVideoSes[1] -PathType Leaf){
   $null=Protect-File  $wratSyVideoSes[1] -Algorithm AES -KeyAsPlainText $wratSyVideoSes[0] -RemoveSource 
}else{
    $temp=Split-Path $wratSyVideoSes[1] -leaf
    $temp='\k'+$temp.Replace('.csv','.dat')
    Remove-Item -Path $uPath$temp
}

if(Test-Path $wratSyAppshrngSes[1] -PathType Leaf){
   $null=Protect-File  $wratSyAppshrngSes[1] -Algorithm AES -KeyAsPlainText $wratSyAppshrngSes[0] -RemoveSource 
}else{
    $temp=Split-Path $wratSyAppshrngSes[1] -leaf
    $temp='\k'+$temp.Replace('.csv','.dat')
    Remove-Item -Path $uPath$temp
}

if(Test-Path $wratSyalldomains[1] -PathType Leaf){
   $null=Protect-File  $wratSyalldomains[1] -Algorithm AES -KeyAsPlainText $wratSyalldomains[0] -RemoveSource 
}else{
    $temp=Split-Path $wratSyalldomains[1] -leaf
    $temp='\k'+$temp.Replace('.csv','.dat')
    Remove-Item -Path $uPath$temp
}

$csvDir=$egInstallpath.Value+'/agent/'+$rptPath+'/'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'Syallsessions'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'SyRatMycalFeedBack'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'Syalldomains'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'SyVideoSessions'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'SyAppshrngSessions'
    Eg-DeleteCsvFiles -FilePath $csvDir -Pattern 'CSOnlineUsr'
    exit
