Public/DuoUserFunctions.ps1

Function Sync-DuoUser{
<#
.SYNOPSIS
    Syncs a user from Duo to from directory within Duo
 
.DESCRIPTION
     Syncs a user from Duo to from directory
 
.PARAMETER Username
    Sync user by their Duo username
 
.PARAMETER UserID
    Sync user by their Duo UserID
 
.PARAMETER Directory
    The intended directory you wish to sync from
 
.Parameter Email
    Switch to change username search to email if normalization is on
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Sync-DuoUser -Username DuoUser1 -Directory "DuoDirectory"
 
.EXAMPLE
    Sync-Duouser -Username DuoUser1@Duosecurity.com -Directory "DuoDirectory"
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    Param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipeLine=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
            [String]$Username,
        [Parameter(
            Mandatory=$true,
            Position=1)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            If($_ -In (Get-DuoDirectoryNames)){$true}
            Else{Throw "$($_) is an invalid directory"}
        })]
            [String]$Directory,
        [Parameter(
            Mandatory=$false,
            Position=2
        )]
            [Switch]$Email
    )
    
    $dKey = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((Get-DuoDirectoryKey -DirectoryName $Directory)))
    
    #Base Claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/directorysync/$($dKey)/syncuser"
    [Hashtable]$DuoParams = @{}

    #$User = Get-DuoUser -Username $Username
    If($Email){$VerifiedUsername = (Get-Duouser -Username $Username).email}
    Else{$VerifiedUsername = $Username}
    $DuoParams.Add("username",$VerifiedUsername.ToLower())

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }

}

Function Get-DuoUser {
<#
.Synopsis
    Utilizing Duo REST API to return user(s)
.DESCRIPTION
    Returns a list of Duo users or an individual user
 
.EXAMPLE
    Get-DuoUser
    Returns all users from Duo. Initiates a call for each 300
 
.EXAMPLE
    Get-UserUser -Username TestUser
 
.EXAMPLE
    Get-UserUser -UserID ABCDEF12G34567HIJKLM
 
.OUTPUTS
    [PSCustomObject]$DuoUsers
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    PARAM(
        [Parameter(ParameterSetName="UName",
            Mandatory = $false,
            ValueFromPipeLine = $true,
            Position=0
            )]
            [String]$Username,
        [Parameter(ParameterSetName="UID",
            Mandatory = $false,
            ValueFromPipeLine = $false,
            Position=0
            )]
            [String]$UserID
    )

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users"
    [Hashtable]$DuoParams = @{}

    If($Username){
        $DuoParams.Add("username",$Username.ToLower())
    }
    ElseIf($UserID){    
        $Uri = "/admin/v1/users/$($UserID)"
    }
    Else{
        $DuoParams.Add("limit","300")
        $DuoParams.Add("offset","0")
    }
    $Offset = 0

    #Duo has a 300 user limit in their api. Loop to return all users
    Do{
        $DuoParams.Offset = $Offset
        $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
        $Response = Invoke-RestMethod @Request
        If($Response.stat -ne 'OK'){
            Write-Warning 'DUO REST Call Failed'
            Write-Warning "Arguments:"+($DuoParams | Out-String)
            Write-Warning "Method:$Method Path:$Uri"
        }   
        Else{
            $Output = $Response | Select-Object -ExpandProperty Response 
            $Output
            #Increment offset to return the next 300 users
            $Offset += 300
        }
    }Until($Output.Count -lt 300)
}

Function New-DuoUser{
<#
.SYNOPSIS
    Creates a new user within Duo with Duo as the source
 
.DESCRIPTION
     Creates a new user within Duo
 
.PARAMETER Username
    New user's username
 
.PARAMETER Alias1
    First alias for the new user
 
.PARAMETER Alias2
    Second alias for the new user
 
.PARAMETER Alias3
    Third alias for the new user
 
.PARAMETER Alias4
    Fourth alias for the new user
 
.PARAMETER Realname
    The user's realname
 
.PARAMETER Firstname
    User's first name
 
.PARAMETER Lastname
    User's last name
 
.Parameter Email
    User's email address
 
.Parameter Status
    Status for the account to be created with either Active, Bypass, or Disabled
 
.Parameter Notes
    Any notes to be included on the Duo account
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    New-DuoUser -Username DuoUser1
 
.EXAMPLE
    New-DuoUser -Username DuoUser1 -email DuoUser1@duosecurity.com
 
.EXAMPLE
    New-DuoUser -UserName DuoUser1 -email DuoUser1@duosecurity.com -alias1 DuoDemo -Status Disabled -Notes "Demo purposes"
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    Param(
        [Parameter(Mandatory=$true)]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){Throw "User: $($_) already exist in Duo"}
            Else{$true}
        })]
        [String]$Username,
        
        [Parameter(Mandatory=$false)]
        [String]$Alias1,
        
        [Parameter(Mandatory=$false)]
        [String]$Alias2,
        
        [Parameter(Mandatory=$false)]
        [String]$Alias3,
        
        [Parameter(Mandatory=$false)]
        [String]$Alias4,
        
        [Parameter(Mandatory=$false)]
        [String]$Realname,
        
        [Parameter(Mandatory=$false)]
        [String]$Firstname,
        
        [Parameter(Mandatory=$false)]
        [String]$Lastname,
        
        [Parameter(Mandatory=$false)]
        [String]$Email,
        
        [Parameter(Mandatory=$false)]
        [ValidateSet("Active","Bypass","Disabled")]
        [String]$Status,
        
        [Parameter(Mandatory=$false)]
        [String]$Notes
    )

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users"
    [Hashtable]$DuoParams = @{}

    #Add Username
    $DuoParams.Add("username",$Username.ToLower())

    #Add Optional Parameters
    If($Alias1){$DuoParams.Add("alias1",$Alias1.ToLower())}
    If($Alias2){$DuoParams.Add("alias2",$Alias2.ToLower())}
    If($Alias3){$DuoParams.Add("alias3",$Alias3.ToLower())}
    If($Alias4){$DuoParams.Add("alias4",$Alias4.ToLower())}
    If($Realname){$DuoParams.Add("realname",$Realname)}
    If($Firstname){$DuoParams.Add("firstname",$Firstname)}
    If($Lastname){$DuoParams.Add("lastname",$Lastname)}
    If($Email){$DuoParams.Add("email",$Email.ToLower())}
    If($Status){$DuoParams.Add("status",$Status.ToLower())}
    If($Notes){$DuoParams.Add("notes",$Notes)}

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Set-DuoUser{
<#
.SYNOPSIS
    Sets fields on a Duo user account.
 
.DESCRIPTION
    Sets fields on a Duo user account.
 
.PARAMETER Username
    Sync user by their Duo username
 
.PARAMETER UserID
    Sync user by their Duo UserID
 
.PARAMETER Directory
    The intended directory you wish to sync from
 
.Parameter Email
    Switch to change username search to email if normalization is on
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Set-DuoUser -Username DuoUser1 -Directory "DuoDirectory"
 
.EXAMPLE
    Set-Duouser -Username DuoUser1@Duosecurity.com -Directory "DuoDirectory"
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    Param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipleLine=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "Invalid User ID"}
        })]
        [String]$UserID,

        [Parameter(
            Mandatory=$false,
            ValueFromPipleLine=$true,
            Position=1
        )]
        [String]$Username,

        [Parameter(Mandatory=$false)]
        [String]$Alias1,

        [Parameter(Mandatory=$false)]
        [String]$Alias2,

        [Parameter(Mandatory=$false)]
        [String]$Alias3,

        [Parameter(Mandatory=$false)]
        [String]$Alias4,

        [Parameter(Mandatory=$false)]
        [String]$RealName,
        
        [Parameter(Mandatory=$false)]
        [String]$FirstName,
        
        [Parameter(Mandatory=$false)]
        [String]$LastName,
        
        [Parameter(Mandatory=$false)]
        [String]$Email,
        
        [ValidateSet("Active","Bypass","Disabled")]
        [String]$Status,
        
        [Parameter(Mandatory=$false)]
        [String]$Notes
    )

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)"
    [Hashtable]$DuoParams = @{}

    #Create claim with selected attributes to be modified
    If($Username){$DuoParams.Add("username",$Username.ToLower())}
    If($Alias1){$DuoParams.Add("Alias1",$alias1.ToLower())}
    If($Alias2){$DuoParams.Add("Alias2",$alias2.ToLower())}
    If($Alias3){$DuoParams.Add("Alias3",$alias3.ToLower())}
    If($Alias4){$DuoParams.Add("Alias4",$alias4.ToLower())}
    If($RealName){$DuoParams.Add("realname",$RealName)}
    If($FirstName){$DuoParams.Add("firstname",$FirstName)}
    If($LastName){$DuoParams.Add("lastname",$LastName)}
    If($Email){$DuoParams.Add("email",$Email.ToLower())}
    If($Status){$DuoParams.Add("status",$Status.ToLower())}
    If($Notes){$DuoParams.Add("Notes",$Notes)}

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    #Error Handling
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    #Returning request
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Remove-DuoUser{
<#
.SYNOPSIS
    Removes a Duo user.
 
.DESCRIPTION
    This function removes a specified Duo user by UserID. It includes an optional confirmation prompt unless the Force switch is used.
 
.PARAMETER UserID
    The user ID of the Duo user to be removed. This parameter is mandatory.
 
.PARAMETER Force
    If specified, the user will be removed without a confirmation prompt.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    PARAM(
        [Parameter(
            Mandatory=$true,
            ValueFromPipleLine=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "Invalid User ID"}
        })]$UserID,

        [Switch]$Force
    )
    $Username = (Get-DuoUser -UserID $UserID).username
    If($Force -eq $false){
        $Confirm = $Host.UI.PromptForChoice("Please Confirm","Are you sure you want to delete $($Username) from Duo?",@("Yes","No"),1)
    }

    #
    If(($Force -eq $true) -or ($Confirm -eq 0)){
        #Base claim
        [String]$Method = "POST"
        [String]$Uri = "/admin/v1/users/$($UserID)"

        #Creates the request
        $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
        $Response = Invoke-RestMethod @Request
        
        #Error Handling
        If($Response.stat -ne 'OK'){
            Write-Warning 'DUO REST Call Failed'
            Write-Warning "Arguments:"+($DuoParams | Out-String)
            Write-Warning "Method:$Method Path:$Uri"
        }
        #Returning request
        Else{
            $Output = $Response | Select-Object -ExpandProperty Response 
            $Output
        }
    }
}

Function New-DuoUserEnrollment{
<#
.SYNOPSIS
    Enrolls a new Duo user.
 
.DESCRIPTION
    This function enrolls a new Duo user by specifying the username and email. It also allows setting an expiration time for the enrollment.
 
.PARAMETER Username
    The Duo username to be enrolled. This parameter is mandatory.
 
.PARAMETER Email
    The email address to be enrolled. This parameter is mandatory.
 
.PARAMETER ExpirationDate
    The date and time when the enrollment expires. This parameter is optional and is part of the DateTime parameter set.
 
.PARAMETER TimeToExpire
    The number of seconds until the enrollment expires. This parameter is optional and is part of the Seconds parameter set.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    Param(
        [Parameter(
            Mandatory=$true,
            HelpMessage="Duo username to be enrolled",
            ValueFromPipleLine=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -Username $_){$true}
            Else{Throw "Invalid User ID"}
        })]
        [String]$Username,

        [Parameter(
            Mandatory=$true,
            HelpMessage="Email to be enrolled",
            ValueFromPipeline=$true,
            Position=1
        )]
        [MailAddress]$Email,

        [Parameter(ParameterSetName="DateTime",
            HelpMessage="DateTime for when enrollment expires",
            Mandatory=$false,
            ValueFromPipeline=$false,
            Position=2
        )]
        [DateTime]$ExpirationDate,

        [Parameter(ParameterSetName="Seconds",
            HelpMessage="How many seconds until enrollment expires",
            Mandatory=$false,
            ValueFromPipeline=$false,
            Position=2
        )]
        [Int]$TimeToExpire
    )

    If($ExpirationDate){
        $Time = ($ExpirationDate - (Get-Date)).TotalSeconds
    }
    ElseIf($TimeToExpire){
        $Time = $TimeToExpire
    }


    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/enroll"
    [Hashtable]$DuoParams = @{}

    $DuoParams.Add("username",$Username.ToLower())
    $DuoParams.Add("email",$Email.ToString().ToLower())
    If($Time){
        $Time = [Math]::Round($Time)
        $DuoParams.Add("valid_secs",$Time.ToString())
    }

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Error Handling
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    #Returning request
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function New-DuoUserBypassCode{
<#
.SYNOPSIS
    Creates new bypass codes for a Duo user.
 
.DESCRIPTION
    This function creates new bypass codes for a specified Duo user. It allows for defining the number of codes, specific codes, number of uses, and expiration time.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory.
 
.PARAMETER Count
    The number of bypass codes to create. The maximum is 10. This parameter is optional.
 
.PARAMETER Codes
    Specific bypass codes to be used. The maximum is 10. This parameter is optional.
 
.PARAMETER NumberOfUses
    The number of times the bypass codes can be used. The default is 1. This parameter is optional.
 
.PARAMETER ExpirationDate
    The expiration date and time for the bypass codes. This parameter is optional and is part of the DateTime parameter set.
 
.PARAMETER TimeToExpire
    The number of seconds until the bypass codes expire. This parameter is optional and is part of the Seconds parameter set.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    Param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipleLine=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -Username $_){$true}
            Else{Throw "Invalid User ID"}
        })]
        [String]$Username,

        [Parameter(
            Mandatory=$false,
            HelpMessage="Define bypass codes to be used. Maximum of 10",
            ValueFromPipeline=$false
        )]
        [Int]$Count,
        
        [Parameter(
            Mandatory=$false,
            HelpMessage="Define bypass codes to be used. Maximum of 10",
            ValueFromPipeline=$false
        )]
        [String]$Codes,

        [Parameter(
            Mandatory=$false,
            HelpMessage="Number of times the bypass codes can be used. Default of 1",
            ValueFromPipeline=$false
        )]
        [Int]$NumberOfUses,

        [Parameter(ParameterSetName="DateTime",
            HelpMessage="DateTime for when bypass codes expire",
            Mandatory=$false,
            ValueFromPipeline=$false
        )]
        [DateTime]$ExpirationDate,

        [Parameter(ParameterSetName="Seconds",
            HelpMessage="How many seconds until bypass code expires",
            Mandatory=$false,
            ValueFromPipeline=$false
        )]
        [Int]$TimeToExpire
    )
    $UserID = (Get-DuoUser -Username $Username).user_id

    If($ExpirationDate){
        $Time = [Math]::Round(($ExpirationDate - (Get-Date)).TotalSeconds)
    }
    ElseIf($TimeToExpire){
        $Time = $TimeToExpire
    }
    Else{
        $Time = 3600
    }

    If($Count -eq $null){
        $Count = 1
    }

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)/bypass_codes"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("count",$Count.ToString())
    If($Codes){
        $Codes = $Codes | ConvertTo-Csv -NoTypeInformation
        $DuoParams.Add("codes",$Codes)
    }
    If($NumberOfUses){
        $DuoParams.Add("resuse_count",$NumberOfUses)
    }
    $DuoParams.Add("valid_secs",$Time.ToString())

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Error Handling
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    #Returning request
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoUserBypassCode{
<#
.SYNOPSIS
    Retrieves bypass codes associated with a Duo user.
 
.DESCRIPTION
    This function retrieves bypass codes associated with a Duo user based on the provided parameters. It can fetch bypass codes by Username or UserID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is optional and can be piped.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is optional.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    PARAM(
        [Parameter(ParameterSetName="UName",
            Mandatory = $false,
            ValueFromPipeLine = $true,
            Position=0
            )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,
        
        [Parameter(ParameterSetName="UID",
            Mandatory = $false,
            ValueFromPipeLine = $false,
            Position=0
            )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/bypass_codes"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("limit","300")
    $DuoParams.Add("offset","0")

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Call private function to validate and format the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Offset = 0

    #Duo has a 500 bypass code limit in their api. Loop to return all bypass codes
    Do{
        $DuoParams.Offset = $Offset
        $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
        $Response = Invoke-RestMethod @Request
        If($Response.stat -ne 'OK'){
            Write-Warning 'DUO REST Call Failed'
            Write-Warning "Arguments:"+($DuoParams | Out-String)
            Write-Warning "Method:$Method Path:$Uri"
        }   
        Else{
            $Output = $Response | Select-Object -ExpandProperty Response 
            $Output
            #Increment offset to return the next 300 users
            $Offset += 500
        }
    }Until($Output.Count -lt 500)
}

Function Get-DuoUserGroup{
<#
.SYNOPSIS
    Retrieves groups associated with a Duo user.
 
.DESCRIPTION
    This function retrieves groups associated with a Duo user based on the provided parameters. It can fetch groups by Username or UserID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is optional and can be piped.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is optional.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    PARAM(
        [Parameter(ParameterSetName="UName",
            Mandatory = $false,
            ValueFromPipeLine = $true,
            Position=0
            )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,
        
        [Parameter(ParameterSetName="UID",
            Mandatory = $false,
            ValueFromPipeLine = $false,
            Position=0
            )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/groups"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("limit","300")
    $DuoParams.Add("offset","0")

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Call private function to validate and format the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Offset = 0

    #Duo has a 500 bypass code limit in their api. Loop to return all bypass codes
    Do{
        $DuoParams.Offset = $Offset
        $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
        $Response = Invoke-RestMethod @Request
        If($Response.stat -ne 'OK'){
            Write-Warning 'DUO REST Call Failed'
            Write-Warning "Arguments:"+($DuoParams | Out-String)
            Write-Warning "Method:$Method Path:$Uri"
        }   
        Else{
            $Output = $Response | Select-Object -ExpandProperty Response 
            $Output
            #Increment offset to return the next 300 users
            $Offset += 500
        }
    }Until($Output.Count -lt 500)
}

Function Add-DuoGroupMember{
<#
.SYNOPSIS
    Adds a user to a Duo group.
 
.DESCRIPTION
    This function adds a user to a specified Duo group based on the provided parameters. It can add users by Username or UserID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory when using the Uname parameter set.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is mandatory when using the UID parameter set.
 
.PARAMETER GroupID
    The ID of the Duo group to which the user will be added. This parameter is mandatory.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,
        
        [Parameter(Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoGroup -GroupID $_){$true}
            Else{Throw "GroupID: $($_) doesn't exist in Duo"}
        })]
        [String]$GroupID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)/groups"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("group_id",$GroupID)

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Error Handling
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    #Returning request
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Remove-DuoGroupMember{
<#
.SYNOPSIS
    Removes a user from a Duo group.
 
.DESCRIPTION
    This function removes a user from a specified Duo group based on the provided parameters. It can remove users by Username or UserID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory when using the Uname parameter set.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is mandatory when using the UID parameter set.
 
.PARAMETER GroupID
    The ID of the Duo group from which the user will be removed. This parameter is mandatory.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoGroup -GroupID $_){$true}
            Else{Throw "GroupID: $($_) doesn't exist in Duo"}
        })]
        [String]$GroupID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "DELETE"
    [String]$Uri = "/admin/v1/users/$($UserID)/groups/$($GroupID)"
    [Hashtable]$DuoParams = @{}

    #Creates the request
    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    
    #Error Handling
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }
    #Returning request
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoUserPhone{
<#
.SYNOPSIS
    Retrieves phones associated with a Duo user.
 
.DESCRIPTION
    This function retrieves phones associated with a Duo user based on the provided parameters. It can fetch phones by Username or UserID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory when using the Uname parameter set.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is mandatory when using the UID parameter set.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/phones"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("limit","500")
    $DuoParams.Add("offset","0")
    
    $Offset = 0

    #Duo has a 500 phone limit in their api. Loop to return all phones
    Do{
        $DuoParams.Offset = $Offset
        $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
        $Response = Invoke-RestMethod @Request
        If($Response.stat -ne 'OK'){
            Write-Warning 'DUO REST Call Failed'
            Write-Warning "Arguments:"+($DuoParams | Out-String)
            Write-Warning "Method:$Method Path:$Uri"
        }   
        Else{
            $Output = $Response | Select-Object -ExpandProperty Response 
            $Output
            #Increment offset to return the next 500 phones
            $Offset += 500
        }
    }Until($Output.Count -lt 500)
}

Function Add-DuoPhoneMember{
<#
.SYNOPSIS
    Adds a phone to a Duo user.
 
.DESCRIPTION
    This function adds a phone to a Duo user based on the provided parameters. It can add phones by Username or UserID and PhoneNumber or PhoneID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory when using the Uname parameter set.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is mandatory when using the UID parameter set.
 
.PARAMETER PhoneNumber
    The phone number to add. This parameter is mandatory when using the Pnumber parameter set. The phone number must be in E.164 format.
 
.PARAMETER PhoneID
    The phone ID to add. This parameter is mandatory when using the PID parameter set.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


[CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(ParameterSetName="Pnumber",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Validate-PhoneNumber -PhoneNumber $_){
                If(Test-DuoPhone -Number $_){$true}
                Else{Throw "User: $($_) doesn't exist in Duo"}
            }
            Else{Throw "Invalid phone number. Please use E. 164 format."}
        })]
        [String]$PhoneNumber,

        [Parameter(ParameterSetName="PID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoPhone -PhoneID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$PhoneID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    If($PhoneNumber){
        $PhoneID = (Get-DuoPhone -Number $PhoneNumber).phone_id
    }

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)/phones"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("phone_id",$PhoneID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }

}

Function Remove-DuoPhoneMember{
<#
.SYNOPSIS
    Removes a phone from a Duo user.
 
.DESCRIPTION
    This function removes a phone associated with a Duo user based on the provided parameters. It can remove phones by Username or UserID and PhoneNumber or PhoneID.
 
.PARAMETER Username
    The username of the Duo user. This parameter is mandatory when using the Uname parameter set.
 
.PARAMETER UserID
    The user ID of the Duo user. This parameter is mandatory when using the UID parameter set.
 
.PARAMETER PhoneNumber
    The phone number to remove. This parameter is mandatory when using the Pnumber parameter set. The phone number must be in E.164 format.
 
.PARAMETER PhoneID
    The phone ID to remove. This parameter is mandatory when using the PID parameter set.
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>


    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(ParameterSetName="Pnumber",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Validate-PhoneNumber -PhoneNumber $_){
                If(Test-DuoPhone -Number $_){$true}
                Else{Throw "User: $($_) doesn't exist in Duo"}
            }
            Else{Throw "Invalid phone number. Please use E. 164 format."}
        })]
        [String]$PhoneNumber,

        [Parameter(ParameterSetName="PID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoPhone -PhoneID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$PhoneID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    If($PhoneNumber){
        $PhoneID = (Get-DuoPhone -Number $PhoneNumber).phone_id
    }

    #Base claim
    [String]$Method = "DELETE"
    [String]$Uri = "/admin/v1/users/$($UserID)/phones"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("phone_id",$PhoneID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoUserToken{
<#
.SYNOPSIS
    Returns all tokens assocaited with a particular user.
 
.DESCRIPTION
    Get all tokens associated with a user.
 
.PARAMETER Username
    Duo user's username
 
.PARAMETER UserID
    Dou user's ID
 
.PARAMETER TokenID
    Duo token's ID
 
.Parameter Serial
    Token's serial number. Requires -Type
 
.Parameter Type
    Token type, HOTP-6,HOTP-8,YubiKey, or Duo-D100. Requires -Serial
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Get-DuoUserToken -UserID 123ABCDE456FGH -TokenID 12356879456DFD
 
.EXAMPLE
    Get-DuoUserToken -Username duouser -Serial DDF2341DFBKL5457 -Type YubiKey
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/tokens"
    [Hashtable]$DuoParams = @{}

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Add-DuoTokenMember{
<#
.SYNOPSIS
    Associates a token token from a user.
 
.DESCRIPTION
    Adds a token from a Duo user.
 
.PARAMETER Username
    Duo user's username
 
.PARAMETER UserID
    Dou user's ID
 
.PARAMETER TokenID
    Duo token's ID
 
.Parameter Serial
    Token's serial number. Requires -Type
 
.Parameter Type
    Token type, HOTP-6,HOTP-8,YubiKey, or Duo-D100. Requires -Serial
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Add-DuoTokenMember -UserID 123ABCDE456FGH -TokenID 12356879456DFD
 
.EXAMPLE
    Add-DuoTokenMember -Username duouser -Serial DDF2341DFBKL5457 -Type YubiKey
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>
    
    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(ParameterSetName="TID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoToken -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$TokenID,

        [Parameter(ParameterSetName="Tserial",
            Mandatory = $false,
            ValueFromPipeLine = $false,
            Position=1
        )]
        [String]$Serial,

        [Parameter(ParameterSetName="Tserial",
            Mandatory = $true,
            ValueFromPipeLine = $false,
            Position=2
        )]
        [ValidateSet("HOTP-6","HOTP-8","YubiKey","Duo-D100")]
        [String]$Type
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    If($Serial){
        $TokenID = (Get-DuoTokens -Serial $Serial -Type $Type).token_id
    }

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)/tokens"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("token_id",$TokenID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Remove-DuoTokenMember{
<#
.SYNOPSIS
    Disassociates a token token from a user.
 
.DESCRIPTION
    Removes a token from a Duo user.
 
.PARAMETER Username
    Duo user's username
 
.PARAMETER UserID
    Dou user's ID
 
.PARAMETER TokenID
    Duo token's ID
 
.Parameter Serial
    Token's serial number. Requires -Type
 
.Parameter Type
    Token type, HOTP-6,HOTP-8,YubiKey, or Duo-D100. Requires -Serial
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Remove-DuoTokenMember -UserID 123ABCDE456FGH -TokenID 12356879456DFD
 
.EXAMPLE
    Remove-DuoTokenMember -Username duouser -Serial DDF2341DFBKL5457 -Type YubiKey
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(ParameterSetName="TID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoToken -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$TokenID,
        
        [Parameter(ParameterSetName="Tserial",
            Mandatory = $false,
            ValueFromPipeLine = $false,
            Position=1
            )]
        [String]$Serial,
        
        [Parameter(ParameterSetName="Tserial",
            Mandatory = $true,
            ValueFromPipeLine = $false,
            Position=2
        )]
        [ValidateSet("HOTP-6","HOTP-8","YubiKey","Duo-D100")]
        [String]$Type
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    If($Serial){
        $TokenID = (Get-DuoTokens -Serial $Serial -Type $Type).token_id
    }

    #Base claim
    [String]$Method = "DELETE"
    [String]$Uri = "/admin/v1/users/$($UserID)/tokens"
    [Hashtable]$DuoParams = @{}
    $DuoParams.Add("token_id",$TokenID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoUserWebAuthN{
<#
.SYNOPSIS
    Get all WebAuthN keys associated with a particular user
 
.DESCRIPTION
    Returns all WebAuthN keys assocaited with an individual user
 
.PARAMETER Username
    Duo User's username
 
.PARAMETER UserID
    Duo User's User DI
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Get-DuoUseWebAuthN -UserID 123ABCDE456FGH
 
.EXAMPLE
  Get-DuoUserWebAuthN -Username duouser
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,
        
        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/webauthncredentials"
    [Hashtable]$DuoParams = @{}

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoUserDesktop {
<#
.SYNOPSIS
    Return Desktops associated with a particular user
 
.DESCRIPTION
    Returns all desktops associated with an individual user
 
.PARAMETER Username
    Duo user's username
 
.PARAMETER UserID
    Duo user's ID
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Get-DuoUserDesktop -Username duouser@duosecurity.com
 
.EXAMPLE
    Get-DuoUserDesktop -UserID 123ABDE456FGH
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,
        
        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/desktopauthenticators"
    [Hashtable]$DuoParams = @{}

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Send-DuoPush {
<#
.SYNOPSIS
    Send a Duo Push Verification to identified user.
 
.DESCRIPTION
    Can send a Dup Push Verification to a specific user and returns the PushID for use with Get-DuoVerificationResponse
 
.PARAMETER Username
    Duo user's Username
 
.PARAMETER UserID
    Duo user's Duo user ID
 
.PARAMETER PhoneNumber
    Phone number of the phone associated with the user.
 
.Parameter PhoneID
    Duo's phone ID
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Send-DuoPush -UserID 123ABCDE456FGHI -PhoneID 9875645ADFD
 
.EXAMPLE
    Send-DuoPush -Username Duouser@duosecurity.com -PhoneNumber +15555555555
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$false,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,
        
        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$false,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$false,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoPhone -PhoneID $_){$true}
            Else{Throw "PhoneID: $($_) doesn't exist in Duo"}
        })]
        [String]$PhoneID,
        
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$false,
            Position=1
        )]
        [ValidateScript({
            If(Test-DuoPhone -Number $_){$true}
            Else{Throw "Phone Number: $($_) doesn't exist in Duo"}
        })]
        [String]$PhoneNumber
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    IF($PhoneNumber){
        $PhoneID = (Get-DuoPhone -Number $PhoneNumber).phone_id
    }

    #Base claim
    [String]$Method = "POST"
    [String]$Uri = "/admin/v1/users/$($UserID)/send_verification_push"
    [Hashtable]$DuoParams = @{}

    $DuoParams.Add("phone_id",$PhoneID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}

Function Get-DuoVerificationResponse {
<#
.SYNOPSIS
    Check the response to a identified verification request.
 
.DESCRIPTION
    Returns the user's response to the verifictaion request.
 
.PARAMETER Username
    The user's username within Duo
 
.PARAMETER UserID
    The user's Duo ID
 
.PARAMETER PushID
    The ID of the Push request that was sent to the user.
 
.OUTPUTS
    [PSCustomObject]DuoRequest
 
.EXAMPLE
    Get-DuoVerificationResponse -UID 123ABCD456EFGH -PushID 987654ZYX
 
.EXAMPLE
    Get-DuoVerificationResponse -Username DuoUser@Duosecurity.com -PushID 987654ZYX
 
.LINK
    https://github.com/jyates2006/PSDuo
    https://jaredyatesit.com/Documentation/PSDuo
#>

    [CmdletBinding(DefaultParameterSetName="Uname")]
    Param(
        [Parameter(ParameterSetName="Uname",
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserName $_){$true}
            Else{Throw "User: $($_) doesn't exist in Duo"}
        })]
        [String]$Username,

        [Parameter(ParameterSetName="UID",
            Mandatory=$true,
            ValueFromPipeline=$false,
            Position=0
        )]
        [ValidateScript({
            If(Test-DuoUser -UserID $_){$true}
            Else{Throw "UserID: $($_) doesn't exist in Duo"}
        })]
        [String]$UserID,

        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true,
            Position=1
        )]
        [String]$PushID
    )

    If($Username){
        $UserID = (Get-DuoUser -Username $Username).user_id
    }
    IF($PhoneNumber){
        $PhoneID = (Get-DuoPhone -Number $PhoneNumber).phone_id
    }

    #Base claim
    [String]$Method = "GET"
    [String]$Uri = "/admin/v1/users/$($UserID)/verification_push_response"
    [Hashtable]$DuoParams = @{}

    $DuoParams.Add("push_id",$PushID)

    $Request = New-DuoRequest -UriPath $Uri -Method $Method -Arguments $DuoParams
    $Response = Invoke-RestMethod @Request
    If($Response.stat -ne 'OK'){
        Write-Warning 'DUO REST Call Failed'
        Write-Warning "Arguments:"+($DuoParams | Out-String)
        Write-Warning "Method:$Method Path:$Uri"
    }   
    Else{
        $Output = $Response | Select-Object -ExpandProperty Response 
        $Output
    }
}